aboutsummaryrefslogtreecommitdiffstats
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
42 42
43extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, int, 43extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, int,
44 int (*)(struct sock *, int, int, char __user *, int)); 44 int (*)(struct sock *, int, int, char __user *, int));
45extern int compat_mc_getsockopt(struct sock *, int, int, char __user *,
46 int __user *, int (*)(struct sock *, int, int, char __user *,
47 int __user *));
45 48
46#endif /* NET_COMPAT_H */ 49#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,
640 640
641EXPORT_SYMBOL(compat_mc_setsockopt); 641EXPORT_SYMBOL(compat_mc_setsockopt);
642 642
643int compat_mc_getsockopt(struct sock *sock, int level, int optname,
644 char __user *optval, int __user *optlen,
645 int (*getsockopt)(struct sock *,int,int,char __user *,int __user *))
646{
647 struct compat_group_filter __user *gf32 = (void *)optval;
648 struct group_filter __user *kgf;
649 int __user *koptlen;
650 u32 interface, fmode, numsrc;
651 int klen, ulen, err;
652
653 if (optname != MCAST_MSFILTER)
654 return getsockopt(sock, level, optname, optval, optlen);
655
656 koptlen = compat_alloc_user_space(sizeof(*koptlen));
657 if (!access_ok(VERIFY_READ, optlen, sizeof(*optlen)) ||
658 __get_user(ulen, optlen))
659 return -EFAULT;
660
661 /* adjust len for pad */
662 klen = ulen + sizeof(*kgf) - sizeof(*gf32);
663
664 if (klen < GROUP_FILTER_SIZE(0))
665 return -EINVAL;
666
667 if (!access_ok(VERIFY_WRITE, koptlen, sizeof(*koptlen)) ||
668 __put_user(klen, koptlen))
669 return -EFAULT;
670
671 /* have to allow space for previous compat_alloc_user_space, too */
672 kgf = compat_alloc_user_space(klen+sizeof(*optlen));
673
674 if (!access_ok(VERIFY_READ, gf32, __COMPAT_GF0_SIZE) ||
675 __get_user(interface, &gf32->gf_interface) ||
676 __get_user(fmode, &gf32->gf_fmode) ||
677 __get_user(numsrc, &gf32->gf_numsrc) ||
678 __put_user(interface, &kgf->gf_interface) ||
679 __put_user(fmode, &kgf->gf_fmode) ||
680 __put_user(numsrc, &kgf->gf_numsrc) ||
681 copy_in_user(&kgf->gf_group,&gf32->gf_group,sizeof(kgf->gf_group)))
682 return -EFAULT;
683
684 err = getsockopt(sock, level, optname, (char __user *)kgf, koptlen);
685 if (err)
686 return err;
687
688 if (!access_ok(VERIFY_READ, koptlen, sizeof(*koptlen)) ||
689 __get_user(klen, koptlen))
690 return -EFAULT;
691
692 ulen = klen - (sizeof(*kgf)-sizeof(*gf32));
693
694 if (!access_ok(VERIFY_WRITE, optlen, sizeof(*optlen)) ||
695 __put_user(ulen, optlen))
696 return -EFAULT;
697
698 if (!access_ok(VERIFY_READ, kgf, klen) ||
699 !access_ok(VERIFY_WRITE, gf32, ulen) ||
700 __get_user(interface, &kgf->gf_interface) ||
701 __get_user(fmode, &kgf->gf_fmode) ||
702 __get_user(numsrc, &kgf->gf_numsrc) ||
703 __put_user(interface, &gf32->gf_interface) ||
704 __put_user(fmode, &gf32->gf_fmode) ||
705 __put_user(numsrc, &gf32->gf_numsrc))
706 return -EFAULT;
707 if (numsrc) {
708 int copylen;
709
710 klen -= GROUP_FILTER_SIZE(0);
711 copylen = numsrc * sizeof(gf32->gf_slist[0]);
712 if (copylen > klen)
713 copylen = klen;
714 if (copy_in_user(gf32->gf_slist, kgf->gf_slist, copylen))
715 return -EFAULT;
716 }
717 return err;
718}
719
720EXPORT_SYMBOL(compat_mc_getsockopt);
721
643 722
644/* Argument list sizes for compat_sys_socketcall */ 723/* Argument list sizes for compat_sys_socketcall */
645#define AL(x) ((x) * sizeof(u32)) 724#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,
1186int compat_ip_getsockopt(struct sock *sk, int level, int optname, 1186int compat_ip_getsockopt(struct sock *sk, int level, int optname,
1187 char __user *optval, int __user *optlen) 1187 char __user *optval, int __user *optlen)
1188{ 1188{
1189 int err = do_ip_getsockopt(sk, level, optname, optval, optlen); 1189 int err;
1190
1191 if (optname == MCAST_MSFILTER)
1192 return compat_mc_getsockopt(sk, level, optname, optval, optlen,
1193 ip_getsockopt);
1194
1195 err = do_ip_getsockopt(sk, level, optname, optval, optlen);
1196
1190#ifdef CONFIG_NETFILTER 1197#ifdef CONFIG_NETFILTER
1191 /* we need to exclude all possible ENOPROTOOPTs except default case */ 1198 /* we need to exclude all possible ENOPROTOOPTs except default case */
1192 if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && 1199 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,
1089 if(level != SOL_IPV6) 1089 if(level != SOL_IPV6)
1090 return -ENOPROTOOPT; 1090 return -ENOPROTOOPT;
1091 1091
1092 if (optname == MCAST_MSFILTER)
1093 return compat_mc_getsockopt(sk, level, optname, optval, optlen,
1094 ipv6_getsockopt);
1095
1092 err = do_ipv6_getsockopt(sk, level, optname, optval, optlen); 1096 err = do_ipv6_getsockopt(sk, level, optname, optval, optlen);
1093#ifdef CONFIG_NETFILTER 1097#ifdef CONFIG_NETFILTER
1094 /* we need to exclude all possible ENOPROTOOPTs except default case */ 1098 /* we need to exclude all possible ENOPROTOOPTs except default case */