summaryrefslogtreecommitdiffstats
path: root/net/ipv6/ipv6_sockglue.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2020-03-29 22:08:59 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2020-05-20 20:31:27 -0400
commit0dfe6581a7e35bafe5fc4d9b84edd0e66b4fd78a (patch)
tree7690c0c6a8fb539ca0462408935d8beba1cf8417 /net/ipv6/ipv6_sockglue.c
parent931ca7ab7fe804d77bc6952f1512950c0d870f26 (diff)
get rid of compat_mc_getsockopt()
now we can do MCAST_MSFILTER in compat ->getsockopt() without playing silly buggers with copying things back and forth. We can form a native struct group_filter (sans the variable-length tail) on stack, pass that + pointer to the tail of original request to the helper doing the bulk of the work, then do the rest of copyout - same as the native getsockopt() does. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'net/ipv6/ipv6_sockglue.c')
-rw-r--r--net/ipv6/ipv6_sockglue.c41
1 files changed, 38 insertions, 3 deletions
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index e4a62ca1a3d0..0bbafe73bdde 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -1446,9 +1446,44 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
if (level != SOL_IPV6)
return -ENOPROTOOPT;
- if (optname == MCAST_MSFILTER)
- return compat_mc_getsockopt(sk, level, optname, optval, optlen,
- ipv6_getsockopt);
+ if (optname == MCAST_MSFILTER) {
+ const int size0 = offsetof(struct compat_group_filter, gf_slist);
+ struct compat_group_filter __user *p = (void __user *)optval;
+ struct compat_group_filter gf32;
+ struct group_filter gf;
+ int ulen, err;
+ int num;
+
+ if (get_user(ulen, optlen))
+ return -EFAULT;
+
+ if (ulen < size0)
+ return -EINVAL;
+
+ if (copy_from_user(&gf32, p, size0))
+ return -EFAULT;
+
+ gf.gf_interface = gf32.gf_interface;
+ gf.gf_fmode = gf32.gf_fmode;
+ num = gf.gf_numsrc = gf32.gf_numsrc;
+ gf.gf_group = gf32.gf_group;
+
+ if (gf.gf_group.ss_family != AF_INET6)
+ return -EADDRNOTAVAIL;
+ lock_sock(sk);
+ err = ip6_mc_msfget(sk, &gf, p->gf_slist);
+ release_sock(sk);
+ if (err)
+ return err;
+ if (num > gf.gf_numsrc)
+ num = gf.gf_numsrc;
+ ulen = GROUP_FILTER_SIZE(num) - (sizeof(gf)-sizeof(gf32));
+ if (put_user(ulen, optlen) ||
+ put_user(gf.gf_fmode, &p->gf_fmode) ||
+ put_user(gf.gf_numsrc, &p->gf_numsrc))
+ return -EFAULT;
+ return 0;
+ }
err = do_ipv6_getsockopt(sk, level, optname, optval, optlen,
MSG_CMSG_COMPAT);