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

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
...@@ -298,8 +298,16 @@ static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src) ...@@ -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]); len = nlmsg_attrlen(nlh_src, xfrm_msg_min[type]);
nla_for_each_attr(nla, attrs, len, remaining) { 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) if (err)
return err; return err;
} }
...@@ -341,7 +349,8 @@ static int xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh_src ...@@ -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. */ /* Calculates len of translated 64-bit message. */
static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src, 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); size_t len = nlmsg_len(src);
...@@ -358,10 +367,20 @@ static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src, ...@@ -358,10 +367,20 @@ static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src,
case XFRM_MSG_POLEXPIRE: case XFRM_MSG_POLEXPIRE:
len += 8; len += 8;
break; break;
case XFRM_MSG_NEWSPDINFO:
/* attirbutes are xfrm_spdattr_type_t, not xfrm_attr_type_t */
return len;
default: default:
break; 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]) if (attrs[XFRMA_SA])
len += 4; len += 4;
if (attrs[XFRMA_POLICY]) if (attrs[XFRMA_POLICY])
...@@ -440,7 +459,8 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla, ...@@ -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, static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src,
struct nlattr *attrs[XFRMA_MAX+1], 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; size_t pos;
int i; int i;
...@@ -520,6 +540,25 @@ static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src, ...@@ -520,6 +540,25 @@ static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src,
} }
pos = dst->nlmsg_len; 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++) { for (i = 1; i < XFRMA_MAX + 1; i++) {
int err; int err;
...@@ -564,7 +603,7 @@ static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32, ...@@ -564,7 +603,7 @@ static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32,
if (err < 0) if (err < 0)
return ERR_PTR(err); 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 */ /* The message doesn't need translation */
if (len == nlmsg_len(h32)) if (len == nlmsg_len(h32))
return NULL; return NULL;
...@@ -574,7 +613,7 @@ static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32, ...@@ -574,7 +613,7 @@ static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32,
if (!h64) if (!h64)
return ERR_PTR(-ENOMEM); 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) { if (err < 0) {
kvfree(h64); kvfree(h64);
return ERR_PTR(err); return ERR_PTR(err);
......
...@@ -484,13 +484,16 @@ enum desc_type { ...@@ -484,13 +484,16 @@ enum desc_type {
MONITOR_ACQUIRE, MONITOR_ACQUIRE,
EXPIRE_STATE, EXPIRE_STATE,
EXPIRE_POLICY, EXPIRE_POLICY,
SPDINFO_ATTRS,
}; };
const char *desc_name[] = { const char *desc_name[] = {
"create tunnel", "create tunnel",
"alloc spi", "alloc spi",
"monitor acquire", "monitor acquire",
"expire state", "expire state",
"expire policy" "expire policy",
"spdinfo attributes",
""
}; };
struct xfrm_desc { struct xfrm_desc {
enum desc_type type; enum desc_type type;
...@@ -1593,6 +1596,155 @@ static int xfrm_expire_policy(int xfrm_sock, uint32_t *seq, ...@@ -1593,6 +1596,155 @@ static int xfrm_expire_policy(int xfrm_sock, uint32_t *seq,
return ret; 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, static int child_serv(int xfrm_sock, uint32_t *seq,
unsigned int nr, int cmd_fd, void *buf, struct xfrm_desc *desc) 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) ...@@ -1717,6 +1869,9 @@ static int child_f(unsigned int nr, int test_desc_fd, int cmd_fd, void *buf)
case EXPIRE_POLICY: case EXPIRE_POLICY:
ret = xfrm_expire_policy(xfrm_sock, &seq, nr, &desc); ret = xfrm_expire_policy(xfrm_sock, &seq, nr, &desc);
break; break;
case SPDINFO_ATTRS:
ret = xfrm_spdinfo_attrs(xfrm_sock, &seq);
break;
default: default:
printk("Unknown desc type %d", desc.type); printk("Unknown desc type %d", desc.type);
exit(KSFT_FAIL); exit(KSFT_FAIL);
...@@ -1994,8 +2149,10 @@ static int write_proto_plan(int fd, int proto) ...@@ -1994,8 +2149,10 @@ static int write_proto_plan(int fd, int proto)
* sizeof(xfrm_user_polexpire) = 168 | sizeof(xfrm_user_polexpire) = 176 * sizeof(xfrm_user_polexpire) = 168 | sizeof(xfrm_user_polexpire) = 176
* *
* Check the affected by the UABI difference structures. * 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) static int write_compat_struct_tests(int test_desc_fd)
{ {
struct xfrm_desc desc = {}; struct xfrm_desc desc = {};
...@@ -2019,6 +2176,10 @@ static int write_compat_struct_tests(int test_desc_fd) ...@@ -2019,6 +2176,10 @@ static int write_compat_struct_tests(int test_desc_fd)
if (__write_desc(test_desc_fd, &desc)) if (__write_desc(test_desc_fd, &desc))
return -1; return -1;
desc.type = SPDINFO_ATTRS;
if (__write_desc(test_desc_fd, &desc))
return -1;
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment