Commit 7cb74580 authored by Steffen Klassert's avatar Steffen Klassert
Browse files

Merge branch 'xfrm/compat: Fix xfrm_spdattr_type_t copying'



Dmitry Safonov says:

====================
Here is the fix for both 32=>64 and 64=>32 bit translators and a
selftest that reproduced the issue.

Big thanks to YueHaibing for fuzzing and reporting the issue,
I really appreciate it!
====================

Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
parents 2580d3f4 70bfdf62
Loading
Loading
Loading
Loading
+44 −5
Original line number Diff line number Diff line
@@ -298,8 +298,16 @@ static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src)
	len = nlmsg_attrlen(nlh_src, xfrm_msg_min[type]);

	nla_for_each_attr(nla, attrs, len, remaining) {
		int err = xfrm_xlate64_attr(dst, nla);
		int err;

		switch (type) {
		case XFRM_MSG_NEWSPDINFO:
			err = xfrm_nla_cpy(dst, nla, nla_len(nla));
			break;
		default:
			err = xfrm_xlate64_attr(dst, nla);
			break;
		}
		if (err)
			return err;
	}
@@ -341,7 +349,8 @@ static int xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh_src

/* Calculates len of translated 64-bit message. */
static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src,
					    struct nlattr *attrs[XFRMA_MAX+1])
					    struct nlattr *attrs[XFRMA_MAX + 1],
					    int maxtype)
{
	size_t len = nlmsg_len(src);

@@ -358,10 +367,20 @@ static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src,
	case XFRM_MSG_POLEXPIRE:
		len += 8;
		break;
	case XFRM_MSG_NEWSPDINFO:
		/* attirbutes are xfrm_spdattr_type_t, not xfrm_attr_type_t */
		return len;
	default:
		break;
	}

	/* Unexpected for anything, but XFRM_MSG_NEWSPDINFO, please
	 * correct both 64=>32-bit and 32=>64-bit translators to copy
	 * new attributes.
	 */
	if (WARN_ON_ONCE(maxtype))
		return len;

	if (attrs[XFRMA_SA])
		len += 4;
	if (attrs[XFRMA_POLICY])
@@ -440,7 +459,8 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla,

static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src,
			struct nlattr *attrs[XFRMA_MAX+1],
			size_t size, u8 type, struct netlink_ext_ack *extack)
			size_t size, u8 type, int maxtype,
			struct netlink_ext_ack *extack)
{
	size_t pos;
	int i;
@@ -520,6 +540,25 @@ static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src,
	}
	pos = dst->nlmsg_len;

	if (maxtype) {
		/* attirbutes are xfrm_spdattr_type_t, not xfrm_attr_type_t */
		WARN_ON_ONCE(src->nlmsg_type != XFRM_MSG_NEWSPDINFO);

		for (i = 1; i <= maxtype; i++) {
			int err;

			if (!attrs[i])
				continue;

			/* just copy - no need for translation */
			err = xfrm_attr_cpy32(dst, &pos, attrs[i], size,
					nla_len(attrs[i]), nla_len(attrs[i]));
			if (err)
				return err;
		}
		return 0;
	}

	for (i = 1; i < XFRMA_MAX + 1; i++) {
		int err;

@@ -564,7 +603,7 @@ static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32,
	if (err < 0)
		return ERR_PTR(err);

	len = xfrm_user_rcv_calculate_len64(h32, attrs);
	len = xfrm_user_rcv_calculate_len64(h32, attrs, maxtype);
	/* The message doesn't need translation */
	if (len == nlmsg_len(h32))
		return NULL;
@@ -574,7 +613,7 @@ static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32,
	if (!h64)
		return ERR_PTR(-ENOMEM);

	err = xfrm_xlate32(h64, h32, attrs, len, type, extack);
	err = xfrm_xlate32(h64, h32, attrs, len, type, maxtype, extack);
	if (err < 0) {
		kvfree(h64);
		return ERR_PTR(err);
+163 −2
Original line number Diff line number Diff line
@@ -484,13 +484,16 @@ enum desc_type {
	MONITOR_ACQUIRE,
	EXPIRE_STATE,
	EXPIRE_POLICY,
	SPDINFO_ATTRS,
};
const char *desc_name[] = {
	"create tunnel",
	"alloc spi",
	"monitor acquire",
	"expire state",
	"expire policy"
	"expire policy",
	"spdinfo attributes",
	""
};
struct xfrm_desc {
	enum desc_type	type;
@@ -1593,6 +1596,155 @@ static int xfrm_expire_policy(int xfrm_sock, uint32_t *seq,
	return ret;
}

static int xfrm_spdinfo_set_thresh(int xfrm_sock, uint32_t *seq,
		unsigned thresh4_l, unsigned thresh4_r,
		unsigned thresh6_l, unsigned thresh6_r,
		bool add_bad_attr)

{
	struct {
		struct nlmsghdr		nh;
		union {
			uint32_t	unused;
			int		error;
		};
		char			attrbuf[MAX_PAYLOAD];
	} req;
	struct xfrmu_spdhthresh thresh;

	memset(&req, 0, sizeof(req));
	req.nh.nlmsg_len	= NLMSG_LENGTH(sizeof(req.unused));
	req.nh.nlmsg_type	= XFRM_MSG_NEWSPDINFO;
	req.nh.nlmsg_flags	= NLM_F_REQUEST | NLM_F_ACK;
	req.nh.nlmsg_seq	= (*seq)++;

	thresh.lbits = thresh4_l;
	thresh.rbits = thresh4_r;
	if (rtattr_pack(&req.nh, sizeof(req), XFRMA_SPD_IPV4_HTHRESH, &thresh, sizeof(thresh)))
		return -1;

	thresh.lbits = thresh6_l;
	thresh.rbits = thresh6_r;
	if (rtattr_pack(&req.nh, sizeof(req), XFRMA_SPD_IPV6_HTHRESH, &thresh, sizeof(thresh)))
		return -1;

	if (add_bad_attr) {
		BUILD_BUG_ON(XFRMA_IF_ID <= XFRMA_SPD_MAX + 1);
		if (rtattr_pack(&req.nh, sizeof(req), XFRMA_IF_ID, NULL, 0)) {
			pr_err("adding attribute failed: no space");
			return -1;
		}
	}

	if (send(xfrm_sock, &req, req.nh.nlmsg_len, 0) < 0) {
		pr_err("send()");
		return -1;
	}

	if (recv(xfrm_sock, &req, sizeof(req), 0) < 0) {
		pr_err("recv()");
		return -1;
	} else if (req.nh.nlmsg_type != NLMSG_ERROR) {
		printk("expected NLMSG_ERROR, got %d", (int)req.nh.nlmsg_type);
		return -1;
	}

	if (req.error) {
		printk("NLMSG_ERROR: %d: %s", req.error, strerror(-req.error));
		return -1;
	}

	return 0;
}

static int xfrm_spdinfo_attrs(int xfrm_sock, uint32_t *seq)
{
	struct {
		struct nlmsghdr			nh;
		union {
			uint32_t	unused;
			int		error;
		};
		char			attrbuf[MAX_PAYLOAD];
	} req;

	if (xfrm_spdinfo_set_thresh(xfrm_sock, seq, 32, 31, 120, 16, false)) {
		pr_err("Can't set SPD HTHRESH");
		return KSFT_FAIL;
	}

	memset(&req, 0, sizeof(req));

	req.nh.nlmsg_len	= NLMSG_LENGTH(sizeof(req.unused));
	req.nh.nlmsg_type	= XFRM_MSG_GETSPDINFO;
	req.nh.nlmsg_flags	= NLM_F_REQUEST;
	req.nh.nlmsg_seq	= (*seq)++;
	if (send(xfrm_sock, &req, req.nh.nlmsg_len, 0) < 0) {
		pr_err("send()");
		return KSFT_FAIL;
	}

	if (recv(xfrm_sock, &req, sizeof(req), 0) < 0) {
		pr_err("recv()");
		return KSFT_FAIL;
	} else if (req.nh.nlmsg_type == XFRM_MSG_NEWSPDINFO) {
		size_t len = NLMSG_PAYLOAD(&req.nh, sizeof(req.unused));
		struct rtattr *attr = (void *)req.attrbuf;
		int got_thresh = 0;

		for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
			if (attr->rta_type == XFRMA_SPD_IPV4_HTHRESH) {
				struct xfrmu_spdhthresh *t = RTA_DATA(attr);

				got_thresh++;
				if (t->lbits != 32 || t->rbits != 31) {
					pr_err("thresh differ: %u, %u",
							t->lbits, t->rbits);
					return KSFT_FAIL;
				}
			}
			if (attr->rta_type == XFRMA_SPD_IPV6_HTHRESH) {
				struct xfrmu_spdhthresh *t = RTA_DATA(attr);

				got_thresh++;
				if (t->lbits != 120 || t->rbits != 16) {
					pr_err("thresh differ: %u, %u",
							t->lbits, t->rbits);
					return KSFT_FAIL;
				}
			}
		}
		if (got_thresh != 2) {
			pr_err("only %d thresh returned by XFRM_MSG_GETSPDINFO", got_thresh);
			return KSFT_FAIL;
		}
	} else if (req.nh.nlmsg_type != NLMSG_ERROR) {
		printk("expected NLMSG_ERROR, got %d", (int)req.nh.nlmsg_type);
		return KSFT_FAIL;
	} else {
		printk("NLMSG_ERROR: %d: %s", req.error, strerror(-req.error));
		return -1;
	}

	/* Restore the default */
	if (xfrm_spdinfo_set_thresh(xfrm_sock, seq, 32, 32, 128, 128, false)) {
		pr_err("Can't restore SPD HTHRESH");
		return KSFT_FAIL;
	}

	/*
	 * At this moment xfrm uses nlmsg_parse_deprecated(), which
	 * implies NL_VALIDATE_LIBERAL - ignoring attributes with
	 * (type > maxtype). nla_parse_depricated_strict() would enforce
	 * it. Or even stricter nla_parse().
	 * Right now it's not expected to fail, but to be ignored.
	 */
	if (xfrm_spdinfo_set_thresh(xfrm_sock, seq, 32, 32, 128, 128, true))
		return KSFT_PASS;

	return KSFT_PASS;
}

static int child_serv(int xfrm_sock, uint32_t *seq,
		unsigned int nr, int cmd_fd, void *buf, struct xfrm_desc *desc)
{
@@ -1717,6 +1869,9 @@ static int child_f(unsigned int nr, int test_desc_fd, int cmd_fd, void *buf)
		case EXPIRE_POLICY:
			ret = xfrm_expire_policy(xfrm_sock, &seq, nr, &desc);
			break;
		case SPDINFO_ATTRS:
			ret = xfrm_spdinfo_attrs(xfrm_sock, &seq);
			break;
		default:
			printk("Unknown desc type %d", desc.type);
			exit(KSFT_FAIL);
@@ -1994,8 +2149,10 @@ static int write_proto_plan(int fd, int proto)
 *   sizeof(xfrm_user_polexpire)  = 168  |  sizeof(xfrm_user_polexpire)  = 176
 *
 * Check the affected by the UABI difference structures.
 * Also, check translation for xfrm_set_spdinfo: it has it's own attributes
 * which needs to be correctly copied, but not translated.
 */
const unsigned int compat_plan = 4;
const unsigned int compat_plan = 5;
static int write_compat_struct_tests(int test_desc_fd)
{
	struct xfrm_desc desc = {};
@@ -2019,6 +2176,10 @@ static int write_compat_struct_tests(int test_desc_fd)
	if (__write_desc(test_desc_fd, &desc))
		return -1;

	desc.type = SPDINFO_ATTRS;
	if (__write_desc(test_desc_fd, &desc))
		return -1;

	return 0;
}