Loading net/ipv6/ipv6_sockglue.c +83 −76 Original line number Diff line number Diff line Loading @@ -171,6 +171,87 @@ static int do_ipv6_mcast_group_source(struct sock *sk, int optname, return ip6_mc_source(add, omode, sk, greqs); } static int ipv6_set_mcast_msfilter(struct sock *sk, void __user *optval, int optlen) { struct group_filter *gsf; int ret; if (optlen < GROUP_FILTER_SIZE(0)) return -EINVAL; if (optlen > sysctl_optmem_max) return -ENOBUFS; gsf = memdup_user(optval, optlen); if (IS_ERR(gsf)) return PTR_ERR(gsf); /* numsrc >= (4G-140)/128 overflow in 32 bits */ ret = -ENOBUFS; if (gsf->gf_numsrc >= 0x1ffffffU || gsf->gf_numsrc > sysctl_mld_max_msf) goto out_free_gsf; ret = -EINVAL; if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) goto out_free_gsf; ret = ip6_mc_msfilter(sk, gsf, gsf->gf_slist); out_free_gsf: kfree(gsf); return ret; } #ifdef CONFIG_COMPAT static int compat_ipv6_set_mcast_msfilter(struct sock *sk, void __user *optval, int optlen) { const int size0 = offsetof(struct compat_group_filter, gf_slist); struct compat_group_filter *gf32; void *p; int ret; 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 */ ret = -EFAULT; if (copy_from_user(gf32, optval, optlen)) goto out_free_p; /* numsrc >= (4G-140)/128 overflow in 32 bits */ ret = -ENOBUFS; n = gf32->gf_numsrc; if (n >= 0x1ffffffU || n > sysctl_mld_max_msf) goto out_free_p; ret = -EINVAL; if (offsetof(struct compat_group_filter, gf_slist[n]) > optlen) goto out_free_p; rtnl_lock(); lock_sock(sk); ret = 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(); out_free_p: kfree(p); return ret; } #endif static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen) { Loading Loading @@ -762,37 +843,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, break; } case MCAST_MSFILTER: { struct group_filter *gsf; if (optlen < GROUP_FILTER_SIZE(0)) goto e_inval; if (optlen > sysctl_optmem_max) { retv = -ENOBUFS; break; } gsf = memdup_user(optval, optlen); if (IS_ERR(gsf)) { retv = PTR_ERR(gsf); break; } /* numsrc >= (4G-140)/128 overflow in 32 bits */ if (gsf->gf_numsrc >= 0x1ffffffU || gsf->gf_numsrc > sysctl_mld_max_msf) { kfree(gsf); retv = -ENOBUFS; break; } if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { kfree(gsf); retv = -EINVAL; retv = ipv6_set_mcast_msfilter(sk, optval, optlen); break; } retv = ip6_mc_msfilter(sk, gsf, gsf->gf_slist); kfree(gsf); break; } case IPV6_ROUTER_ALERT: if (optlen < sizeof(int)) goto e_inval; Loading Loading @@ -977,52 +1029,7 @@ int compat_ipv6_setsockopt(struct sock *sk, int level, int optname, 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; } return compat_ipv6_set_mcast_msfilter(sk, optval, optlen); } err = do_ipv6_setsockopt(sk, level, optname, optval, optlen); Loading Loading
net/ipv6/ipv6_sockglue.c +83 −76 Original line number Diff line number Diff line Loading @@ -171,6 +171,87 @@ static int do_ipv6_mcast_group_source(struct sock *sk, int optname, return ip6_mc_source(add, omode, sk, greqs); } static int ipv6_set_mcast_msfilter(struct sock *sk, void __user *optval, int optlen) { struct group_filter *gsf; int ret; if (optlen < GROUP_FILTER_SIZE(0)) return -EINVAL; if (optlen > sysctl_optmem_max) return -ENOBUFS; gsf = memdup_user(optval, optlen); if (IS_ERR(gsf)) return PTR_ERR(gsf); /* numsrc >= (4G-140)/128 overflow in 32 bits */ ret = -ENOBUFS; if (gsf->gf_numsrc >= 0x1ffffffU || gsf->gf_numsrc > sysctl_mld_max_msf) goto out_free_gsf; ret = -EINVAL; if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) goto out_free_gsf; ret = ip6_mc_msfilter(sk, gsf, gsf->gf_slist); out_free_gsf: kfree(gsf); return ret; } #ifdef CONFIG_COMPAT static int compat_ipv6_set_mcast_msfilter(struct sock *sk, void __user *optval, int optlen) { const int size0 = offsetof(struct compat_group_filter, gf_slist); struct compat_group_filter *gf32; void *p; int ret; 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 */ ret = -EFAULT; if (copy_from_user(gf32, optval, optlen)) goto out_free_p; /* numsrc >= (4G-140)/128 overflow in 32 bits */ ret = -ENOBUFS; n = gf32->gf_numsrc; if (n >= 0x1ffffffU || n > sysctl_mld_max_msf) goto out_free_p; ret = -EINVAL; if (offsetof(struct compat_group_filter, gf_slist[n]) > optlen) goto out_free_p; rtnl_lock(); lock_sock(sk); ret = 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(); out_free_p: kfree(p); return ret; } #endif static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen) { Loading Loading @@ -762,37 +843,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, break; } case MCAST_MSFILTER: { struct group_filter *gsf; if (optlen < GROUP_FILTER_SIZE(0)) goto e_inval; if (optlen > sysctl_optmem_max) { retv = -ENOBUFS; break; } gsf = memdup_user(optval, optlen); if (IS_ERR(gsf)) { retv = PTR_ERR(gsf); break; } /* numsrc >= (4G-140)/128 overflow in 32 bits */ if (gsf->gf_numsrc >= 0x1ffffffU || gsf->gf_numsrc > sysctl_mld_max_msf) { kfree(gsf); retv = -ENOBUFS; break; } if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { kfree(gsf); retv = -EINVAL; retv = ipv6_set_mcast_msfilter(sk, optval, optlen); break; } retv = ip6_mc_msfilter(sk, gsf, gsf->gf_slist); kfree(gsf); break; } case IPV6_ROUTER_ALERT: if (optlen < sizeof(int)) goto e_inval; Loading Loading @@ -977,52 +1029,7 @@ int compat_ipv6_setsockopt(struct sock *sk, int level, int optname, 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; } return compat_ipv6_set_mcast_msfilter(sk, optval, optlen); } err = do_ipv6_setsockopt(sk, level, optname, optval, optlen); Loading