Commit c536fc74 authored by David S. Miller's avatar David S. Miller

Merge branch 'uaccess.net' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Al Viro says:

====================
uaccess-related stuff in net/*

Assorted uaccess-related work in net/*.  First, there's
getting rid of compat_alloc_user_space() mess in MCAST_...
[gs]etsockopt() - no need to play with copying to/from temporary
object on userland stack, etc., when ->compat_[sg]etsockopt()
instances in question can easly do everything without that.
That's the first 13 patches.  Then there's a trivial bit in
net/batman-adv (completely unrelated to everything else) and
finally getting the atm compat ioctls into simpler shape.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents f78cdbd7 0edecc02
...@@ -123,7 +123,7 @@ extern int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf,int ifindex); ...@@ -123,7 +123,7 @@ extern int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf,int ifindex);
extern int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, extern int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
struct ip_msfilter __user *optval, int __user *optlen); struct ip_msfilter __user *optval, int __user *optlen);
extern int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, extern int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
struct group_filter __user *optval, int __user *optlen); struct sockaddr_storage __user *p);
extern int ip_mc_sf_allow(struct sock *sk, __be32 local, __be32 rmt, extern int ip_mc_sf_allow(struct sock *sk, __be32 local, __be32 rmt,
int dif, int sdif); int dif, int sdif);
extern void ip_mc_init_dev(struct in_device *); extern void ip_mc_init_dev(struct in_device *);
......
...@@ -67,11 +67,28 @@ int put_cmsg_compat(struct msghdr*, int, int, int, void *); ...@@ -67,11 +67,28 @@ int put_cmsg_compat(struct msghdr*, int, int, int, void *);
int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *,
unsigned char *, int); unsigned char *, int);
int compat_mc_setsockopt(struct sock *, int, int, char __user *, unsigned int, struct compat_group_req {
int (*)(struct sock *, int, int, char __user *, __u32 gr_interface;
unsigned int)); struct __kernel_sockaddr_storage gr_group
int compat_mc_getsockopt(struct sock *, int, int, char __user *, int __user *, __aligned(4);
int (*)(struct sock *, int, int, char __user *, } __packed;
int __user *));
struct compat_group_source_req {
__u32 gsr_interface;
struct __kernel_sockaddr_storage gsr_group
__aligned(4);
struct __kernel_sockaddr_storage gsr_source
__aligned(4);
} __packed;
struct compat_group_filter {
__u32 gf_interface;
struct __kernel_sockaddr_storage gf_group
__aligned(4);
__u32 gf_fmode;
__u32 gf_numsrc;
struct __kernel_sockaddr_storage gf_slist[1]
__aligned(4);
} __packed;
#endif /* NET_COMPAT_H */ #endif /* NET_COMPAT_H */
...@@ -1136,9 +1136,10 @@ struct group_filter; ...@@ -1136,9 +1136,10 @@ struct group_filter;
int ip6_mc_source(int add, int omode, struct sock *sk, int ip6_mc_source(int add, int omode, struct sock *sk,
struct group_source_req *pgsr); struct group_source_req *pgsr);
int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf); int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
struct sockaddr_storage *list);
int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
struct group_filter __user *optval, int __user *optlen); struct sockaddr_storage __user *p);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
int ac6_proc_init(struct net *net); int ac6_proc_init(struct net *net);
......
...@@ -56,6 +56,8 @@ static int do_vcc_ioctl(struct socket *sock, unsigned int cmd, ...@@ -56,6 +56,8 @@ static int do_vcc_ioctl(struct socket *sock, unsigned int cmd,
int error; int error;
struct list_head *pos; struct list_head *pos;
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
void __user *buf;
int __user *len;
vcc = ATM_SD(sock); vcc = ATM_SD(sock);
switch (cmd) { switch (cmd) {
...@@ -162,7 +164,49 @@ static int do_vcc_ioctl(struct socket *sock, unsigned int cmd, ...@@ -162,7 +164,49 @@ static int do_vcc_ioctl(struct socket *sock, unsigned int cmd,
if (error != -ENOIOCTLCMD) if (error != -ENOIOCTLCMD)
goto done; goto done;
error = atm_dev_ioctl(cmd, argp, compat); if (cmd == ATM_GETNAMES) {
if (IS_ENABLED(CONFIG_COMPAT) && compat) {
#ifdef CONFIG_COMPAT
struct compat_atm_iobuf __user *ciobuf = argp;
compat_uptr_t cbuf;
len = &ciobuf->length;
if (get_user(cbuf, &ciobuf->buffer))
return -EFAULT;
buf = compat_ptr(cbuf);
#endif
} else {
struct atm_iobuf __user *iobuf = argp;
len = &iobuf->length;
if (get_user(buf, &iobuf->buffer))
return -EFAULT;
}
error = atm_getnames(buf, len);
} else {
int number;
if (IS_ENABLED(CONFIG_COMPAT) && compat) {
#ifdef CONFIG_COMPAT
struct compat_atmif_sioc __user *csioc = argp;
compat_uptr_t carg;
len = &csioc->length;
if (get_user(carg, &csioc->arg))
return -EFAULT;
buf = compat_ptr(carg);
if (get_user(number, &csioc->number))
return -EFAULT;
#endif
} else {
struct atmif_sioc __user *sioc = argp;
len = &sioc->length;
if (get_user(buf, &sioc->arg))
return -EFAULT;
if (get_user(number, &sioc->number))
return -EFAULT;
}
error = atm_dev_ioctl(cmd, buf, len, number, compat);
}
done: done:
return error; return error;
...@@ -230,61 +274,25 @@ static struct { ...@@ -230,61 +274,25 @@ static struct {
static int do_atm_iobuf(struct socket *sock, unsigned int cmd, static int do_atm_iobuf(struct socket *sock, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
struct atm_iobuf __user *iobuf; struct compat_atm_iobuf __user *iobuf32 = compat_ptr(arg);
struct compat_atm_iobuf __user *iobuf32;
u32 data; u32 data;
void __user *datap;
int len, err;
iobuf = compat_alloc_user_space(sizeof(*iobuf));
iobuf32 = compat_ptr(arg);
if (get_user(len, &iobuf32->length) || if (get_user(data, &iobuf32->buffer))
get_user(data, &iobuf32->buffer))
return -EFAULT; return -EFAULT;
datap = compat_ptr(data);
if (put_user(len, &iobuf->length) ||
put_user(datap, &iobuf->buffer))
return -EFAULT;
err = do_vcc_ioctl(sock, cmd, (unsigned long) iobuf, 0);
if (!err) { return atm_getnames(&iobuf32->length, compat_ptr(data));
if (copy_in_user(&iobuf32->length, &iobuf->length,
sizeof(int)))
err = -EFAULT;
}
return err;
} }
static int do_atmif_sioc(struct socket *sock, unsigned int cmd, static int do_atmif_sioc(struct socket *sock, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
struct atmif_sioc __user *sioc; struct compat_atmif_sioc __user *sioc32 = compat_ptr(arg);
struct compat_atmif_sioc __user *sioc32; int number;
u32 data; u32 data;
void __user *datap;
int err;
sioc = compat_alloc_user_space(sizeof(*sioc));
sioc32 = compat_ptr(arg);
if (copy_in_user(&sioc->number, &sioc32->number, 2 * sizeof(int)) || if (get_user(data, &sioc32->arg) || get_user(number, &sioc32->number))
get_user(data, &sioc32->arg))
return -EFAULT; return -EFAULT;
datap = compat_ptr(data); return atm_dev_ioctl(cmd, compat_ptr(data), &sioc32->length, number, 0);
if (put_user(datap, &sioc->arg))
return -EFAULT;
err = do_vcc_ioctl(sock, cmd, (unsigned long) sioc, 0);
if (!err) {
if (copy_in_user(&sioc32->length, &sioc->length,
sizeof(int)))
err = -EFAULT;
}
return err;
} }
static int do_atm_ioctl(struct socket *sock, unsigned int cmd32, static int do_atm_ioctl(struct socket *sock, unsigned int cmd32,
......
...@@ -193,88 +193,48 @@ static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg, ...@@ -193,88 +193,48 @@ static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg,
return error ? -EFAULT : 0; return error ? -EFAULT : 0;
} }
int atm_dev_ioctl(unsigned int cmd, void __user *arg, int compat) int atm_getnames(void __user *buf, int __user *iobuf_len)
{ {
void __user *buf; int error, len, size = 0;
int error, len, number, size = 0;
struct atm_dev *dev; struct atm_dev *dev;
struct list_head *p; struct list_head *p;
int *tmp_buf, *tmp_p; int *tmp_buf, *tmp_p;
int __user *sioc_len;
int __user *iobuf_len;
switch (cmd) { if (get_user(len, iobuf_len))
case ATM_GETNAMES: return -EFAULT;
if (IS_ENABLED(CONFIG_COMPAT) && compat) { mutex_lock(&atm_dev_mutex);
#ifdef CONFIG_COMPAT list_for_each(p, &atm_devs)
struct compat_atm_iobuf __user *ciobuf = arg; size += sizeof(int);
compat_uptr_t cbuf; if (size > len) {
iobuf_len = &ciobuf->length;
if (get_user(cbuf, &ciobuf->buffer))
return -EFAULT;
buf = compat_ptr(cbuf);
#endif
} else {
struct atm_iobuf __user *iobuf = arg;
iobuf_len = &iobuf->length;
if (get_user(buf, &iobuf->buffer))
return -EFAULT;
}
if (get_user(len, iobuf_len))
return -EFAULT;
mutex_lock(&atm_dev_mutex);
list_for_each(p, &atm_devs)
size += sizeof(int);
if (size > len) {
mutex_unlock(&atm_dev_mutex);
return -E2BIG;
}
tmp_buf = kmalloc(size, GFP_ATOMIC);
if (!tmp_buf) {
mutex_unlock(&atm_dev_mutex);
return -ENOMEM;
}
tmp_p = tmp_buf;
list_for_each(p, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list);
*tmp_p++ = dev->number;
}
mutex_unlock(&atm_dev_mutex); mutex_unlock(&atm_dev_mutex);
error = ((copy_to_user(buf, tmp_buf, size)) || return -E2BIG;
put_user(size, iobuf_len))
? -EFAULT : 0;
kfree(tmp_buf);
return error;
default:
break;
} }
tmp_buf = kmalloc(size, GFP_ATOMIC);
if (IS_ENABLED(CONFIG_COMPAT) && compat) { if (!tmp_buf) {
#ifdef CONFIG_COMPAT mutex_unlock(&atm_dev_mutex);
struct compat_atmif_sioc __user *csioc = arg; return -ENOMEM;
compat_uptr_t carg; }
tmp_p = tmp_buf;
sioc_len = &csioc->length; list_for_each(p, &atm_devs) {
if (get_user(carg, &csioc->arg)) dev = list_entry(p, struct atm_dev, dev_list);
return -EFAULT; *tmp_p++ = dev->number;
buf = compat_ptr(carg);
if (get_user(len, &csioc->length))
return -EFAULT;
if (get_user(number, &csioc->number))
return -EFAULT;
#endif
} else {
struct atmif_sioc __user *sioc = arg;
sioc_len = &sioc->length;
if (get_user(buf, &sioc->arg))
return -EFAULT;
if (get_user(len, &sioc->length))
return -EFAULT;
if (get_user(number, &sioc->number))
return -EFAULT;
} }
mutex_unlock(&atm_dev_mutex);
error = ((copy_to_user(buf, tmp_buf, size)) ||
put_user(size, iobuf_len))
? -EFAULT : 0;
kfree(tmp_buf);
return error;
}
int atm_dev_ioctl(unsigned int cmd, void __user *buf, int __user *sioc_len,
int number, int compat)
{
int error, len, size = 0;
struct atm_dev *dev;
if (get_user(len, sioc_len))
return -EFAULT;
dev = try_then_request_module(atm_dev_lookup(number), "atm-device-%d", dev = try_then_request_module(atm_dev_lookup(number), "atm-device-%d",
number); number);
......
...@@ -14,8 +14,9 @@ ...@@ -14,8 +14,9 @@
extern struct list_head atm_devs; extern struct list_head atm_devs;
extern struct mutex atm_dev_mutex; extern struct mutex atm_dev_mutex;
int atm_dev_ioctl(unsigned int cmd, void __user *arg, int compat); int atm_getnames(void __user *buf, int __user *iobuf_len);
int atm_dev_ioctl(unsigned int cmd, void __user *buf, int __user *sioc_len,
int number, int compat);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
......
...@@ -135,9 +135,6 @@ static ssize_t batadv_socket_read(struct file *file, char __user *buf, ...@@ -135,9 +135,6 @@ static ssize_t batadv_socket_read(struct file *file, char __user *buf,
if (!buf || count < sizeof(struct batadv_icmp_packet)) if (!buf || count < sizeof(struct batadv_icmp_packet))
return -EINVAL; return -EINVAL;
if (!access_ok(buf, count))
return -EFAULT;
error = wait_event_interruptible(socket_client->queue_wait, error = wait_event_interruptible(socket_client->queue_wait,
socket_client->queue_len); socket_client->queue_len);
......
...@@ -448,200 +448,6 @@ COMPAT_SYSCALL_DEFINE5(getsockopt, int, fd, int, level, int, optname, ...@@ -448,200 +448,6 @@ COMPAT_SYSCALL_DEFINE5(getsockopt, int, fd, int, level, int, optname,
return __compat_sys_getsockopt(fd, level, optname, optval, optlen); return __compat_sys_getsockopt(fd, level, optname, optval, optlen);
} }
struct compat_group_req {
__u32 gr_interface;
struct __kernel_sockaddr_storage gr_group
__aligned(4);
} __packed;
struct compat_group_source_req {
__u32 gsr_interface;
struct __kernel_sockaddr_storage gsr_group
__aligned(4);
struct __kernel_sockaddr_storage gsr_source
__aligned(4);
} __packed;
struct compat_group_filter {
__u32 gf_interface;
struct __kernel_sockaddr_storage gf_group
__aligned(4);
__u32 gf_fmode;
__u32 gf_numsrc;
struct __kernel_sockaddr_storage gf_slist[1]
__aligned(4);
} __packed;
#define __COMPAT_GF0_SIZE (sizeof(struct compat_group_filter) - \
sizeof(struct __kernel_sockaddr_storage))
int compat_mc_setsockopt(struct sock *sock, int level, int optname,
char __user *optval, unsigned int optlen,
int (*setsockopt)(struct sock *, int, int, char __user *, unsigned int))
{
char __user *koptval = optval;
int koptlen = optlen;
switch (optname) {
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
{
struct compat_group_req __user *gr32 = (void __user *)optval;
struct group_req __user *kgr =
compat_alloc_user_space(sizeof(struct group_req));
u32 interface;
if (!access_ok(gr32, sizeof(*gr32)) ||
!access_ok(kgr, sizeof(struct group_req)) ||
__get_user(interface, &gr32->gr_interface) ||
__put_user(interface, &kgr->gr_interface) ||
copy_in_user(&kgr->gr_group, &gr32->gr_group,
sizeof(kgr->gr_group)))
return -EFAULT;
koptval = (char __user *)kgr;
koptlen = sizeof(struct group_req);
break;
}
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
case MCAST_BLOCK_SOURCE:
case MCAST_UNBLOCK_SOURCE:
{
struct compat_group_source_req __user *gsr32 = (void __user *)optval;
struct group_source_req __user *kgsr = compat_alloc_user_space(
sizeof(struct group_source_req));
u32 interface;
if (!access_ok(gsr32, sizeof(*gsr32)) ||
!access_ok(kgsr,
sizeof(struct group_source_req)) ||
__get_user(interface, &gsr32->gsr_interface) ||
__put_user(interface, &kgsr->gsr_interface) ||
copy_in_user(&kgsr->gsr_group, &gsr32->gsr_group,
sizeof(kgsr->gsr_group)) ||
copy_in_user(&kgsr->gsr_source, &gsr32->gsr_source,
sizeof(kgsr->gsr_source)))
return -EFAULT;
koptval = (char __user *)kgsr;
koptlen = sizeof(struct group_source_req);
break;
}
case MCAST_MSFILTER:
{
struct compat_group_filter __user *gf32 = (void __user *)optval;
struct group_filter __user *kgf;
u32 interface, fmode, numsrc;
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))
return -EFAULT;
koptlen = optlen + sizeof(struct group_filter) -
sizeof(struct compat_group_filter);
if (koptlen < GROUP_FILTER_SIZE(numsrc))
return -EINVAL;
kgf = compat_alloc_user_space(koptlen);
if (!access_ok(kgf, koptlen) ||
__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)) ||
(numsrc && copy_in_user(kgf->gf_slist, gf32->gf_slist,
numsrc * sizeof(kgf->gf_slist[0]))))
return -EFAULT;
koptval = (char __user *)kgf;
break;
}
default:
break;
}
return setsockopt(sock, level, optname, koptval, koptlen);
}
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] = {
......
...@@ -2565,9 +2565,9 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, ...@@ -2565,9 +2565,9 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
} }
int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
struct group_filter __user *optval, int __user *optlen) struct sockaddr_storage __user *p)
{ {
int err, i, count, copycount; int i, count, copycount;
struct sockaddr_in *psin; struct sockaddr_in *psin;
__be32 addr; __be32 addr;
struct ip_mc_socklist *pmc; struct ip_mc_socklist *pmc;
...@@ -2583,37 +2583,29 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, ...@@ -2583,37 +2583,29 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
if (!ipv4_is_multicast(addr)) if (!ipv4_is_multicast(addr))
return -EINVAL; return -EINVAL;
err = -EADDRNOTAVAIL;
for_each_pmc_rtnl(inet, pmc) { for_each_pmc_rtnl(inet, pmc) {
if (pmc->multi.imr_multiaddr.s_addr == addr && if (pmc->multi.imr_multiaddr.s_addr == addr &&
pmc->multi.imr_ifindex == gsf->gf_interface) pmc->multi.imr_ifindex == gsf->gf_interface)
break; break;
} }
if (!pmc) /* must have a prior join */ if (!pmc) /* must have a prior join */
goto done; return -EADDRNOTAVAIL;
gsf->gf_fmode = pmc->sfmode; gsf->gf_fmode = pmc->sfmode;
psl = rtnl_dereference(pmc->sflist); psl = rtnl_dereference(pmc->sflist);
count = psl ? psl->sl_count : 0; count = psl ? psl->sl_count : 0;
copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
gsf->gf_numsrc = count; gsf->gf_numsrc = count;
if (put_user(GROUP_FILTER_SIZE(copycount), optlen) || for (i = 0; i < copycount; i++, p++) {
copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) {
return -EFAULT;
}
for (i = 0; i < copycount; i++) {
struct sockaddr_storage ss; struct sockaddr_storage ss;
psin = (struct sockaddr_in *)&ss; psin = (struct sockaddr_in *)&ss;
memset(&ss, 0, sizeof(ss)); memset(&ss, 0, sizeof(ss));
psin->sin_family = AF_INET; psin->sin_family = AF_INET;
psin->sin_addr.s_addr = psl->sl_addr[i]; psin->sin_addr.s_addr = psl->sl_addr[i];
if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss))) if (copy_to_user(p, &ss, sizeof(ss)))
return -EFAULT; return -EFAULT;
} }
return 0; return 0;
done:
return err;
} }
/* /*
......
...@@ -587,6 +587,86 @@ static bool setsockopt_needs_rtnl(int optname) ...@@ -587,6 +587,86 @@ static bool setsockopt_needs_rtnl(int optname)
return false; return false;
} }
static int set_mcast_msfilter(struct sock *sk, int ifindex,
int numsrc, int fmode,
struct sockaddr_storage *group,
struct sockaddr_storage *list)
{
int msize = IP_MSFILTER_SIZE(numsrc);
struct ip_msfilter *msf;
struct sockaddr_in *psin;
int err, i;
msf = kmalloc(msize, GFP_KERNEL);
if (!msf)
return -ENOBUFS;
psin = (struct sockaddr_in *)group;
if (psin->sin_family != AF_INET)
goto Eaddrnotavail;
msf->imsf_multiaddr = psin->sin_addr.s_addr;
msf->imsf_interface = 0;
msf->imsf_fmode = fmode;
msf->imsf_numsrc = numsrc;
for (i = 0; i < numsrc; ++i) {
psin = (struct sockaddr_in *)&list[i];
if (psin->sin_family != AF_INET)
goto Eaddrnotavail;
msf->imsf_slist[i] = psin->sin_addr.s_addr;
}
err = ip_mc_msfilter(sk, msf, ifindex);
kfree(msf);
return err;
Eaddrnotavail:
kfree(msf);
return -EADDRNOTAVAIL;
}
static int do_mcast_group_source(struct sock *sk, int optname,
struct group_source_req *greqs)
{
struct ip_mreq_source mreqs;
struct sockaddr_in *psin;
int omode, add, err;
if (greqs->gsr_group.ss_family != AF_INET ||
greqs->gsr_source.ss_family != AF_INET)
return -EADDRNOTAVAIL;
psin = (struct sockaddr_in *)&greqs->gsr_group;
mreqs.imr_multiaddr = psin->sin_addr.s_addr;
psin = (struct sockaddr_in *)&greqs->gsr_source;
mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
mreqs.imr_interface = 0; /* use index for mc_source */
if (optname == MCAST_BLOCK_SOURCE) {
omode = MCAST_EXCLUDE;
add = 1;
} else if (optname == MCAST_UNBLOCK_SOURCE) {
omode = MCAST_EXCLUDE;
add = 0;
} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
struct ip_mreqn mreq;
psin = (struct sockaddr_in *)&greqs->gsr_group;
mreq.imr_multiaddr = psin->sin_addr;
mreq.imr_address.s_addr = 0;
mreq.imr_ifindex = greqs->gsr_interface;
err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
if (err && err != -EADDRINUSE)
return err;
greqs->gsr_interface = mreq.imr_ifindex;
omode = MCAST_INCLUDE;
add = 1;
} else /* MCAST_LEAVE_SOURCE_GROUP */ {
omode = MCAST_INCLUDE;
add = 0;
}
return ip_mc_source(add, omode, sk, &mreqs, greqs->gsr_interface);
}
static int do_ip_setsockopt(struct sock *sk, int level, static int do_ip_setsockopt(struct sock *sk, int level,
int optname, char __user *optval, unsigned int optlen) int optname, char __user *optval, unsigned int optlen)
{ {
...@@ -1029,9 +1109,6 @@ static int do_ip_setsockopt(struct sock *sk, int level, ...@@ -1029,9 +1109,6 @@ static int do_ip_setsockopt(struct sock *sk, int level,
case MCAST_UNBLOCK_SOURCE: case MCAST_UNBLOCK_SOURCE:
{ {
struct group_source_req greqs; struct group_source_req greqs;
struct ip_mreq_source mreqs;
struct sockaddr_in *psin;
int omode, add;
if (optlen != sizeof(struct group_source_req)) if (optlen != sizeof(struct group_source_req))
goto e_inval; goto e_inval;
...@@ -1039,50 +1116,12 @@ static int do_ip_setsockopt(struct sock *sk, int level, ...@@ -1039,50 +1116,12 @@ static int do_ip_setsockopt(struct sock *sk, int level,
err = -EFAULT; err = -EFAULT;
break; break;
} }
if (greqs.gsr_group.ss_family != AF_INET || err = do_mcast_group_source(sk, optname, &greqs);
greqs.gsr_source.ss_family != AF_INET) {
err = -EADDRNOTAVAIL;
break;
}
psin = (struct sockaddr_in *)&greqs.gsr_group;
mreqs.imr_multiaddr = psin->sin_addr.s_addr;
psin = (struct sockaddr_in *)&greqs.gsr_source;
mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
mreqs.imr_interface = 0; /* use index for mc_source */
if (optname == MCAST_BLOCK_SOURCE) {
omode = MCAST_EXCLUDE;
add = 1;
} else if (optname == MCAST_UNBLOCK_SOURCE) {
omode = MCAST_EXCLUDE;
add = 0;
} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
struct ip_mreqn mreq;
psin = (struct sockaddr_in *)&greqs.gsr_group;
mreq.imr_multiaddr = psin->sin_addr;
mreq.imr_address.s_addr = 0;
mreq.imr_ifindex = greqs.gsr_interface;
err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
if (err && err != -EADDRINUSE)
break;
greqs.gsr_interface = mreq.imr_ifindex;
omode = MCAST_INCLUDE;
add = 1;
} else /* MCAST_LEAVE_SOURCE_GROUP */ {
omode = MCAST_INCLUDE;
add = 0;
}
err = ip_mc_source(add, omode, sk, &mreqs,
greqs.gsr_interface);
break; break;
} }
case MCAST_MSFILTER: case MCAST_MSFILTER:
{ {
struct sockaddr_in *psin;
struct ip_msfilter *msf = NULL;
struct group_filter *gsf = NULL; struct group_filter *gsf = NULL;
int msize, i, ifindex;
if (optlen < GROUP_FILTER_SIZE(0)) if (optlen < GROUP_FILTER_SIZE(0))
goto e_inval; goto e_inval;
...@@ -1095,7 +1134,6 @@ static int do_ip_setsockopt(struct sock *sk, int level, ...@@ -1095,7 +1134,6 @@ static int do_ip_setsockopt(struct sock *sk, int level,
err = PTR_ERR(gsf); err = PTR_ERR(gsf);
break; break;
} }
/* numsrc >= (4G-140)/128 overflow in 32 bits */ /* numsrc >= (4G-140)/128 overflow in 32 bits */
if (gsf->gf_numsrc >= 0x1ffffff || if (gsf->gf_numsrc >= 0x1ffffff ||
gsf->gf_numsrc > net->ipv4.sysctl_igmp_max_msf) { gsf->gf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
...@@ -1106,36 +1144,10 @@ static int do_ip_setsockopt(struct sock *sk, int level, ...@@ -1106,36 +1144,10 @@ static int do_ip_setsockopt(struct sock *sk, int level,
err = -EINVAL; err = -EINVAL;
goto mc_msf_out; goto mc_msf_out;
} }
msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); err = set_mcast_msfilter(sk, gsf->gf_interface,
msf = kmalloc(msize, GFP_KERNEL); gsf->gf_numsrc, gsf->gf_fmode,
if (!msf) { &gsf->gf_group, gsf->gf_slist);
err = -ENOBUFS;
goto mc_msf_out;
}
ifindex = gsf->gf_interface;
psin = (struct sockaddr_in *)&gsf->gf_group;
if (psin->sin_family != AF_INET) {
err = -EADDRNOTAVAIL;
goto mc_msf_out;
}
msf->imsf_multiaddr = psin->sin_addr.s_addr;
msf->imsf_interface = 0;
msf->imsf_fmode = gsf->gf_fmode;
msf->imsf_numsrc = gsf->gf_numsrc;
err = -EADDRNOTAVAIL;
for (i = 0; i < gsf->gf_numsrc; ++i) {
psin = (struct sockaddr_in *)&gsf->gf_slist[i];
if (psin->sin_family != AF_INET)
goto mc_msf_out;
msf->imsf_slist[i] = psin->sin_addr.s_addr;
}
kfree(gsf);
gsf = NULL;
err = ip_mc_msfilter(sk, msf, ifindex);
mc_msf_out: mc_msf_out:
kfree(msf);
kfree(gsf); kfree(gsf);
break; break;
} }
...@@ -1272,9 +1284,113 @@ int compat_ip_setsockopt(struct sock *sk, int level, int optname, ...@@ -1272,9 +1284,113 @@ int compat_ip_setsockopt(struct sock *sk, int level, int optname,
if (level != SOL_IP) if (level != SOL_IP)
return -ENOPROTOOPT; return -ENOPROTOOPT;
if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER) switch (optname) {
return compat_mc_setsockopt(sk, level, optname, optval, optlen, case MCAST_JOIN_GROUP:
ip_setsockopt); case MCAST_LEAVE_GROUP:
{
struct compat_group_req __user *gr32 = (void __user *)optval;
struct group_req greq;
struct sockaddr_in *psin = (struct sockaddr_in *)&greq.gr_group;
struct ip_mreqn mreq;
if (optlen < sizeof(struct compat_group_req))
return -EINVAL;
if (get_user(greq.gr_interface, &gr32->gr_interface) ||
copy_from_user(&greq.gr_group, &gr32->gr_group,
sizeof(greq.gr_group)))
return -EFAULT;
if (psin->sin_family != AF_INET)
return -EINVAL;
memset(&mreq, 0, sizeof(mreq));
mreq.imr_multiaddr = psin->sin_addr;
mreq.imr_ifindex = greq.gr_interface;
rtnl_lock();
lock_sock(sk);
if (optname == MCAST_JOIN_GROUP)
err = ip_mc_join_group(sk, &mreq);
else
err = ip_mc_leave_group(sk, &mreq);
release_sock(sk);
rtnl_unlock();
return err;
}
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
case MCAST_BLOCK_SOURCE:
case MCAST_UNBLOCK_SOURCE:
{
struct compat_group_source_req __user *gsr32 = (void __user *)optval;
struct group_source_req greqs;
if (optlen != sizeof(struct compat_group_source_req))
return -EINVAL;
if (get_user(greqs.gsr_interface, &gsr32->gsr_interface) ||
copy_from_user(&greqs.gsr_group, &gsr32->gsr_group,
sizeof(greqs.gsr_group)) ||
copy_from_user(&greqs.gsr_source, &gsr32->gsr_source,
sizeof(greqs.gsr_source)))
return -EFAULT;
rtnl_lock();
lock_sock(sk);
err = do_mcast_group_source(sk, optname, &greqs);
release_sock(sk);
rtnl_unlock();
return err;
}
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); err = do_ip_setsockopt(sk, level, optname, optval, optlen);
#ifdef CONFIG_NETFILTER #ifdef CONFIG_NETFILTER
...@@ -1465,19 +1581,28 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, ...@@ -1465,19 +1581,28 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
} }
case MCAST_MSFILTER: case MCAST_MSFILTER:
{ {
struct group_filter __user *p = (void __user *)optval;
struct group_filter gsf; struct group_filter gsf;
const int size0 = offsetof(struct group_filter, gf_slist);
int num;
if (len < GROUP_FILTER_SIZE(0)) { if (len < size0) {
err = -EINVAL; err = -EINVAL;
goto out; goto out;
} }
if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { if (copy_from_user(&gsf, p, size0)) {
err = -EFAULT; err = -EFAULT;
goto out; goto out;
} }
err = ip_mc_gsfget(sk, &gsf, num = gsf.gf_numsrc;
(struct group_filter __user *)optval, err = ip_mc_gsfget(sk, &gsf, p->gf_slist);
optlen); if (err)
goto out;
if (gsf.gf_numsrc < num)
num = gsf.gf_numsrc;
if (put_user(GROUP_FILTER_SIZE(num), optlen) ||
copy_to_user(p, &gsf, size0))
err = -EFAULT;
goto out; goto out;
} }
case IP_MULTICAST_ALL: case IP_MULTICAST_ALL:
...@@ -1590,9 +1715,47 @@ int compat_ip_getsockopt(struct sock *sk, int level, int optname, ...@@ -1590,9 +1715,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);
......
...@@ -136,6 +136,41 @@ static bool setsockopt_needs_rtnl(int optname) ...@@ -136,6 +136,41 @@ static bool setsockopt_needs_rtnl(int optname)
return false; return false;
} }
static int do_ipv6_mcast_group_source(struct sock *sk, int optname,
struct group_source_req *greqs)
{
int omode, add;
if (greqs->gsr_group.ss_family != AF_INET6 ||
greqs->gsr_source.ss_family != AF_INET6)
return -EADDRNOTAVAIL;
if (optname == MCAST_BLOCK_SOURCE) {
omode = MCAST_EXCLUDE;
add = 1;
} else if (optname == MCAST_UNBLOCK_SOURCE) {
omode = MCAST_EXCLUDE;
add = 0;
} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
struct sockaddr_in6 *psin6;
int retv;
psin6 = (struct sockaddr_in6 *)&greqs->gsr_group;
retv = ipv6_sock_mc_join_ssm(sk, greqs->gsr_interface,
&psin6->sin6_addr,
MCAST_INCLUDE);
/* prior join w/ different source is ok */
if (retv && retv != -EADDRINUSE)
return retv;
omode = MCAST_INCLUDE;
add = 1;
} else /* MCAST_LEAVE_SOURCE_GROUP */ {
omode = MCAST_INCLUDE;
add = 0;
}
return ip6_mc_source(add, omode, sk, greqs);
}
static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, unsigned int optlen) char __user *optval, unsigned int optlen)
{ {
...@@ -715,7 +750,6 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -715,7 +750,6 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
case MCAST_UNBLOCK_SOURCE: case MCAST_UNBLOCK_SOURCE:
{ {
struct group_source_req greqs; struct group_source_req greqs;
int omode, add;
if (optlen < sizeof(struct group_source_req)) if (optlen < sizeof(struct group_source_req))
goto e_inval; goto e_inval;
...@@ -723,34 +757,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -723,34 +757,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
retv = -EFAULT; retv = -EFAULT;
break; break;
} }
if (greqs.gsr_group.ss_family != AF_INET6 || retv = do_ipv6_mcast_group_source(sk, optname, &greqs);
greqs.gsr_source.ss_family != AF_INET6) {
retv = -EADDRNOTAVAIL;
break;
}
if (optname == MCAST_BLOCK_SOURCE) {
omode = MCAST_EXCLUDE;
add = 1;
} else if (optname == MCAST_UNBLOCK_SOURCE) {
omode = MCAST_EXCLUDE;
add = 0;
} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
struct sockaddr_in6 *psin6;
psin6 = (struct sockaddr_in6 *)&greqs.gsr_group;
retv = ipv6_sock_mc_join_ssm(sk, greqs.gsr_interface,
&psin6->sin6_addr,
MCAST_INCLUDE);
/* prior join w/ different source is ok */
if (retv && retv != -EADDRINUSE)
break;
omode = MCAST_INCLUDE;
add = 1;
} else /* MCAST_LEAVE_SOURCE_GROUP */ {
omode = MCAST_INCLUDE;
add = 0;
}
retv = ip6_mc_source(add, omode, sk, &greqs);
break; break;
} }
case MCAST_MSFILTER: case MCAST_MSFILTER:
...@@ -780,7 +787,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -780,7 +787,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
retv = -EINVAL; retv = -EINVAL;
break; break;
} }
retv = ip6_mc_msfilter(sk, gsf); retv = ip6_mc_msfilter(sk, gsf, gsf->gf_slist);
kfree(gsf); kfree(gsf);
break; break;
...@@ -973,9 +980,110 @@ int compat_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -973,9 +980,110 @@ int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
if (level != SOL_IPV6) if (level != SOL_IPV6)
return -ENOPROTOOPT; return -ENOPROTOOPT;
if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER) switch (optname) {
return compat_mc_setsockopt(sk, level, optname, optval, optlen, case MCAST_JOIN_GROUP:
ipv6_setsockopt); case MCAST_LEAVE_GROUP:
{
struct compat_group_req __user *gr32 = (void __user *)optval;
struct group_req greq;
struct sockaddr_in6 *psin6 = (struct sockaddr_in6 *)&greq.gr_group;
if (optlen < sizeof(struct compat_group_req))
return -EINVAL;
if (get_user(greq.gr_interface, &gr32->gr_interface) ||
copy_from_user(&greq.gr_group, &gr32->gr_group,
sizeof(greq.gr_group)))
return -EFAULT;
if (greq.gr_group.ss_family != AF_INET6)
return -EADDRNOTAVAIL;
rtnl_lock();
lock_sock(sk);
if (optname == MCAST_JOIN_GROUP)
err = ipv6_sock_mc_join(sk, greq.gr_interface,
&psin6->sin6_addr);
else
err = ipv6_sock_mc_drop(sk, greq.gr_interface,
&psin6->sin6_addr);
release_sock(sk);
rtnl_unlock();
return err;
}
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
case MCAST_BLOCK_SOURCE:
case MCAST_UNBLOCK_SOURCE:
{
struct compat_group_source_req __user *gsr32 = (void __user *)optval;
struct group_source_req greqs;
if (optlen < sizeof(struct compat_group_source_req))
return -EINVAL;
if (get_user(greqs.gsr_interface, &gsr32->gsr_interface) ||
copy_from_user(&greqs.gsr_group, &gsr32->gsr_group,
sizeof(greqs.gsr_group)) ||
copy_from_user(&greqs.gsr_source, &gsr32->gsr_source,
sizeof(greqs.gsr_source)))
return -EFAULT;
rtnl_lock();
lock_sock(sk);
err = do_ipv6_mcast_group_source(sk, optname, &greqs);
release_sock(sk);
rtnl_unlock();
return err;
}
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 >= 0x1ffffffU ||
n > sysctl_mld_max_msf) {
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);
err = ip6_mc_msfilter(sk, &(struct group_filter){
.gf_interface = gf32->gf_interface,
.gf_group = gf32->gf_group,
.gf_fmode = gf32->gf_fmode,
.gf_numsrc = gf32->gf_numsrc}, gf32->gf_slist);
release_sock(sk);
rtnl_unlock();
mc_msf_out:
kfree(p);
return err;
}
}
err = do_ipv6_setsockopt(sk, level, optname, optval, optlen); err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
#ifdef CONFIG_NETFILTER #ifdef CONFIG_NETFILTER
...@@ -1048,18 +1156,28 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, ...@@ -1048,18 +1156,28 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
break; break;
case MCAST_MSFILTER: case MCAST_MSFILTER:
{ {
struct group_filter __user *p = (void __user *)optval;
struct group_filter gsf; struct group_filter gsf;
const int size0 = offsetof(struct group_filter, gf_slist);
int num;
int err; int err;
if (len < GROUP_FILTER_SIZE(0)) if (len < size0)
return -EINVAL; return -EINVAL;
if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) if (copy_from_user(&gsf, p, size0))
return -EFAULT; return -EFAULT;
if (gsf.gf_group.ss_family != AF_INET6) if (gsf.gf_group.ss_family != AF_INET6)
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
num = gsf.gf_numsrc;
lock_sock(sk); lock_sock(sk);
err = ip6_mc_msfget(sk, &gsf, err = ip6_mc_msfget(sk, &gsf, p->gf_slist);
(struct group_filter __user *)optval, optlen); if (!err) {
if (num > gsf.gf_numsrc)
num = gsf.gf_numsrc;
if (put_user(GROUP_FILTER_SIZE(num), optlen) ||
copy_to_user(p, &gsf, size0))
err = -EFAULT;
}
release_sock(sk); release_sock(sk);
return err; return err;
} }
...@@ -1428,9 +1546,44 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname, ...@@ -1428,9 +1546,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);
......
...@@ -457,7 +457,8 @@ int ip6_mc_source(int add, int omode, struct sock *sk, ...@@ -457,7 +457,8 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
return err; return err;
} }
int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
struct sockaddr_storage *list)
{ {
const struct in6_addr *group; const struct in6_addr *group;
struct ipv6_mc_socklist *pmc; struct ipv6_mc_socklist *pmc;
...@@ -509,10 +510,10 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) ...@@ -509,10 +510,10 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
goto done; goto done;
} }
newpsl->sl_max = newpsl->sl_count = gsf->gf_numsrc; newpsl->sl_max = newpsl->sl_count = gsf->gf_numsrc;
for (i = 0; i < newpsl->sl_count; ++i) { for (i = 0; i < newpsl->sl_count; ++i, ++list) {
struct sockaddr_in6 *psin6; struct sockaddr_in6 *psin6;
psin6 = (struct sockaddr_in6 *)&gsf->gf_slist[i]; psin6 = (struct sockaddr_in6 *)list;
newpsl->sl_addr[i] = psin6->sin6_addr; newpsl->sl_addr[i] = psin6->sin6_addr;
} }
err = ip6_mc_add_src(idev, group, gsf->gf_fmode, err = ip6_mc_add_src(idev, group, gsf->gf_fmode,
...@@ -547,7 +548,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) ...@@ -547,7 +548,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
} }
int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
struct group_filter __user *optval, int __user *optlen) struct sockaddr_storage *p)
{ {
int err, i, count, copycount; int err, i, count, copycount;
const struct in6_addr *group; const struct in6_addr *group;
...@@ -592,14 +593,10 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, ...@@ -592,14 +593,10 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
gsf->gf_numsrc = count; gsf->gf_numsrc = count;
if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) {
return -EFAULT;
}
/* changes to psl require the socket lock, and a write lock /* changes to psl require the socket lock, and a write lock
* on pmc->sflock. We have the socket lock so reading here is safe. * on pmc->sflock. We have the socket lock so reading here is safe.
*/ */
for (i = 0; i < copycount; i++) { for (i = 0; i < copycount; i++, p++) {
struct sockaddr_in6 *psin6; struct sockaddr_in6 *psin6;
struct sockaddr_storage ss; struct sockaddr_storage ss;
...@@ -607,7 +604,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, ...@@ -607,7 +604,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
memset(&ss, 0, sizeof(ss)); memset(&ss, 0, sizeof(ss));
psin6->sin6_family = AF_INET6; psin6->sin6_family = AF_INET6;
psin6->sin6_addr = psl->sl_addr[i]; psin6->sin6_addr = psl->sl_addr[i];
if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss))) if (copy_to_user(p, &ss, sizeof(ss)))
return -EFAULT; return -EFAULT;
} }
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment