From: Håkon Bugge Date: Fri, 28 Sep 2018 13:23:13 +0000 (+0200) Subject: net: rds: Use address family to designate IPv4 or IPv6 addresses X-Git-Tag: v4.1.12-124.31.3~517 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=60ca15120a6679e75f1b1ccc2e47cc3285c076f8;p=users%2Fjedix%2Flinux-maple.git net: rds: Use address family to designate IPv4 or IPv6 addresses The condition to interpret the supplied address in rds_cancel_sent_to() was the length of the supplied user-data. We need to tighten the API here to the extent possible, subject to SKGXP passing in sizeof(struct sockaddr_storage) as the optlen, independent of the actual address size. We will 1) make sure the data passed in to the kernel is "big enough", 2) that the address is either AF_INET or AF_INET6, and 3) that the bound address is ipv6_addr_v4mapped() in the AF_INET case and not in the AF_INET6 case. Orabug: 28720071 Signed-off-by: Håkon Bugge Reviewed-by: Darren Kenny --- diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index ce81b506ce0f..cb78fcbe5b0b 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c @@ -307,8 +307,9 @@ static int rds_cancel_sent_to(struct rds_sock *rs, char __user *optval, int len) { struct sockaddr_in6 sin6; - struct sockaddr_in sin; + int cpy_len; int ret = 0; + bool is_v4; /* racing with another thread binding seems ok here */ if (ipv6_addr_any(&rs->rs_bound_addr)) { @@ -319,20 +320,37 @@ static int rds_cancel_sent_to(struct rds_sock *rs, char __user *optval, if (len < sizeof(struct sockaddr_in)) { ret = -EINVAL; goto out; - } else if (len < sizeof(struct sockaddr_in6)) { - /* Assume IPv4 */ - if (copy_from_user(&sin, optval, sizeof(struct sockaddr_in))) { - ret = -EFAULT; - goto out; - } - ipv6_addr_set_v4mapped(sin.sin_addr.s_addr, &sin6.sin6_addr); - sin6.sin6_port = sin.sin_port; - } else { - if (copy_from_user(&sin6, optval, - sizeof(struct sockaddr_in6))) { - ret = -EFAULT; - goto out; - } + } + + /* Lets restrict copying to at most sizeof(sin6) */ + cpy_len = min_t(int, (int)sizeof(sin6), len); + if (copy_from_user(&sin6, optval, cpy_len)) { + ret = -EFAULT; + goto out; + } + + /* We only support IPv4 and IPv6 */ + if (sin6.sin6_family != AF_INET && sin6.sin6_family != AF_INET6) { + ret = -EINVAL; + goto out; + } + + is_v4 = (sin6.sin6_family == AF_INET); + + /* Check that the bound address matches the supplied af */ + if (ipv6_addr_v4mapped(&rs->rs_bound_sin6.sin6_addr) != is_v4) { + ret = -EINVAL; + goto out; + } + + if (is_v4) { + const struct sockaddr_in *sin4p = (struct sockaddr_in *)&sin6; + + ipv6_addr_set_v4mapped(sin4p->sin_addr.s_addr, &sin6.sin6_addr); + sin6.sin6_port = sin4p->sin_port; + } else if (cpy_len != sizeof(sin6)) { + ret = -EINVAL; + goto out; } rds_send_drop_to(rs, &sin6);