diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2020-03-30 15:39:43 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2020-05-20 20:31:28 -0400 |
commit | 2e04172875c9daf929659eb5c3ef4b98fdf34396 (patch) | |
tree | 5b961e119043bd1ad29abda4569528686a06297e | |
parent | e986d4dabcb8fab87b36b607bc710e42fe206baf (diff) | |
download | lwn-2e04172875c9daf929659eb5c3ef4b98fdf34396.tar.gz lwn-2e04172875c9daf929659eb5c3ef4b98fdf34396.zip |
ipv4: do compat setsockopt for MCAST_MSFILTER directly
Parallel to what the native setsockopt() does, except that unlike
the native setsockopt() we do not use memdup_user() - we want
the sockaddr_storage fields properly aligned, so we allocate
4 bytes more and copy compat_group_filter at the offset 4,
which yields the proper alignments.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | net/ipv4/ip_sockglue.c | 48 |
1 files changed, 47 insertions, 1 deletions
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index cc0441157b02..b6b889b5dacf 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -1286,9 +1286,55 @@ int compat_ip_setsockopt(struct sock *sk, int level, int optname, case MCAST_LEAVE_SOURCE_GROUP: case MCAST_BLOCK_SOURCE: case MCAST_UNBLOCK_SOURCE: - case MCAST_MSFILTER: return compat_mc_setsockopt(sk, level, optname, optval, optlen, ip_setsockopt); + case MCAST_MSFILTER: + { + const int size0 = offsetof(struct compat_group_filter, gf_slist); + struct compat_group_filter *gf32; + void *p; + int n; + + if (optlen < size0) + return -EINVAL; + if (optlen > sysctl_optmem_max - 4) + return -ENOBUFS; + + p = kmalloc(optlen + 4, GFP_KERNEL); + if (!p) + return -ENOMEM; + gf32 = p + 4; /* we want ->gf_group and ->gf_slist aligned */ + if (copy_from_user(gf32, optval, optlen)) { + err = -EFAULT; + goto mc_msf_out; + } + + n = gf32->gf_numsrc; + /* numsrc >= (4G-140)/128 overflow in 32 bits */ + if (n >= 0x1ffffff) { + err = -ENOBUFS; + goto mc_msf_out; + } + if (offsetof(struct compat_group_filter, gf_slist[n]) > optlen) { + err = -EINVAL; + goto mc_msf_out; + } + + rtnl_lock(); + lock_sock(sk); + /* numsrc >= (4G-140)/128 overflow in 32 bits */ + if (n > sock_net(sk)->ipv4.sysctl_igmp_max_msf) + err = -ENOBUFS; + else + err = set_mcast_msfilter(sk, gf32->gf_interface, + n, gf32->gf_fmode, + &gf32->gf_group, gf32->gf_slist); + release_sock(sk); + rtnl_unlock(); +mc_msf_out: + kfree(p); + return err; + } } err = do_ip_setsockopt(sk, level, optname, optval, optlen); |