summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/net/compat.h3
-rw-r--r--net/compat.c79
-rw-r--r--net/ipv4/ip_sockglue.c9
-rw-r--r--net/ipv6/ipv6_sockglue.c4
4 files changed, 94 insertions, 1 deletions
diff --git a/include/net/compat.h b/include/net/compat.h
index 05fa5d0254ab..164cb682e220 100644
--- a/include/net/compat.h
+++ b/include/net/compat.h
@@ -42,5 +42,8 @@ extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, unsi
extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, int,
int (*)(struct sock *, int, int, char __user *, int));
+extern int compat_mc_getsockopt(struct sock *, int, int, char __user *,
+ int __user *, int (*)(struct sock *, int, int, char __user *,
+ int __user *));
#endif /* NET_COMPAT_H */
diff --git a/net/compat.c b/net/compat.c
index 8146f654391c..c823f6f290cb 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -640,6 +640,85 @@ int compat_mc_setsockopt(struct sock *sock, int level, int optname,
EXPORT_SYMBOL(compat_mc_setsockopt);
+int compat_mc_getsockopt(struct sock *sock, int level, int optname,
+ char __user *optval, int __user *optlen,
+ int (*getsockopt)(struct sock *,int,int,char __user *,int __user *))
+{
+ struct compat_group_filter __user *gf32 = (void *)optval;
+ struct group_filter __user *kgf;
+ int __user *koptlen;
+ u32 interface, fmode, numsrc;
+ int klen, ulen, err;
+
+ if (optname != MCAST_MSFILTER)
+ return getsockopt(sock, level, optname, optval, optlen);
+
+ koptlen = compat_alloc_user_space(sizeof(*koptlen));
+ if (!access_ok(VERIFY_READ, optlen, sizeof(*optlen)) ||
+ __get_user(ulen, optlen))
+ return -EFAULT;
+
+ /* adjust len for pad */
+ klen = ulen + sizeof(*kgf) - sizeof(*gf32);
+
+ if (klen < GROUP_FILTER_SIZE(0))
+ return -EINVAL;
+
+ if (!access_ok(VERIFY_WRITE, koptlen, sizeof(*koptlen)) ||
+ __put_user(klen, koptlen))
+ return -EFAULT;
+
+ /* have to allow space for previous compat_alloc_user_space, too */
+ kgf = compat_alloc_user_space(klen+sizeof(*optlen));
+
+ if (!access_ok(VERIFY_READ, gf32, __COMPAT_GF0_SIZE) ||
+ __get_user(interface, &gf32->gf_interface) ||
+ __get_user(fmode, &gf32->gf_fmode) ||
+ __get_user(numsrc, &gf32->gf_numsrc) ||
+ __put_user(interface, &kgf->gf_interface) ||
+ __put_user(fmode, &kgf->gf_fmode) ||
+ __put_user(numsrc, &kgf->gf_numsrc) ||
+ copy_in_user(&kgf->gf_group,&gf32->gf_group,sizeof(kgf->gf_group)))
+ return -EFAULT;
+
+ err = getsockopt(sock, level, optname, (char __user *)kgf, koptlen);
+ if (err)
+ return err;
+
+ if (!access_ok(VERIFY_READ, koptlen, sizeof(*koptlen)) ||
+ __get_user(klen, koptlen))
+ return -EFAULT;
+
+ ulen = klen - (sizeof(*kgf)-sizeof(*gf32));
+
+ if (!access_ok(VERIFY_WRITE, optlen, sizeof(*optlen)) ||
+ __put_user(ulen, optlen))
+ return -EFAULT;
+
+ if (!access_ok(VERIFY_READ, kgf, klen) ||
+ !access_ok(VERIFY_WRITE, gf32, ulen) ||
+ __get_user(interface, &kgf->gf_interface) ||
+ __get_user(fmode, &kgf->gf_fmode) ||
+ __get_user(numsrc, &kgf->gf_numsrc) ||
+ __put_user(interface, &gf32->gf_interface) ||
+ __put_user(fmode, &gf32->gf_fmode) ||
+ __put_user(numsrc, &gf32->gf_numsrc))
+ return -EFAULT;
+ if (numsrc) {
+ int copylen;
+
+ klen -= GROUP_FILTER_SIZE(0);
+ copylen = numsrc * sizeof(gf32->gf_slist[0]);
+ if (copylen > klen)
+ copylen = klen;
+ if (copy_in_user(gf32->gf_slist, kgf->gf_slist, copylen))
+ return -EFAULT;
+ }
+ return err;
+}
+
+EXPORT_SYMBOL(compat_mc_getsockopt);
+
/* Argument list sizes for compat_sys_socketcall */
#define AL(x) ((x) * sizeof(u32))
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 4d8d95404f45..e0514e82308e 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -1186,7 +1186,14 @@ int ip_getsockopt(struct sock *sk, int level,
int compat_ip_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
- int err = do_ip_getsockopt(sk, level, optname, optval, optlen);
+ int err;
+
+ if (optname == MCAST_MSFILTER)
+ return compat_mc_getsockopt(sk, level, optname, optval, optlen,
+ ip_getsockopt);
+
+ err = do_ip_getsockopt(sk, level, optname, optval, optlen);
+
#ifdef CONFIG_NETFILTER
/* we need to exclude all possible ENOPROTOOPTs except default case */
if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index db6fdc1498aa..b4a26f2505f8 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -1089,6 +1089,10 @@ int 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);
+
err = do_ipv6_getsockopt(sk, level, optname, optval, optlen);
#ifdef CONFIG_NETFILTER
/* we need to exclude all possible ENOPROTOOPTs except default case */