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>
This commit is contained in:
parent
931ca7ab7f
commit
0dfe6581a7
4 changed files with 79 additions and 88 deletions
|
@ -70,9 +70,6 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *,
|
||||||
int compat_mc_setsockopt(struct sock *, int, int, char __user *, unsigned int,
|
int compat_mc_setsockopt(struct sock *, int, int, char __user *, unsigned int,
|
||||||
int (*)(struct sock *, int, int, char __user *,
|
int (*)(struct sock *, int, int, char __user *,
|
||||||
unsigned int));
|
unsigned int));
|
||||||
int compat_mc_getsockopt(struct sock *, int, int, char __user *, int __user *,
|
|
||||||
int (*)(struct sock *, int, int, char __user *,
|
|
||||||
int __user *));
|
|
||||||
|
|
||||||
struct compat_group_req {
|
struct compat_group_req {
|
||||||
__u32 gr_interface;
|
__u32 gr_interface;
|
||||||
|
|
79
net/compat.c
79
net/compat.c
|
@ -538,85 +538,6 @@ int compat_mc_setsockopt(struct sock *sock, int level, int optname,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(compat_mc_setsockopt);
|
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 __user *)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(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(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(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(koptlen, sizeof(*koptlen)) ||
|
|
||||||
__get_user(klen, koptlen))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
ulen = klen - (sizeof(*kgf)-sizeof(*gf32));
|
|
||||||
|
|
||||||
if (!access_ok(optlen, sizeof(*optlen)) ||
|
|
||||||
__put_user(ulen, optlen))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (!access_ok(kgf, klen) ||
|
|
||||||
!access_ok(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 */
|
/* Argument list sizes for compat_sys_socketcall */
|
||||||
#define AL(x) ((x) * sizeof(u32))
|
#define AL(x) ((x) * sizeof(u32))
|
||||||
static unsigned char nas[21] = {
|
static unsigned char nas[21] = {
|
||||||
|
|
|
@ -1607,9 +1607,47 @@ int compat_ip_getsockopt(struct sock *sk, int level, int optname,
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (optname == MCAST_MSFILTER)
|
if (optname == MCAST_MSFILTER) {
|
||||||
return compat_mc_getsockopt(sk, level, optname, optval, optlen,
|
const int size0 = offsetof(struct compat_group_filter, gf_slist);
|
||||||
ip_getsockopt);
|
struct compat_group_filter __user *p = (void __user *)optval;
|
||||||
|
struct compat_group_filter gf32;
|
||||||
|
struct group_filter gf;
|
||||||
|
int ulen, err;
|
||||||
|
int num;
|
||||||
|
|
||||||
|
if (level != SOL_IP)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
rtnl_lock();
|
||||||
|
lock_sock(sk);
|
||||||
|
err = ip_mc_gsfget(sk, &gf, p->gf_slist);
|
||||||
|
release_sock(sk);
|
||||||
|
rtnl_unlock();
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
if (gf.gf_numsrc < num)
|
||||||
|
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_ip_getsockopt(sk, level, optname, optval, optlen,
|
err = do_ip_getsockopt(sk, level, optname, optval, optlen,
|
||||||
MSG_CMSG_COMPAT);
|
MSG_CMSG_COMPAT);
|
||||||
|
|
|
@ -1446,9 +1446,44 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
|
||||||
if (level != SOL_IPV6)
|
if (level != SOL_IPV6)
|
||||||
return -ENOPROTOOPT;
|
return -ENOPROTOOPT;
|
||||||
|
|
||||||
if (optname == MCAST_MSFILTER)
|
if (optname == MCAST_MSFILTER) {
|
||||||
return compat_mc_getsockopt(sk, level, optname, optval, optlen,
|
const int size0 = offsetof(struct compat_group_filter, gf_slist);
|
||||||
ipv6_getsockopt);
|
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,
|
err = do_ipv6_getsockopt(sk, level, optname, optval, optlen,
|
||||||
MSG_CMSG_COMPAT);
|
MSG_CMSG_COMPAT);
|
||||||
|
|
Loading…
Reference in a new issue