netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
@ 2008-04-24  8:02 YOSHIFUJI Hideaki / 吉藤英明
  2008-04-24  8:08 ` David Miller
  2008-04-24 17:52 ` David Stevens
  0 siblings, 2 replies; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-24  8:02 UTC (permalink / raw)
  To: davem; +Cc: dlstevens, yoshfuji, netdev

Dave,

Please consider pulling following changes on top of net-2.6
to fix several bugs related to 32-bit SSM (Source Specific Multicast)
applications on 64bit kernel because of difference of alignment
requirements between 64bit mode and 32bit mode.

IPv4 side should be fixed as well, but it is less critical
because we have ip_mreqn / ip_mreq_source /ip_msfilter,
which does not need compat layer.  Anyway, I plan to post
IPv4 side as well.

Tree is available at
	git://git.linux-ipv6.org/gitroot/yoshfuji/linux-2.6-dev.git net-2.6-gd7d31300/inet6/05_compat-20080423

Thanks.

----

HEADLINES
---------

    [NET]: Introduce __compat_sockaddr_storage{}.
    [IPV6] COMPAT: Introduce compat_do_ipv6_{set,get}sockopt() wrapper.
    [IPV6] MCAST: Move mode check for group_source_req{}-sockopt to net/ipv6/mcast.c.
    [IPV6] COMPAT: group_source_req{}-setsockopt compat layer.
    [IPV6] COMPAT: group_req{}-setsockopt compat layer.
    [IPV6] COMPAT: group_filter{}-setsockopt compat layer.
    [IPV6] MCAST: Split ip6_mc_msfget().
    [IPV6] MCAST: Make ip6_mc_msfget() more generic.
    [IPV6] COMPAT: group_filter{}-getsockopt compat layer.

DIFFSTAT
--------

 include/linux/in.h       |   31 +++++++
 include/linux/socket.h   |    7 ++
 include/net/ipv6.h       |    6 +
 net/ipv6/ipv6_sockglue.c |  181 +++++++++++++++++++++++++++++++++++--------
 net/ipv6/mcast.c         |  194 +++++++++++++++++++++++++++++++++++-----------
 5 files changed, 338 insertions(+), 81 deletions(-)

CHANGESETS
----------

commit 0f382e910bc7d34e5619c6a038efa2e3396359d0
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Tue Apr 22 01:12:25 2008 +0900

    [NET]: Introduce __compat_sockaddr_storage{}.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/linux/socket.h b/include/linux/socket.h
index bd2b30a..a3d77e7 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -16,6 +16,13 @@ struct __kernel_sockaddr_storage {
 				/* _SS_MAXSIZE value minus size of ss_family */
 } __attribute__ ((aligned(_K_SS_ALIGNSIZE)));	/* force desired alignment */
 
+#ifdef __KERNEL__
+struct __compat_sockaddr_storage {
+	unsigned short  ss_family;
+	char		__data[_K_SS_MAXSIZE - sizeof(unsigned short)];
+} __attribute__ ((aligned(4)));
+#endif
+
 #if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
 
 #include <asm/socket.h>			/* arch-dependent defines	*/

---
commit 70be517de9b9582ace4633918f967c61345a978f
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Wed Apr 23 18:00:20 2008 +0900

    [IPV6] COMPAT: Introduce compat_do_ipv6_{set,get}sockopt() wrapper.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 06de9d0..a53aed4 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -764,6 +764,13 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
 EXPORT_SYMBOL(ipv6_setsockopt);
 
 #ifdef CONFIG_COMPAT
+static int
+compat_do_ipv6_setsockopt(struct sock *sk, int level, int optname,
+			  char __user *optval, int optlen)
+{
+	return do_ipv6_setsockopt(sk, level, optname, optval, optlen);
+}
+
 int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
 			   char __user *optval, int optlen)
 {
@@ -779,7 +786,7 @@ int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
 	if (level != SOL_IPV6)
 		return -ENOPROTOOPT;
 
-	err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
+	err = compat_do_ipv6_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
 	/* we need to exclude all possible ENOPROTOOPTs except default case */
 	if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY &&
@@ -1107,6 +1114,13 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
 EXPORT_SYMBOL(ipv6_getsockopt);
 
 #ifdef CONFIG_COMPAT
+static int
+compat_do_ipv6_getsockopt(struct sock *sk, int level, int optname,
+			  char __user *optval, int __user *optlen)
+{
+	return do_ipv6_getsockopt(sk, level, optname, optval, optlen);
+}
+
 int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
 			   char __user *optval, int __user *optlen)
 {
@@ -1122,7 +1136,7 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
 	if (level != SOL_IPV6)
 		return -ENOPROTOOPT;
 
-	err = do_ipv6_getsockopt(sk, level, optname, optval, optlen);
+	err = compat_do_ipv6_getsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
 	/* we need to exclude all possible ENOPROTOOPTs except default case */
 	if (err == -ENOPROTOOPT && optname != IPV6_2292PKTOPTIONS) {

---
commit 5f4a79968bd9b82d06324ddc10ae41d95fd53896
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Tue Apr 22 12:14:20 2008 +0900

    [IPV6] MCAST: Move mode check for group_source_req{}-sockopt to net/ipv6/mcast.c.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 49c4898..aa81120 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -565,7 +565,7 @@ extern const struct proto_ops inet6_dgram_ops;
 struct group_source_req;
 struct group_filter;
 
-extern int ip6_mc_source(int add, int omode, struct sock *sk,
+extern int ip6_mc_source(struct sock *sk, int optname,
 			 struct group_source_req *pgsr);
 extern int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf);
 extern int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index a53aed4..694e031 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -542,7 +542,6 @@ done:
 	case MCAST_UNBLOCK_SOURCE:
 	{
 		struct group_source_req greqs;
-		int omode, add;
 
 		if (optlen < sizeof(struct group_source_req))
 			goto e_inval;
@@ -550,33 +549,7 @@ done:
 			retv = -EFAULT;
 			break;
 		}
-		if (greqs.gsr_group.ss_family != AF_INET6 ||
-		    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(sk, greqs.gsr_interface,
-				&psin6->sin6_addr);
-			/* 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);
+		retv = ip6_mc_source(sk, optname, &greqs);
 		break;
 	}
 	case MCAST_MSFILTER:
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 54f91ef..5d8d653 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -355,8 +355,8 @@ void ipv6_sock_mc_close(struct sock *sk)
 	write_unlock_bh(&ipv6_sk_mc_lock);
 }
 
-int ip6_mc_source(int add, int omode, struct sock *sk,
-	struct group_source_req *pgsr)
+static int __ip6_mc_source(int add, int omode, struct sock *sk,
+			   struct group_source_req *pgsr)
 {
 	struct in6_addr *source, *group;
 	struct ipv6_mc_socklist *pmc;
@@ -496,6 +496,40 @@ done:
 	return err;
 }
 
+int ip6_mc_source(struct sock *sk, int optname,
+		  struct group_source_req *pgsr)
+{
+	int omode, add, retv;
+
+	if (pgsr->gsr_group.ss_family != AF_INET6 ||
+	    pgsr->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;
+
+		psin6 = (struct sockaddr_in6 *)&pgsr->gsr_group;
+		retv = ipv6_sock_mc_join(sk, pgsr->gsr_interface,
+					 &psin6->sin6_addr);
+		/* 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, pgsr);
+}
+
 int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
 {
 	struct in6_addr *group;

---
commit a001dde8990027b0235ce729a8ce3acebc819bdd
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Tue Apr 22 12:22:20 2008 +0900

    [IPV6] COMPAT: group_source_req{}-setsockopt compat layer.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/linux/in.h b/include/linux/in.h
index 4065313..cbeaee0 100644
--- a/include/linux/in.h
+++ b/include/linux/in.h
@@ -156,6 +156,14 @@ struct group_source_req
 	struct __kernel_sockaddr_storage gsr_source;	/* source address */
 };
 
+#ifdef __KERNEL__
+struct compat_group_source_req {
+	__u32					gsr_interface;
+	struct __compat_sockaddr_storage	gsr_group;
+	struct __compat_sockaddr_storage	gsr_source;
+};
+#endif
+
 struct group_filter
 {
 	__u32				 gf_interface;	/* interface index */
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 694e031..b7ae360 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -741,7 +741,37 @@ static int
 compat_do_ipv6_setsockopt(struct sock *sk, int level, int optname,
 			  char __user *optval, int optlen)
 {
-	return do_ipv6_setsockopt(sk, level, optname, optval, optlen);
+	int retv;
+
+	switch (optname) {
+	case MCAST_JOIN_SOURCE_GROUP:
+	case MCAST_LEAVE_SOURCE_GROUP:
+	case MCAST_BLOCK_SOURCE:
+	case MCAST_UNBLOCK_SOURCE:
+	    {
+		struct group_source_req greqs;
+		struct compat_group_source_req __user *ugreqs = (void __user *)optval;
+
+		if (optlen < sizeof(*ugreqs)) {
+			retv = -EINVAL;
+			break;
+		}
+		if (access_ok(VERIFY_READ, optval, sizeof(*ugreqs)) ||
+		    __get_user(greqs.gsr_interface, &ugreqs->gsr_interface) ||
+		    __copy_from_user(&greqs.gsr_group, &ugreqs->gsr_group,
+				     sizeof(greqs.gsr_group)) ||
+		    __copy_from_user(&greqs.gsr_source, &ugreqs->gsr_source,
+				     sizeof(greqs.gsr_source))) {
+			retv = -EFAULT;
+			break;
+		}
+		retv = ip6_mc_source(sk, optname, &greqs);
+		break;
+	    }
+	default:
+		retv = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
+	}
+	return retv;
 }
 
 int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,

---
commit 1190e439a3d0b631349d068f534e5b912ede72b5
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Tue Apr 22 12:29:11 2008 +0900

    [IPV6] COMPAT: group_req{}-setsockopt compat layer.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/linux/in.h b/include/linux/in.h
index cbeaee0..9ec8e2a 100644
--- a/include/linux/in.h
+++ b/include/linux/in.h
@@ -149,6 +149,14 @@ struct group_req
 	struct __kernel_sockaddr_storage gr_group;	/* group address */
 };
 
+#ifdef __KERNEL__
+struct compat_group_req
+{
+	__u32				 gr_interface;
+	struct __compat_sockaddr_storage gr_group;
+};
+#endif
+
 struct group_source_req
 {
 	__u32				 gsr_interface;	/* interface index */
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index b7ae360..30e6ca1 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -744,6 +744,32 @@ compat_do_ipv6_setsockopt(struct sock *sk, int level, int optname,
 	int retv;
 
 	switch (optname) {
+	case MCAST_JOIN_GROUP:
+	case MCAST_LEAVE_GROUP:
+	    {
+		struct group_req greq;
+		struct compat_group_req __user *ugreq = (void __user *)optval;
+		struct sockaddr_in6 *psin6;
+
+		if (optlen < sizeof(*ugreq))
+			return -EINVAL;
+		if (access_ok(VERIFY_READ, ugreq, sizeof(*ugreq)) ||
+		    __get_user(greq.gr_interface, &ugreq->gr_interface) ||
+		    __copy_from_user(&greq.gr_group, &ugreq->gr_group,
+				     sizeof(greq.gr_group)))
+			return -EFAULT;
+		if (greq.gr_group.ss_family != AF_INET6)
+			return -EADDRNOTAVAIL;
+
+		psin6 = (struct sockaddr_in6 *)&greq.gr_group;
+		if (optname == MCAST_JOIN_GROUP)
+			retv = ipv6_sock_mc_join(sk, greq.gr_interface,
+				&psin6->sin6_addr);
+		else
+			retv = ipv6_sock_mc_drop(sk, greq.gr_interface,
+				&psin6->sin6_addr);
+		break;
+	    }
 	case MCAST_JOIN_SOURCE_GROUP:
 	case MCAST_LEAVE_SOURCE_GROUP:
 	case MCAST_BLOCK_SOURCE:

---
commit 947e9bb2d318179936b414250d2ae31398aff0ba
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Tue Apr 22 12:32:44 2008 +0900

    [IPV6] COMPAT: group_filter{}-setsockopt compat layer.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/linux/in.h b/include/linux/in.h
index 9ec8e2a..14a3cdb 100644
--- a/include/linux/in.h
+++ b/include/linux/in.h
@@ -185,6 +185,21 @@ struct group_filter
 	(sizeof(struct group_filter) - sizeof(struct __kernel_sockaddr_storage) \
 	+ (numsrc) * sizeof(struct __kernel_sockaddr_storage))
 
+#ifdef __KERNEL__
+struct compat_group_filter
+{
+	__u32					gf_interface;
+	struct __compat_sockaddr_storage	gf_group;
+	__u32					gf_fmode;
+	__u32					gf_numsrc;
+	struct __compat_sockaddr_storage	gf_slist[1];
+};
+
+#define COMPAT_GROUP_FILTER_SIZE(numsrc) \
+	(sizeof(struct compat_group_filter) - sizeof(struct __compat_sockaddr_storage) \
+	+ (numsrc) * sizeof(struct __compat_sockaddr_storage))
+#endif
+
 struct in_pktinfo
 {
 	int		ipi_ifindex;
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 30e6ca1..0895e7d 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -794,6 +794,57 @@ compat_do_ipv6_setsockopt(struct sock *sk, int level, int optname,
 		retv = ip6_mc_source(sk, optname, &greqs);
 		break;
 	    }
+	case MCAST_MSFILTER:
+	    {
+		extern int sysctl_mld_max_msf;
+		struct compat_group_filter __user *ugsf = (void __user *)optval;
+		struct group_filter *gsf;
+		u32 gf_numsrc;
+		int i;
+
+		if (optlen < COMPAT_GROUP_FILTER_SIZE(0))
+			return -EINVAL;
+
+		if (access_ok(VERIFY_READ, ugsf, COMPAT_GROUP_FILTER_SIZE(0)) ||
+		    __get_user(gf_numsrc, &ugsf->gf_numsrc))
+			return -EFAULT;
+
+		if (COMPAT_GROUP_FILTER_SIZE(gf_numsrc) > optlen)
+			return -EINVAL;
+
+		if (optlen > sysctl_optmem_max ||
+		    gf_numsrc >= 0x1ffffffU ||
+		    gf_numsrc > sysctl_mld_max_msf)
+			return -ENOBUFS;
+
+		optlen = GROUP_FILTER_SIZE(gf_numsrc);
+		gsf = kmalloc(optlen, GFP_KERNEL);
+		if (!gsf) {
+			retv = -ENOBUFS;
+			break;
+		}
+		retv = -EFAULT;
+		if (__get_user(gsf->gf_interface, &ugsf->gf_interface) ||
+		    __copy_from_user(&gsf->gf_group, &ugsf->gf_group, sizeof(gsf->gf_group)) ||
+		    __get_user(gsf->gf_fmode, &ugsf->gf_fmode) ||
+		    access_ok(VERIFY_READ, ugsf->gf_slist,
+			      sizeof(struct __compat_sockaddr_storage) * gf_numsrc))
+			goto kfree_out;
+
+		gsf->gf_numsrc = gf_numsrc;
+
+		for (i = 0; i < gf_numsrc; i++) {
+			if (__copy_from_user(&gsf->gf_slist[i],
+					     &ugsf->gf_slist[i],
+					     sizeof(gsf->gf_slist[i])))
+				goto kfree_out;
+		}
+
+		retv = ip6_mc_msfilter(sk, gsf);
+kfree_out:
+		kfree(gsf);
+		break;
+	    }
 	default:
 		retv = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
 	}

---
commit 19e24c1dabcc43c2febe076afd1e984cf1e13979
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Tue Apr 22 19:16:33 2008 +0900

    [IPV6] MCAST: Split ip6_mc_msfget().
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 5d8d653..d32d6a3 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -621,79 +621,99 @@ done:
 	return err;
 }
 
-int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
-	struct group_filter __user *optval, int __user *optlen)
+int __ip6_mc_msf_lookup(struct sock *sk,
+			struct in6_addr *group, int interface,
+			struct ip6_sf_socklist **ppsl)
 {
-	int err, i, count, copycount;
-	struct in6_addr *group;
 	struct ipv6_mc_socklist *pmc;
-	struct inet6_dev *idev;
-	struct net_device *dev;
 	struct ipv6_pinfo *inet6 = inet6_sk(sk);
-	struct ip6_sf_socklist *psl;
+	struct net_device *dev;
+	struct inet6_dev *idev;
 	struct net *net = sock_net(sk);
+	int ret = 0;
 
-	group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
-
-	if (!ipv6_addr_is_multicast(group))
-		return -EINVAL;
-
-	idev = ip6_mc_find_dev(net, group, gsf->gf_interface);
+	if (!ipv6_addr_is_multicast(group)) {
+		ret = -EINVAL;
+		goto out;
+	}
 
-	if (!idev)
-		return -ENODEV;
+	idev = ip6_mc_find_dev(net, group, interface);
+	if (!idev) {
+		ret = -ENODEV;
+		goto out;
+	}
 
 	dev = idev->dev;
 
-	err = -EADDRNOTAVAIL;
+	ret = -EADDRNOTAVAIL;
 	/*
 	 * changes to the ipv6_mc_list require the socket lock and
 	 * a read lock on ip6_sk_mc_lock. We have the socket lock,
 	 * so reading the list is safe.
 	 */
 
-	for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) {
-		if (pmc->ifindex != gsf->gf_interface)
+	for (pmc = inet6->ipv6_mc_list; pmc; pmc = pmc->next) {
+		if (pmc->ifindex != interface)
 			continue;
 		if (ipv6_addr_equal(group, &pmc->addr))
 			break;
 	}
-	if (!pmc)		/* must have a prior join */
-		goto done;
-	gsf->gf_fmode = pmc->sfmode;
-	psl = pmc->sflist;
-	count = psl ? psl->sl_count : 0;
+	if (pmc) {
+		*ppsl = pmc->sflist;
+		ret = pmc->sfmode;
+	}
+
 	read_unlock_bh(&idev->lock);
 	in6_dev_put(idev);
 	dev_put(dev);
+out:
+	return ret;
+}
 
-	copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
-	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, a read lock on
-	 * on ipv6_sk_mc_lock and a write lock on pmc->sflock. We
-	 * have the socket lock, so reading here is safe.
-	 */
-	for (i=0; i<copycount; i++) {
-		struct sockaddr_in6 *psin6;
-		struct sockaddr_storage ss;
+int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
+	struct group_filter __user *optval, int __user *optlen)
+{
+	struct in6_addr *group;
+	struct ip6_sf_socklist *psl;
+	int fmode;
+
+	group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
+
+	fmode = __ip6_mc_msf_lookup(sk, group, gsf->gf_interface, &psl);
+	if (fmode < 0)
+		return fmode;
 
-		psin6 = (struct sockaddr_in6 *)&ss;
-		memset(&ss, 0, sizeof(ss));
-		psin6->sin6_family = AF_INET6;
-		psin6->sin6_addr = psl->sl_addr[i];
-		if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss)))
+	if (psl) {
+		int count = psl ? psl->sl_count : 0;
+		int i, copycount;
+		struct sockaddr_in6 sin6;
+
+		copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
+
+		if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
+		    access_ok(VERIFY_WRITE, optval, GROUP_FILTER_SIZE(copycount)) ||
+		    __put_user((u32)fmode, &optval->gf_fmode) ||
+		    __put_user((u32)count, &optval->gf_numsrc))
 			return -EFAULT;
+
+		/* changes to psl require the socket lock, a read lock on
+		 * on ipv6_sk_mc_lock and a write lock on pmc->sflock. We
+		 * have the socket lock, so reading here is safe.
+		 */
+		memset(&sin6, 0, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+
+		for (i = 0; i < copycount; i++) {
+			struct sockaddr_in6 __user *usin6 = (void __user *)&optval->gf_slist[i];
+
+			ipv6_addr_copy(&sin6.sin6_addr, &psl->sl_addr[i]);
+
+			if (__copy_to_user(usin6, &sin6, sizeof(sin6)) ||
+			    __clear_user(usin6 + 1, sizeof(optval->gf_slist[i]) - sizeof(*usin6)))
+				return -EFAULT;
+		}
 	}
 	return 0;
-done:
-	read_unlock_bh(&idev->lock);
-	in6_dev_put(idev);
-	dev_put(dev);
-	return err;
 }
 
 int inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,

---
commit 533a1a4139aee99a4068f804b0d3db91152dab42
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Wed Apr 23 18:03:14 2008 +0900

    [IPV6] MCAST: Make ip6_mc_msfget() more generic.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index aa81120..103272f 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -568,7 +568,9 @@ struct group_filter;
 extern int ip6_mc_source(struct sock *sk, int optname,
 			 struct group_source_req *pgsr);
 extern int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf);
-extern int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
+extern int ip6_mc_msfget(struct sock *sk,
+			 struct in6_addr *group, u32 gf_interface,
+			 u32 gf_srcnum,
 			 struct group_filter __user *optval,
 			 int __user *optlen);
 
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 0895e7d..d670078 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -949,8 +949,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
 		if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0)))
 			return -EFAULT;
 		lock_sock(sk);
-		err = ip6_mc_msfget(sk, &gsf,
-			(struct group_filter __user *)optval, optlen);
+		err = ip6_mc_msfget(sk,
+				    &((struct sockaddr_in6 *)&gsf.gf_group)->sin6_addr,
+				    gsf.gf_interface, gsf.gf_numsrc,
+				    (struct group_filter __user *)optval, optlen);
 		release_sock(sk);
 		return err;
 	}
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index d32d6a3..b781192 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -670,16 +670,14 @@ out:
 	return ret;
 }
 
-int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
-	struct group_filter __user *optval, int __user *optlen)
+int ip6_mc_msfget(struct sock *sk,
+		  struct in6_addr *group, u32 gf_interface, u32 gf_numsrc,
+		  struct group_filter __user *optval, int __user *optlen)
 {
-	struct in6_addr *group;
 	struct ip6_sf_socklist *psl;
 	int fmode;
 
-	group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
-
-	fmode = __ip6_mc_msf_lookup(sk, group, gsf->gf_interface, &psl);
+	fmode = __ip6_mc_msf_lookup(sk, group, gf_interface, &psl);
 	if (fmode < 0)
 		return fmode;
 
@@ -688,7 +686,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
 		int i, copycount;
 		struct sockaddr_in6 sin6;
 
-		copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
+		copycount = count < gf_numsrc ? count : gf_numsrc;
 
 		if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
 		    access_ok(VERIFY_WRITE, optval, GROUP_FILTER_SIZE(copycount)) ||

---
commit f14168835b7e389649383a285015a83d4d4e7968
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Wed Apr 23 18:06:37 2008 +0900

    [IPV6] COMPAT: group_filter{}-getsockopt compat layer.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index d670078..27d9b3d 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -1200,7 +1200,28 @@ static int
 compat_do_ipv6_getsockopt(struct sock *sk, int level, int optname,
 			  char __user *optval, int __user *optlen)
 {
-	return do_ipv6_getsockopt(sk, level, optname, optval, optlen);
+	switch (optname) {
+	case MCAST_MSFILTER:
+	    {
+		struct compat_group_filter gsf;
+		int err;
+
+		if (len < COMPAT_GROUP_FILTER_SIZE(0))
+			return -EINVAL;
+		if (copy_from_user(&gsf, optval, COMPAT_GROUP_FILTER_SIZE(0)))
+			return -EFAULT;
+		lock_sock(sk);
+		err = ip6_mc_compat_msfget(sk,
+					   &((struct sockaddr_in6 *)&gsf.gf_group)->sin6_addr,
+					   gsf.gf_interface, gsf.gsf_numsrc,
+					   (struct group_filter __user *)optval,
+					   optlen);
+		release_sock(sk);
+	    }
+	default:
+		err = do_ipv6_getsockopt(sk, level, optname, optval, optlen);
+	}
+	return err;
 }
 
 int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index b781192..bdfabbb 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -714,6 +714,54 @@ int ip6_mc_msfget(struct sock *sk,
 	return 0;
 }
 
+#ifdef CONFIG_COMPAT
+int ip6_mc_compat_msfget(struct sock *sk,
+			 struct in6_addr *group,
+			 u32 gf_interface, u32 gf_numsrc,
+			 struct compat_group_filter __user *optval,
+			 int __user *optlen)
+{
+	struct ip6_sf_socklist *psl;
+	int fmode;
+
+	fmode = __ip6_mc_msf_lookup(sk, group, gf_interface, &psl);
+	if (fmode < 0)
+		return fmode;
+
+	if (psl) {
+		int count = psl ? psl->sl_count : 0;
+		int i, copycount;
+		struct sockaddr_in6 sin6;
+
+		copycount = count < gf_numsrc ? count : gf_numsrc;
+
+		if (put_user(COMPAT_GROUP_FILTER_SIZE(copycount), optlen) ||
+		    access_ok(VERIFY_WRITE, optval, COMPAT_GROUP_FILTER_SIZE(copycount)) ||
+		    __put_user((u32)fmode, &optval->gf_fmode) ||
+		    __put_user((u32)count, &optval->gf_numsrc))
+			return -EFAULT;
+
+		/* changes to psl require the socket lock, a read lock on
+		 * on ipv6_sk_mc_lock and a write lock on pmc->sflock. We
+		 * have the socket lock, so reading here is safe.
+		 */
+		memset(&sin6, 0, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+
+		for (i = 0; i < copycount; i++) {
+			struct sockaddr_in6 __user *usin6 = (void __user *)&optval->gf_slist[i];
+
+			ipv6_addr_copy(&sin6.sin6_addr, &psl->sl_addr[i]);
+
+			if (__copy_to_user(usin6, &sin6, sizeof(sin6)) ||
+			    __clear_user(usin6 + 1, sizeof(optval->gf_slist[i]) - sizeof(*usin6)))
+				return -EFAULT;
+		}
+	}
+	return 0;
+}
+#endif
+
 int inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
 		   const struct in6_addr *src_addr)
 {

---

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-24  8:02 [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-24  8:08 ` David Miller
  2008-04-24  8:16   ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-24 17:52 ` David Stevens
  1 sibling, 1 reply; 39+ messages in thread
From: David Miller @ 2008-04-24  8:08 UTC (permalink / raw)
  To: yoshfuji; +Cc: dlstevens, netdev

From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
Date: Thu, 24 Apr 2008 17:02:57 +0900 (JST)

> +#ifdef __KERNEL__
> +struct __compat_sockaddr_storage {
> +	unsigned short  ss_family;
> +	char		__data[_K_SS_MAXSIZE - sizeof(unsigned short)];
> +} __attribute__ ((aligned(4)));
> +#endif

Should include <linux/compat.h>, use compat types, and be guarded
by CONFIG_COMPAT.  This goes for all such structures.

>     [IPV6] COMPAT: group_filter{}-getsockopt compat layer.
>     
>     Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

Where is ip6_mc_compat_msfget() header file declaration?

This following call casts the 5th argument to "struct group_filter"
but the argument type in the implementation is...

> +int ip6_mc_compat_msfget(struct sock *sk,
> +			 struct in6_addr *group,
> +			 u32 gf_interface, u32 gf_numsrc,
> +			 struct compat_group_filter __user *optval,
> +			 int __user *optlen)

"struct compat_group_filter".

You didn't catch this because you forgot to add the extern
prototype to the header file.  Actually, I wonder if you test
built this at all, as gcc should have warned about the lack of
extern declaration when compiling ipv6_sockglue.c

Thanks for doing this work, please make corrections and I also
look forward to your ipv4 side fixes.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-24  8:08 ` David Miller
@ 2008-04-24  8:16   ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-24  8:49     ` David Miller
  2008-04-24 18:46     ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 2 replies; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-24  8:16 UTC (permalink / raw)
  To: davem; +Cc: dlstevens, netdev

In article <20080424.010845.257071066.davem@davemloft.net> (at Thu, 24 Apr 2008 01:08:45 -0700 (PDT)), David Miller <davem@davemloft.net> says:

> From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
> Date: Thu, 24 Apr 2008 17:02:57 +0900 (JST)
> 
> > +#ifdef __KERNEL__
> > +struct __compat_sockaddr_storage {
> > +	unsigned short  ss_family;
> > +	char		__data[_K_SS_MAXSIZE - sizeof(unsigned short)];
> > +} __attribute__ ((aligned(4)));
> > +#endif
> 
> Should include <linux/compat.h>, use compat types, and be guarded
> by CONFIG_COMPAT.  This goes for all such structures.

Do you mean, something like this?

#ifdef __KERNEL__
#include <linux/compat.h>

#ifdef CONFIG_COMPAT
struct __compat_sockaddr_storage {
   unsigned short  ss_family;
  char            __data[_K_SS_MAXSIZE - sizeof(unsigned short)];
 __attribute__ ((aligned(alignof(compat_uptr_t))));
#endif
#endif

> Where is ip6_mc_compat_msfget() header file declaration?
> 
> This following call casts the 5th argument to "struct group_filter"
> but the argument type in the implementation is...

I'll fix...

> Thanks for doing this work, please make corrections and I also
> look forward to your ipv4 side fixes.

Will do.

--yoshfuji

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-24  8:16   ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-24  8:49     ` David Miller
  2008-04-24  9:28       ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-24 18:46     ` YOSHIFUJI Hideaki / 吉藤英明
  1 sibling, 1 reply; 39+ messages in thread
From: David Miller @ 2008-04-24  8:49 UTC (permalink / raw)
  To: yoshfuji; +Cc: dlstevens, netdev

From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
Date: Thu, 24 Apr 2008 17:16:44 +0900 (JST)

> In article <20080424.010845.257071066.davem@davemloft.net> (at Thu, 24 Apr 2008 01:08:45 -0700 (PDT)), David Miller <davem@davemloft.net> says:
> 
> > From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
> > Date: Thu, 24 Apr 2008 17:02:57 +0900 (JST)
> > 
> > > +#ifdef __KERNEL__
> > > +struct __compat_sockaddr_storage {
> > > +	unsigned short  ss_family;
> > > +	char		__data[_K_SS_MAXSIZE - sizeof(unsigned short)];
> > > +} __attribute__ ((aligned(4)));
> > > +#endif
> > 
> > Should include <linux/compat.h>, use compat types, and be guarded
> > by CONFIG_COMPAT.  This goes for all such structures.
> 
> Do you mean, something like this?
> 
> #ifdef __KERNEL__
> #include <linux/compat.h>
> 
> #ifdef CONFIG_COMPAT
> struct __compat_sockaddr_storage {
>    unsigned short  ss_family;
>   char            __data[_K_SS_MAXSIZE - sizeof(unsigned short)];
>  __attribute__ ((aligned(alignof(compat_uptr_t))));
> #endif
> #endif

I think first of all that ((packed)) is a better attribute to use than
the explicit alignment.

I thought we had a compat_ushort_t, but we don't.  So I guess you
can avoid the compat.h include, but you do need the CONFIG_COMPAT
guard.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-24  8:49     ` David Miller
@ 2008-04-24  9:28       ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-24  9:35         ` David Miller
  0 siblings, 1 reply; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-24  9:28 UTC (permalink / raw)
  To: davem; +Cc: dlstevens, netdev, yoshfuji

In article <20080424.014956.94335109.davem@davemloft.net> (at Thu, 24 Apr 2008 01:49:56 -0700 (PDT)), David Miller <davem@davemloft.net> says:

> From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
> Date: Thu, 24 Apr 2008 17:16:44 +0900 (JST)
> 
> > In article <20080424.010845.257071066.davem@davemloft.net> (at Thu, 24 Apr 2008 01:08:45 -0700 (PDT)), David Miller <davem@davemloft.net> says:
> > 
> > > From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
> > > Date: Thu, 24 Apr 2008 17:02:57 +0900 (JST)
> > > 
> > > > +#ifdef __KERNEL__
> > > > +struct __compat_sockaddr_storage {
> > > > +	unsigned short  ss_family;
> > > > +	char		__data[_K_SS_MAXSIZE - sizeof(unsigned short)];
> > > > +} __attribute__ ((aligned(4)));
> > > > +#endif
> > > 
> > > Should include <linux/compat.h>, use compat types, and be guarded
> > > by CONFIG_COMPAT.  This goes for all such structures.
> > 
> > Do you mean, something like this?
> > 
> > #ifdef __KERNEL__
> > #include <linux/compat.h>
> > 
> > #ifdef CONFIG_COMPAT
> > struct __compat_sockaddr_storage {
> >    unsigned short  ss_family;
> >   char            __data[_K_SS_MAXSIZE - sizeof(unsigned short)];
> >  __attribute__ ((aligned(alignof(compat_uptr_t))));
> > #endif
> > #endif
> 
> I think first of all that ((packed)) is a better attribute to use than
> the explicit alignment.

Well, I don't think so.

Most (if not all) compat_XXX{} do not have such attribute,
and sockaddr_storage{} shuould actually be aligned on 32bit on
32bit archs, so I think we should declare, no?

For example, if we have
    struct foobar {
        char a;
        struct sockaddr_storage b;
    };
Then, offsetof(struct foobar, b) should be 4, not 1 nor 2.
(Of course all other fields of group_xxx structures are of
u32 here, so it is safe so far...)

Or, are you worrying about alignment of short?

--yoshfuji

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-24  9:28       ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-24  9:35         ` David Miller
  0 siblings, 0 replies; 39+ messages in thread
From: David Miller @ 2008-04-24  9:35 UTC (permalink / raw)
  To: yoshfuji; +Cc: dlstevens, netdev

From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
Date: Thu, 24 Apr 2008 18:28:34 +0900 (JST)

> Most (if not all) compat_XXX{} do not have such attribute,
> and sockaddr_storage{} shuould actually be aligned on 32bit on
> 32bit archs, so I think we should declare, no?
> 
> For example, if we have
>     struct foobar {
>         char a;
>         struct sockaddr_storage b;
>     };
> Then, offsetof(struct foobar, b) should be 4, not 1 nor 2.
> (Of course all other fields of group_xxx structures are of
> u32 here, so it is safe so far...)

You're right, it seems the aligned(4) is the best way.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-24  8:02 [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels YOSHIFUJI Hideaki / 吉藤英明
  2008-04-24  8:08 ` David Miller
@ 2008-04-24 17:52 ` David Stevens
  2008-04-25  5:38   ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-25  6:31   ` David Miller
  1 sibling, 2 replies; 39+ messages in thread
From: David Stevens @ 2008-04-24 17:52 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: davem, netdev, netdev-owner, yoshfuji

[-- Attachment #1: Type: text/plain, Size: 6771 bytes --]

I was looking at this problem too, and below is what I have at the
moment. I've only tested the group_req portion, and I was looking
at putting this in net/compat.c with hooks back, since the code is
literally identical for IPv4 except the sockopt function you call at
the end, but I've moved it to ipv6_sockglue.c for this patch.

This touches a lot less code and I think comes out a lot smaller.

Method is to translate to padded version on the user stack, make
the call, and translate back (in the MSFILTER case) on return, as
is done for some other compat functions.

                                        +-DLS

inline for viewing, attached for applying, and no signed-off line
since I have completed it; just for discussion.

--- linux-2.6.18.ppc64/net/ipv6/ipv6_sockglue.c 2008-04-21 
13:51:02.000000000 -0700
+++ linux-2.6.18.ppc64DLS1/net/ipv6/ipv6_sockglue.c     2008-04-24 
02:20:23.000000000 -0700
@@ -39,6 +39,7 @@
 #include <linux/init.h>
 #include <linux/sysctl.h>
 #include <linux/netfilter.h>
+#include <linux/compat.h>
 
 #include <net/sock.h>
 #include <net/snmp.h>
@@ -754,6 +755,27 @@ int ipv6_setsockopt(struct sock *sk, int
 
 
 #ifdef CONFIG_COMPAT
+
+struct compat_group_req {
+       __u32                                   gr_interface;
+       struct __kernel_sockaddr_storage        gr_group;
+} __attribute__ ((packed));
+
+struct compat_group_source_req {
+       __u32                                   gsr_interface;
+       struct __kernel_sockaddr_storage        gsr_group;
+       struct __kernel_sockaddr_storage        gsr_source;
+} __attribute__ ((packed));
+
+struct compat_group_filter {
+       __u32                                   gf_interface;
+       struct __kernel_sockaddr_storage        gf_group;
+       __u32                                   gf_fmode;
+       __u32                                   gf_numsrc;
+       struct __kernel_sockaddr_storage        gf_slist[1];
+} __attribute__ ((packed));
+
+
 int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
                           char __user *optval, int optlen)
 {
@@ -769,6 +791,95 @@ int compat_ipv6_setsockopt(struct sock *
        if (level != SOL_IPV6)
                return -ENOPROTOOPT;
 
+       switch (optname) {
+       case MCAST_JOIN_GROUP:
+       case MCAST_LEAVE_GROUP:
+       {
+               struct compat_group_req __user *gr32 = (void *)optval;
+               struct group_req __user *kgr =
+                       compat_alloc_user_space(sizeof(struct group_req));
+               u32 interface;
+
+               if (!access_ok(VERIFY_READ, gr32, sizeof(*gr32)) ||
+                   !access_ok(VERIFY_WRITE, kgr, sizeof(struct 
group_req)) ||
+                   __get_user(interface, &gr32->gr_interface) ||
+                   __put_user(interface, &kgr->gr_interface) ||
+                   __copy_tofrom_user(&kgr->gr_group, &gr32->gr_group,
+                               sizeof(kgr->gr_group)))
+                       return -EFAULT;
+               return do_ipv6_setsockopt(sk, level, optname,
+                       (char __user *)kgr, sizeof(struct group_req));
+       }
+       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 
*)optval;
+               struct group_source_req *kgsr = compat_alloc_user_space(
+                       sizeof(struct group_source_req));
+               u32 interface;
+
+               if (!access_ok(VERIFY_READ, gsr32, sizeof(*gsr32)) ||
+                   !access_ok(VERIFY_WRITE, kgsr,
+                       sizeof(struct group_source_req)) ||
+                   __get_user(interface, &gsr32->gsr_interface) ||
+                   __put_user(interface, &kgsr->gsr_interface) ||
+                   __copy_tofrom_user(&kgsr->gsr_group, 
&gsr32->gsr_group,
+                               sizeof(kgsr->gsr_group)) ||
+                   __copy_tofrom_user(&kgsr->gsr_source, 
&gsr32->gsr_source,
+                               sizeof(kgsr->gsr_source)))
+                       return -EFAULT;
+               return do_ipv6_setsockopt(sk, level, optname,
+                       (char __user *)kgsr, sizeof(struct 
group_source_req));
+       }
+       case MCAST_MSFILTER:
+       {
+               struct compat_group_filter __user *gf32 = (void *)optval;
+               struct group_filter *kgf;
+               u32 interface, fmode, numsrc;
+               int len;
+
+               if (!access_ok(VERIFY_READ, gf32, sizeof(*gf32)) ||
+                   __get_user(interface, &gf32->gf_interface) ||
+                   __get_user(fmode, &gf32->gf_fmode) ||
+                   __get_user(numsrc, &gf32->gf_numsrc))
+                       return -EFAULT;
+               if (optlen < GROUP_FILTER_SIZE(0))
+                       return -EINVAL;
+               len = GROUP_FILTER_SIZE(numsrc);
+               len = len < optlen ? len : optlen;
+               kgf = compat_alloc_user_space(len);
+               if (!access_ok(VERIFY_WRITE, kgf, len) ||
+                   __put_user(interface, &kgf->gf_interface) ||
+                   __put_user(fmode, &kgf->gf_fmode) ||
+                   __put_user(numsrc, &kgf->gf_numsrc) ||
+                   __copy_tofrom_user(&kgf->gf_group, &gf32->gf_group,
+                               sizeof(kgf->gf_group)) ||
+                   (numsrc && __copy_tofrom_user(&kgf->gf_slist,
+                               &gf32->gf_slist, len - 
GROUP_FILTER_SIZE(0))))
+                       return -EFAULT;
+               err = do_ipv6_setsockopt(sk, level, optname,
+                       (char __user *)kgf, len);
+               if (!access_ok(VERIFY_WRITE, gf32, sizeof(*gf32)) ||
+                   __get_user(interface, &kgf->gf_interface) ||
+                   __put_user(interface, &gf32->gf_interface) ||
+                   __get_user(fmode, &kgf->gf_fmode) ||
+                   __put_user(fmode, &gf32->gf_fmode) ||
+                   __get_user(numsrc, &kgf->gf_numsrc) ||
+                   __put_user(numsrc, &gf32->gf_numsrc) ||
+                   __copy_tofrom_user(&gf32->gf_group, &kgf->gf_group,
+                               sizeof(gf32->gf_group)) ||
+                   (numsrc && __copy_tofrom_user(&gf32->gf_slist,
+                               &kgf->gf_slist, len - 
GROUP_FILTER_SIZE(0))))
+                       return -EFAULT;
+               return err;
+       }
+
+       default:
+               break;
+       }
+
        err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
        /* we need to exclude all possible ENOPROTOOPTs except default 
case */


[-- Attachment #2: mc_compat1.patch --]
[-- Type: application/octet-stream, Size: 4564 bytes --]

--- linux-2.6.18.ppc64/net/ipv6/ipv6_sockglue.c	2008-04-21 13:51:02.000000000 -0700
+++ linux-2.6.18.ppc64DLS1/net/ipv6/ipv6_sockglue.c	2008-04-24 02:20:23.000000000 -0700
@@ -39,6 +39,7 @@
 #include <linux/init.h>
 #include <linux/sysctl.h>
 #include <linux/netfilter.h>
+#include <linux/compat.h>
 
 #include <net/sock.h>
 #include <net/snmp.h>
@@ -754,6 +755,27 @@ int ipv6_setsockopt(struct sock *sk, int
 
 
 #ifdef CONFIG_COMPAT
+
+struct compat_group_req {
+	__u32					gr_interface;
+	struct __kernel_sockaddr_storage	gr_group;
+} __attribute__ ((packed));
+
+struct compat_group_source_req {
+	__u32					gsr_interface;
+	struct __kernel_sockaddr_storage	gsr_group;
+	struct __kernel_sockaddr_storage	gsr_source;
+} __attribute__ ((packed));
+
+struct compat_group_filter {
+	__u32					gf_interface;
+	struct __kernel_sockaddr_storage	gf_group;
+	__u32					gf_fmode;
+	__u32					gf_numsrc;
+	struct __kernel_sockaddr_storage	gf_slist[1];
+} __attribute__ ((packed));
+
+
 int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
 			   char __user *optval, int optlen)
 {
@@ -769,6 +791,95 @@ int compat_ipv6_setsockopt(struct sock *
 	if (level != SOL_IPV6)
 		return -ENOPROTOOPT;
 
+	switch (optname) {
+	case MCAST_JOIN_GROUP:
+	case MCAST_LEAVE_GROUP:
+	{
+		struct compat_group_req __user *gr32 = (void *)optval;
+		struct group_req __user *kgr =
+			compat_alloc_user_space(sizeof(struct group_req));
+		u32 interface;
+
+		if (!access_ok(VERIFY_READ, gr32, sizeof(*gr32)) ||
+		    !access_ok(VERIFY_WRITE, kgr, sizeof(struct group_req)) ||
+		    __get_user(interface, &gr32->gr_interface) ||
+		    __put_user(interface, &kgr->gr_interface) ||
+		    __copy_tofrom_user(&kgr->gr_group, &gr32->gr_group,
+				sizeof(kgr->gr_group)))
+			return -EFAULT;
+		return do_ipv6_setsockopt(sk, level, optname,
+			(char __user *)kgr, sizeof(struct group_req));
+	}
+	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 *)optval;
+		struct group_source_req *kgsr = compat_alloc_user_space(
+			sizeof(struct group_source_req));
+		u32 interface;
+
+		if (!access_ok(VERIFY_READ, gsr32, sizeof(*gsr32)) ||
+		    !access_ok(VERIFY_WRITE, kgsr,
+			sizeof(struct group_source_req)) ||
+		    __get_user(interface, &gsr32->gsr_interface) ||
+		    __put_user(interface, &kgsr->gsr_interface) ||
+		    __copy_tofrom_user(&kgsr->gsr_group, &gsr32->gsr_group,
+				sizeof(kgsr->gsr_group)) ||
+		    __copy_tofrom_user(&kgsr->gsr_source, &gsr32->gsr_source,
+				sizeof(kgsr->gsr_source)))
+			return -EFAULT;
+		return do_ipv6_setsockopt(sk, level, optname,
+			(char __user *)kgsr, sizeof(struct group_source_req));
+	}
+	case MCAST_MSFILTER:
+	{
+		struct compat_group_filter __user *gf32 = (void *)optval;
+		struct group_filter *kgf;
+		u32 interface, fmode, numsrc;
+		int len;
+
+		if (!access_ok(VERIFY_READ, gf32, sizeof(*gf32)) ||
+		    __get_user(interface, &gf32->gf_interface) ||
+		    __get_user(fmode, &gf32->gf_fmode) ||
+		    __get_user(numsrc, &gf32->gf_numsrc))
+			return -EFAULT;
+		if (optlen < GROUP_FILTER_SIZE(0))
+			return -EINVAL;
+		len = GROUP_FILTER_SIZE(numsrc);
+		len = len < optlen ? len : optlen;
+		kgf = compat_alloc_user_space(len);
+		if (!access_ok(VERIFY_WRITE, kgf, len) ||
+		    __put_user(interface, &kgf->gf_interface) ||
+		    __put_user(fmode, &kgf->gf_fmode) ||
+		    __put_user(numsrc, &kgf->gf_numsrc) ||
+		    __copy_tofrom_user(&kgf->gf_group, &gf32->gf_group,
+				sizeof(kgf->gf_group)) ||
+		    (numsrc && __copy_tofrom_user(&kgf->gf_slist,
+				&gf32->gf_slist, len - GROUP_FILTER_SIZE(0))))
+			return -EFAULT;
+		err = do_ipv6_setsockopt(sk, level, optname,
+			(char __user *)kgf, len);
+		if (!access_ok(VERIFY_WRITE, gf32, sizeof(*gf32)) ||
+		    __get_user(interface, &kgf->gf_interface) ||
+		    __put_user(interface, &gf32->gf_interface) ||
+		    __get_user(fmode, &kgf->gf_fmode) ||
+		    __put_user(fmode, &gf32->gf_fmode) ||
+		    __get_user(numsrc, &kgf->gf_numsrc) ||
+		    __put_user(numsrc, &gf32->gf_numsrc) ||
+		    __copy_tofrom_user(&gf32->gf_group, &kgf->gf_group,
+				sizeof(gf32->gf_group)) ||
+		    (numsrc && __copy_tofrom_user(&gf32->gf_slist,
+				&kgf->gf_slist, len - GROUP_FILTER_SIZE(0))))
+			return -EFAULT;
+		return err;
+	}
+
+	default:
+		break;
+	}
+
 	err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
 	/* we need to exclude all possible ENOPROTOOPTs except default case */

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-24  8:16   ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-24  8:49     ` David Miller
@ 2008-04-24 18:46     ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-24 20:01       ` David Stevens
  1 sibling, 1 reply; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-24 18:46 UTC (permalink / raw)
  To: davem; +Cc: dlstevens, netdev

In article <20080424.171644.106117727.yoshfuji@linux-ipv6.org> (at Thu, 24 Apr 2008 17:16:44 +0900 (JST)), YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org> says:

> > Thanks for doing this work, please make corrections and I also
> > look forward to your ipv4 side fixes.
> 
> Will do.

Okay, here it is.

---
[GIT PULL] [IPV4,IPV6] Compat layer for IGMP/MLD (Take 2)

Many MCAST_xxx (IGMP/MLD) socket options did not work
appropriately for 32bit applications on 64bit kernel
because of the difference of alignment requirements between 
them
With this series of changes, compat layer is being added for them.

Tree is available at
    git://git.linux-ipv6.org/gitroot/yoshfuji/linux-2.6-dev.git net-2.6-gd7d31300/inet6/05_compat-20080424a

HEADLINES
---------

    [NET]: Introduce __compat_sockaddr_storage{}.
    [IPV6] COMPAT: Introduce compat_do_ipv6_{set,get}sockopt() wrapper.
    [IPV6] MCAST: Move mode check for group_source_req{}-sockopt to net/ipv6/mcast.c.
    [IPV6] COMPAT: group_source_req{}-setsockopt compat layer.
    [IPV6] COMPAT: group_req{}-setsockopt compat layer.
    [IPV6] COMPAT: group_filter{}-setsockopt compat layer.
    [IPV6] MCAST: Split ip6_mc_msfget().
    [IPV6] MCAST: Make ip6_mc_msfget() more generic.
    [IPV6] COMPAT: group_filter{}-getsockopt compat layer.
    [IPV4] COMPAT: Introduce compat_do_ip_{set,get}sockopt() wrapper.
    [IPV4] IGMP: Move mode check for ip_mreq_source/group_source_req{}-sockopts to net/ipv4/igmp.c.
    [IPV4] COMPAT: group_source_req{}-setsockopt compat layer.
    [IPV4] COMPAT: group_req{}-setsockopt compat layer.
    [IPV4] COMPAT: group_filter{}-setsockopt compat layer.
    [IPV4] IGMP: Consolidate common code path of ip_mc_{msf,gsf}get().
    [IPV4] IGMP: Make ip_mc_gsfget() more generic.
    [IPV4] COMPAT: group_filter{}-getsockopt compat layer.

DIFFSTAT
--------

 include/linux/igmp.h     |   12 ++
 include/linux/in.h       |   29 +++++
 include/linux/socket.h   |   11 ++
 include/net/ipv6.h       |   16 ++-
 net/ipv4/igmp.c          |  259 +++++++++++++++++++++++++++++++---------------
 net/ipv4/ip_sockglue.c   |  200 ++++++++++++++++++++++++++----------
 net/ipv6/ipv6_sockglue.c |  186 +++++++++++++++++++++++++++------
 net/ipv6/mcast.c         |  194 ++++++++++++++++++++++++++--------
 8 files changed, 685 insertions(+), 222 deletions(-)

CHANGESETS
----------

commit 8d1070ce23288bfcc52835ef08bb0c7a9d45ad8c
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Fri Apr 25 02:56:32 2008 +0900

    [NET]: Introduce __compat_sockaddr_storage{}.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/linux/socket.h b/include/linux/socket.h
index bd2b30a..7c30c6b 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -16,6 +16,17 @@ struct __kernel_sockaddr_storage {
 				/* _SS_MAXSIZE value minus size of ss_family */
 } __attribute__ ((aligned(_K_SS_ALIGNSIZE)));	/* force desired alignment */
 
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+#define _K_CSS_ALIGNSIZE	4	/* __alignof__ (compat_uptr_t) */
+
+struct __compat_sockaddr_storage {
+	unsigned short  ss_family;
+	char		__data[_K_SS_MAXSIZE - sizeof(unsigned short)];
+} __attribute__ ((aligned(_K_CSS_ALIGNSIZE)));
+#endif
+#endif
+
 #if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
 
 #include <asm/socket.h>			/* arch-dependent defines	*/

---
commit b43bfbc68b2bb862c309615f1b5074009b453d3d
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Wed Apr 23 18:00:20 2008 +0900

    [IPV6] COMPAT: Introduce compat_do_ipv6_{set,get}sockopt() wrapper.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 06de9d0..a53aed4 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -764,6 +764,13 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
 EXPORT_SYMBOL(ipv6_setsockopt);
 
 #ifdef CONFIG_COMPAT
+static int
+compat_do_ipv6_setsockopt(struct sock *sk, int level, int optname,
+			  char __user *optval, int optlen)
+{
+	return do_ipv6_setsockopt(sk, level, optname, optval, optlen);
+}
+
 int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
 			   char __user *optval, int optlen)
 {
@@ -779,7 +786,7 @@ int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
 	if (level != SOL_IPV6)
 		return -ENOPROTOOPT;
 
-	err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
+	err = compat_do_ipv6_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
 	/* we need to exclude all possible ENOPROTOOPTs except default case */
 	if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY &&
@@ -1107,6 +1114,13 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
 EXPORT_SYMBOL(ipv6_getsockopt);
 
 #ifdef CONFIG_COMPAT
+static int
+compat_do_ipv6_getsockopt(struct sock *sk, int level, int optname,
+			  char __user *optval, int __user *optlen)
+{
+	return do_ipv6_getsockopt(sk, level, optname, optval, optlen);
+}
+
 int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
 			   char __user *optval, int __user *optlen)
 {
@@ -1122,7 +1136,7 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
 	if (level != SOL_IPV6)
 		return -ENOPROTOOPT;
 
-	err = do_ipv6_getsockopt(sk, level, optname, optval, optlen);
+	err = compat_do_ipv6_getsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
 	/* we need to exclude all possible ENOPROTOOPTs except default case */
 	if (err == -ENOPROTOOPT && optname != IPV6_2292PKTOPTIONS) {

---
commit afdf2703bd9423a62cf7d984bdc88273fa205033
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Tue Apr 22 12:14:20 2008 +0900

    [IPV6] MCAST: Move mode check for group_source_req{}-sockopt to net/ipv6/mcast.c.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 49c4898..aa81120 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -565,7 +565,7 @@ extern const struct proto_ops inet6_dgram_ops;
 struct group_source_req;
 struct group_filter;
 
-extern int ip6_mc_source(int add, int omode, struct sock *sk,
+extern int ip6_mc_source(struct sock *sk, int optname,
 			 struct group_source_req *pgsr);
 extern int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf);
 extern int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index a53aed4..694e031 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -542,7 +542,6 @@ done:
 	case MCAST_UNBLOCK_SOURCE:
 	{
 		struct group_source_req greqs;
-		int omode, add;
 
 		if (optlen < sizeof(struct group_source_req))
 			goto e_inval;
@@ -550,33 +549,7 @@ done:
 			retv = -EFAULT;
 			break;
 		}
-		if (greqs.gsr_group.ss_family != AF_INET6 ||
-		    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(sk, greqs.gsr_interface,
-				&psin6->sin6_addr);
-			/* 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);
+		retv = ip6_mc_source(sk, optname, &greqs);
 		break;
 	}
 	case MCAST_MSFILTER:
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 54f91ef..5d8d653 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -355,8 +355,8 @@ void ipv6_sock_mc_close(struct sock *sk)
 	write_unlock_bh(&ipv6_sk_mc_lock);
 }
 
-int ip6_mc_source(int add, int omode, struct sock *sk,
-	struct group_source_req *pgsr)
+static int __ip6_mc_source(int add, int omode, struct sock *sk,
+			   struct group_source_req *pgsr)
 {
 	struct in6_addr *source, *group;
 	struct ipv6_mc_socklist *pmc;
@@ -496,6 +496,40 @@ done:
 	return err;
 }
 
+int ip6_mc_source(struct sock *sk, int optname,
+		  struct group_source_req *pgsr)
+{
+	int omode, add, retv;
+
+	if (pgsr->gsr_group.ss_family != AF_INET6 ||
+	    pgsr->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;
+
+		psin6 = (struct sockaddr_in6 *)&pgsr->gsr_group;
+		retv = ipv6_sock_mc_join(sk, pgsr->gsr_interface,
+					 &psin6->sin6_addr);
+		/* 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, pgsr);
+}
+
 int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
 {
 	struct in6_addr *group;

---
commit d54a002064782edebf3544a6d509c4ed30a8a03b
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Thu Apr 24 17:28:31 2008 +0900

    [IPV6] COMPAT: group_source_req{}-setsockopt compat layer.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/linux/in.h b/include/linux/in.h
index 4065313..8554dee 100644
--- a/include/linux/in.h
+++ b/include/linux/in.h
@@ -169,6 +169,16 @@ struct group_filter
 	(sizeof(struct group_filter) - sizeof(struct __kernel_sockaddr_storage) \
 	+ (numsrc) * sizeof(struct __kernel_sockaddr_storage))
 
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+struct compat_group_source_req {
+	__u32					gsr_interface;
+	struct __compat_sockaddr_storage	gsr_group;
+	struct __compat_sockaddr_storage	gsr_source;
+};
+#endif
+#endif
+
 struct in_pktinfo
 {
 	int		ipi_ifindex;
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 694e031..b7ae360 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -741,7 +741,37 @@ static int
 compat_do_ipv6_setsockopt(struct sock *sk, int level, int optname,
 			  char __user *optval, int optlen)
 {
-	return do_ipv6_setsockopt(sk, level, optname, optval, optlen);
+	int retv;
+
+	switch (optname) {
+	case MCAST_JOIN_SOURCE_GROUP:
+	case MCAST_LEAVE_SOURCE_GROUP:
+	case MCAST_BLOCK_SOURCE:
+	case MCAST_UNBLOCK_SOURCE:
+	    {
+		struct group_source_req greqs;
+		struct compat_group_source_req __user *ugreqs = (void __user *)optval;
+
+		if (optlen < sizeof(*ugreqs)) {
+			retv = -EINVAL;
+			break;
+		}
+		if (access_ok(VERIFY_READ, optval, sizeof(*ugreqs)) ||
+		    __get_user(greqs.gsr_interface, &ugreqs->gsr_interface) ||
+		    __copy_from_user(&greqs.gsr_group, &ugreqs->gsr_group,
+				     sizeof(greqs.gsr_group)) ||
+		    __copy_from_user(&greqs.gsr_source, &ugreqs->gsr_source,
+				     sizeof(greqs.gsr_source))) {
+			retv = -EFAULT;
+			break;
+		}
+		retv = ip6_mc_source(sk, optname, &greqs);
+		break;
+	    }
+	default:
+		retv = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
+	}
+	return retv;
 }
 
 int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,

---
commit d9ba3ae81a4c11210bddaf9d9650323adb9e6d9f
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Thu Apr 24 17:30:15 2008 +0900

    [IPV6] COMPAT: group_req{}-setsockopt compat layer.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/linux/in.h b/include/linux/in.h
index 8554dee..2fbd7c7 100644
--- a/include/linux/in.h
+++ b/include/linux/in.h
@@ -176,6 +176,12 @@ struct compat_group_source_req {
 	struct __compat_sockaddr_storage	gsr_group;
 	struct __compat_sockaddr_storage	gsr_source;
 };
+
+struct compat_group_req
+{
+	__u32				 	gr_interface;
+	struct __compat_sockaddr_storage 	gr_group;
+};
 #endif
 #endif
 
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index b7ae360..30e6ca1 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -744,6 +744,32 @@ compat_do_ipv6_setsockopt(struct sock *sk, int level, int optname,
 	int retv;
 
 	switch (optname) {
+	case MCAST_JOIN_GROUP:
+	case MCAST_LEAVE_GROUP:
+	    {
+		struct group_req greq;
+		struct compat_group_req __user *ugreq = (void __user *)optval;
+		struct sockaddr_in6 *psin6;
+
+		if (optlen < sizeof(*ugreq))
+			return -EINVAL;
+		if (access_ok(VERIFY_READ, ugreq, sizeof(*ugreq)) ||
+		    __get_user(greq.gr_interface, &ugreq->gr_interface) ||
+		    __copy_from_user(&greq.gr_group, &ugreq->gr_group,
+				     sizeof(greq.gr_group)))
+			return -EFAULT;
+		if (greq.gr_group.ss_family != AF_INET6)
+			return -EADDRNOTAVAIL;
+
+		psin6 = (struct sockaddr_in6 *)&greq.gr_group;
+		if (optname == MCAST_JOIN_GROUP)
+			retv = ipv6_sock_mc_join(sk, greq.gr_interface,
+				&psin6->sin6_addr);
+		else
+			retv = ipv6_sock_mc_drop(sk, greq.gr_interface,
+				&psin6->sin6_addr);
+		break;
+	    }
 	case MCAST_JOIN_SOURCE_GROUP:
 	case MCAST_LEAVE_SOURCE_GROUP:
 	case MCAST_BLOCK_SOURCE:

---
commit 3c055f969f7f3716f6846037c2ec0fa06e0424b3
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Thu Apr 24 17:32:10 2008 +0900

    [IPV6] COMPAT: group_filter{}-setsockopt compat layer.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/linux/in.h b/include/linux/in.h
index 2fbd7c7..ccbfc76 100644
--- a/include/linux/in.h
+++ b/include/linux/in.h
@@ -182,6 +182,19 @@ struct compat_group_req
 	__u32				 	gr_interface;
 	struct __compat_sockaddr_storage 	gr_group;
 };
+
+struct compat_group_filter
+{
+	__u32					gf_interface;
+	struct __compat_sockaddr_storage	gf_group;
+	__u32					gf_fmode;
+	__u32					gf_numsrc;
+	struct __compat_sockaddr_storage	gf_slist[1];
+};
+
+#define COMPAT_GROUP_FILTER_SIZE(numsrc) \
+	(sizeof(struct compat_group_filter) - sizeof(struct __compat_sockaddr_storage) \
+	+ (numsrc) * sizeof(struct __compat_sockaddr_storage))
 #endif
 #endif
 
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 30e6ca1..0895e7d 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -794,6 +794,57 @@ compat_do_ipv6_setsockopt(struct sock *sk, int level, int optname,
 		retv = ip6_mc_source(sk, optname, &greqs);
 		break;
 	    }
+	case MCAST_MSFILTER:
+	    {
+		extern int sysctl_mld_max_msf;
+		struct compat_group_filter __user *ugsf = (void __user *)optval;
+		struct group_filter *gsf;
+		u32 gf_numsrc;
+		int i;
+
+		if (optlen < COMPAT_GROUP_FILTER_SIZE(0))
+			return -EINVAL;
+
+		if (access_ok(VERIFY_READ, ugsf, COMPAT_GROUP_FILTER_SIZE(0)) ||
+		    __get_user(gf_numsrc, &ugsf->gf_numsrc))
+			return -EFAULT;
+
+		if (COMPAT_GROUP_FILTER_SIZE(gf_numsrc) > optlen)
+			return -EINVAL;
+
+		if (optlen > sysctl_optmem_max ||
+		    gf_numsrc >= 0x1ffffffU ||
+		    gf_numsrc > sysctl_mld_max_msf)
+			return -ENOBUFS;
+
+		optlen = GROUP_FILTER_SIZE(gf_numsrc);
+		gsf = kmalloc(optlen, GFP_KERNEL);
+		if (!gsf) {
+			retv = -ENOBUFS;
+			break;
+		}
+		retv = -EFAULT;
+		if (__get_user(gsf->gf_interface, &ugsf->gf_interface) ||
+		    __copy_from_user(&gsf->gf_group, &ugsf->gf_group, sizeof(gsf->gf_group)) ||
+		    __get_user(gsf->gf_fmode, &ugsf->gf_fmode) ||
+		    access_ok(VERIFY_READ, ugsf->gf_slist,
+			      sizeof(struct __compat_sockaddr_storage) * gf_numsrc))
+			goto kfree_out;
+
+		gsf->gf_numsrc = gf_numsrc;
+
+		for (i = 0; i < gf_numsrc; i++) {
+			if (__copy_from_user(&gsf->gf_slist[i],
+					     &ugsf->gf_slist[i],
+					     sizeof(gsf->gf_slist[i])))
+				goto kfree_out;
+		}
+
+		retv = ip6_mc_msfilter(sk, gsf);
+kfree_out:
+		kfree(gsf);
+		break;
+	    }
 	default:
 		retv = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
 	}

---
commit 473fd7b42bc26e03847d85561f9afa1371bc99cc
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Tue Apr 22 19:16:33 2008 +0900

    [IPV6] MCAST: Split ip6_mc_msfget().
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 5d8d653..d32d6a3 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -621,79 +621,99 @@ done:
 	return err;
 }
 
-int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
-	struct group_filter __user *optval, int __user *optlen)
+int __ip6_mc_msf_lookup(struct sock *sk,
+			struct in6_addr *group, int interface,
+			struct ip6_sf_socklist **ppsl)
 {
-	int err, i, count, copycount;
-	struct in6_addr *group;
 	struct ipv6_mc_socklist *pmc;
-	struct inet6_dev *idev;
-	struct net_device *dev;
 	struct ipv6_pinfo *inet6 = inet6_sk(sk);
-	struct ip6_sf_socklist *psl;
+	struct net_device *dev;
+	struct inet6_dev *idev;
 	struct net *net = sock_net(sk);
+	int ret = 0;
 
-	group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
-
-	if (!ipv6_addr_is_multicast(group))
-		return -EINVAL;
-
-	idev = ip6_mc_find_dev(net, group, gsf->gf_interface);
+	if (!ipv6_addr_is_multicast(group)) {
+		ret = -EINVAL;
+		goto out;
+	}
 
-	if (!idev)
-		return -ENODEV;
+	idev = ip6_mc_find_dev(net, group, interface);
+	if (!idev) {
+		ret = -ENODEV;
+		goto out;
+	}
 
 	dev = idev->dev;
 
-	err = -EADDRNOTAVAIL;
+	ret = -EADDRNOTAVAIL;
 	/*
 	 * changes to the ipv6_mc_list require the socket lock and
 	 * a read lock on ip6_sk_mc_lock. We have the socket lock,
 	 * so reading the list is safe.
 	 */
 
-	for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) {
-		if (pmc->ifindex != gsf->gf_interface)
+	for (pmc = inet6->ipv6_mc_list; pmc; pmc = pmc->next) {
+		if (pmc->ifindex != interface)
 			continue;
 		if (ipv6_addr_equal(group, &pmc->addr))
 			break;
 	}
-	if (!pmc)		/* must have a prior join */
-		goto done;
-	gsf->gf_fmode = pmc->sfmode;
-	psl = pmc->sflist;
-	count = psl ? psl->sl_count : 0;
+	if (pmc) {
+		*ppsl = pmc->sflist;
+		ret = pmc->sfmode;
+	}
+
 	read_unlock_bh(&idev->lock);
 	in6_dev_put(idev);
 	dev_put(dev);
+out:
+	return ret;
+}
 
-	copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
-	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, a read lock on
-	 * on ipv6_sk_mc_lock and a write lock on pmc->sflock. We
-	 * have the socket lock, so reading here is safe.
-	 */
-	for (i=0; i<copycount; i++) {
-		struct sockaddr_in6 *psin6;
-		struct sockaddr_storage ss;
+int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
+	struct group_filter __user *optval, int __user *optlen)
+{
+	struct in6_addr *group;
+	struct ip6_sf_socklist *psl;
+	int fmode;
+
+	group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
+
+	fmode = __ip6_mc_msf_lookup(sk, group, gsf->gf_interface, &psl);
+	if (fmode < 0)
+		return fmode;
 
-		psin6 = (struct sockaddr_in6 *)&ss;
-		memset(&ss, 0, sizeof(ss));
-		psin6->sin6_family = AF_INET6;
-		psin6->sin6_addr = psl->sl_addr[i];
-		if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss)))
+	if (psl) {
+		int count = psl ? psl->sl_count : 0;
+		int i, copycount;
+		struct sockaddr_in6 sin6;
+
+		copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
+
+		if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
+		    access_ok(VERIFY_WRITE, optval, GROUP_FILTER_SIZE(copycount)) ||
+		    __put_user((u32)fmode, &optval->gf_fmode) ||
+		    __put_user((u32)count, &optval->gf_numsrc))
 			return -EFAULT;
+
+		/* changes to psl require the socket lock, a read lock on
+		 * on ipv6_sk_mc_lock and a write lock on pmc->sflock. We
+		 * have the socket lock, so reading here is safe.
+		 */
+		memset(&sin6, 0, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+
+		for (i = 0; i < copycount; i++) {
+			struct sockaddr_in6 __user *usin6 = (void __user *)&optval->gf_slist[i];
+
+			ipv6_addr_copy(&sin6.sin6_addr, &psl->sl_addr[i]);
+
+			if (__copy_to_user(usin6, &sin6, sizeof(sin6)) ||
+			    __clear_user(usin6 + 1, sizeof(optval->gf_slist[i]) - sizeof(*usin6)))
+				return -EFAULT;
+		}
 	}
 	return 0;
-done:
-	read_unlock_bh(&idev->lock);
-	in6_dev_put(idev);
-	dev_put(dev);
-	return err;
 }
 
 int inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,

---
commit e920bd82cbc6e0b83087513a25220e9379b24b49
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Wed Apr 23 18:03:14 2008 +0900

    [IPV6] MCAST: Make ip6_mc_msfget() more generic.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index aa81120..103272f 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -568,7 +568,9 @@ struct group_filter;
 extern int ip6_mc_source(struct sock *sk, int optname,
 			 struct group_source_req *pgsr);
 extern int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf);
-extern int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
+extern int ip6_mc_msfget(struct sock *sk,
+			 struct in6_addr *group, u32 gf_interface,
+			 u32 gf_srcnum,
 			 struct group_filter __user *optval,
 			 int __user *optlen);
 
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 0895e7d..d670078 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -949,8 +949,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
 		if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0)))
 			return -EFAULT;
 		lock_sock(sk);
-		err = ip6_mc_msfget(sk, &gsf,
-			(struct group_filter __user *)optval, optlen);
+		err = ip6_mc_msfget(sk,
+				    &((struct sockaddr_in6 *)&gsf.gf_group)->sin6_addr,
+				    gsf.gf_interface, gsf.gf_numsrc,
+				    (struct group_filter __user *)optval, optlen);
 		release_sock(sk);
 		return err;
 	}
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index d32d6a3..b781192 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -670,16 +670,14 @@ out:
 	return ret;
 }
 
-int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
-	struct group_filter __user *optval, int __user *optlen)
+int ip6_mc_msfget(struct sock *sk,
+		  struct in6_addr *group, u32 gf_interface, u32 gf_numsrc,
+		  struct group_filter __user *optval, int __user *optlen)
 {
-	struct in6_addr *group;
 	struct ip6_sf_socklist *psl;
 	int fmode;
 
-	group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
-
-	fmode = __ip6_mc_msf_lookup(sk, group, gsf->gf_interface, &psl);
+	fmode = __ip6_mc_msf_lookup(sk, group, gf_interface, &psl);
 	if (fmode < 0)
 		return fmode;
 
@@ -688,7 +686,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
 		int i, copycount;
 		struct sockaddr_in6 sin6;
 
-		copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
+		copycount = count < gf_numsrc ? count : gf_numsrc;
 
 		if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
 		    access_ok(VERIFY_WRITE, optval, GROUP_FILTER_SIZE(copycount)) ||

---
commit 9153c1a545f2366f57bf46bcd59d5b99d61016f7
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Thu Apr 24 17:51:30 2008 +0900

    [IPV6] COMPAT: group_filter{}-getsockopt compat layer.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 103272f..c98c722 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -574,6 +574,16 @@ extern int ip6_mc_msfget(struct sock *sk,
 			 struct group_filter __user *optval,
 			 int __user *optlen);
 
+#ifdef CONFIG_COMPAT
+struct compat_group_filter;
+
+int ip6_mc_compat_msfget(struct sock *sk,
+			 struct in6_addr *group,
+			 u32 gf_interface, u32 gf_numsrc,
+			 struct compat_group_filter __user *optval,
+			 int __user *optlen);
+#endif
+
 #ifdef CONFIG_PROC_FS
 extern int  ac6_proc_init(struct net *net);
 extern void ac6_proc_exit(struct net *net);
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index d670078..46bafee 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -1200,7 +1200,33 @@ static int
 compat_do_ipv6_getsockopt(struct sock *sk, int level, int optname,
 			  char __user *optval, int __user *optlen)
 {
-	return do_ipv6_getsockopt(sk, level, optname, optval, optlen);
+	int len, err;
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+
+	switch (optname) {
+	case MCAST_MSFILTER:
+	    {
+		struct compat_group_filter gsf;
+		int err;
+
+		if (len < COMPAT_GROUP_FILTER_SIZE(0))
+			return -EINVAL;
+		if (copy_from_user(&gsf, optval, COMPAT_GROUP_FILTER_SIZE(0)))
+			return -EFAULT;
+		lock_sock(sk);
+		err = ip6_mc_compat_msfget(sk,
+					   &((struct sockaddr_in6 *)&gsf.gf_group)->sin6_addr,
+					   gsf.gf_interface, gsf.gf_numsrc,
+					   (struct compat_group_filter __user *)optval,
+					   optlen);
+		release_sock(sk);
+	    }
+	default:
+		err = do_ipv6_getsockopt(sk, level, optname, optval, optlen);
+	}
+	return err;
 }
 
 int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index b781192..bdfabbb 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -714,6 +714,54 @@ int ip6_mc_msfget(struct sock *sk,
 	return 0;
 }
 
+#ifdef CONFIG_COMPAT
+int ip6_mc_compat_msfget(struct sock *sk,
+			 struct in6_addr *group,
+			 u32 gf_interface, u32 gf_numsrc,
+			 struct compat_group_filter __user *optval,
+			 int __user *optlen)
+{
+	struct ip6_sf_socklist *psl;
+	int fmode;
+
+	fmode = __ip6_mc_msf_lookup(sk, group, gf_interface, &psl);
+	if (fmode < 0)
+		return fmode;
+
+	if (psl) {
+		int count = psl ? psl->sl_count : 0;
+		int i, copycount;
+		struct sockaddr_in6 sin6;
+
+		copycount = count < gf_numsrc ? count : gf_numsrc;
+
+		if (put_user(COMPAT_GROUP_FILTER_SIZE(copycount), optlen) ||
+		    access_ok(VERIFY_WRITE, optval, COMPAT_GROUP_FILTER_SIZE(copycount)) ||
+		    __put_user((u32)fmode, &optval->gf_fmode) ||
+		    __put_user((u32)count, &optval->gf_numsrc))
+			return -EFAULT;
+
+		/* changes to psl require the socket lock, a read lock on
+		 * on ipv6_sk_mc_lock and a write lock on pmc->sflock. We
+		 * have the socket lock, so reading here is safe.
+		 */
+		memset(&sin6, 0, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+
+		for (i = 0; i < copycount; i++) {
+			struct sockaddr_in6 __user *usin6 = (void __user *)&optval->gf_slist[i];
+
+			ipv6_addr_copy(&sin6.sin6_addr, &psl->sl_addr[i]);
+
+			if (__copy_to_user(usin6, &sin6, sizeof(sin6)) ||
+			    __clear_user(usin6 + 1, sizeof(optval->gf_slist[i]) - sizeof(*usin6)))
+				return -EFAULT;
+		}
+	}
+	return 0;
+}
+#endif
+
 int inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
 		   const struct in6_addr *src_addr)
 {

---
commit f0e116fa05474aa7a8cbf9d25a534ab6cbf67977
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Thu Apr 24 18:49:23 2008 +0900

    [IPV4] COMPAT: Introduce compat_do_ip_{set,get}sockopt() wrapper.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index d8adfd4..2ba4fac 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -915,6 +915,13 @@ int ip_setsockopt(struct sock *sk, int level,
 }
 
 #ifdef CONFIG_COMPAT
+static int
+compat_do_ip_setsockopt(struct sock *sk, int level, int optname,
+			char __user *optval, int optlen)
+{
+	return do_ip_setsockopt(sk, level, optname, optval, optlen);
+}
+
 int compat_ip_setsockopt(struct sock *sk, int level, int optname,
 			 char __user *optval, int optlen)
 {
@@ -923,7 +930,7 @@ int compat_ip_setsockopt(struct sock *sk, int level, int optname,
 	if (level != SOL_IP)
 		return -ENOPROTOOPT;
 
-	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
+	err = compat_do_ip_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
 	/* we need to exclude all possible ENOPROTOOPTs except default case */
 	if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
@@ -1178,10 +1185,17 @@ int ip_getsockopt(struct sock *sk, int level,
 }
 
 #ifdef CONFIG_COMPAT
+static int
+compat_do_ip_getsockopt(struct sock *sk, int level, int optname,
+			char __user *optval, int __user *optlen)
+{
+	return do_ip_getsockopt(sk, level, optname, optval, optlen);
+}
+
 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 = compat_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 &&

---
commit e3291172d59ea751d9e08233215d6f8396d76989
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Thu Apr 24 21:29:13 2008 +0900

    [IPV4] IGMP: Move mode check for ip_mreq_source/group_source_req{}-sockopts to net/ipv4/igmp.c.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index f5a1a0d..f240284 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -220,8 +220,8 @@ extern int igmp_rcv(struct sk_buff *);
 extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr);
 extern int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr);
 extern void ip_mc_drop_socket(struct sock *sk);
-extern int ip_mc_source(int add, int omode, struct sock *sk,
-		struct ip_mreq_source *mreqs, int ifindex);
+extern int ip_mc_source(struct sock *sk, int optname,
+			struct ip_mreq_source *pmreqs, int ifindex);
 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,
 		struct ip_msfilter __user *optval, int __user *optlen);
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 6250f42..c7c2994 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -1865,8 +1865,8 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
 	return ret;
 }
 
-int ip_mc_source(int add, int omode, struct sock *sk, struct
-	ip_mreq_source *mreqs, int ifindex)
+static int __ip_mc_source(int add, int omode, struct sock *sk,
+			  struct ip_mreq_source *mreqs, int ifindex)
 {
 	int err;
 	struct ip_mreqn imr;
@@ -2000,6 +2000,36 @@ done:
 	return err;
 }
 
+int ip_mc_source(struct sock *sk, int optname,
+		 struct ip_mreq_source *pmreqs, int ifindex)
+{
+	int omode, add;
+
+	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;
+		int err;
+
+		mreq.imr_multiaddr.s_addr = pmreqs->imr_multiaddr;
+		mreq.imr_address.s_addr = 0;
+		mreq.imr_ifindex = ifindex;
+		err = ip_mc_join_group(sk, &mreq);
+		if (err && err != -EADDRINUSE)
+			return err;
+		omode = MCAST_INCLUDE;
+		add = 1;
+	} else /* MCAST_LEAVE_SOURCE_GROUP */ {
+		omode = MCAST_INCLUDE;
+		add = 0;
+	}
+	return __ip_mc_source(add, omode, sk, pmreqs, ifindex);
+}
+
 int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
 {
 	int err = 0;
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 2ba4fac..5ebd668 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -684,7 +684,6 @@ static int do_ip_setsockopt(struct sock *sk, int level,
 	case IP_DROP_SOURCE_MEMBERSHIP:
 	{
 		struct ip_mreq_source mreqs;
-		int omode, add;
 
 		if (optlen != sizeof(struct ip_mreq_source))
 			goto e_inval;
@@ -692,28 +691,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
 			err = -EFAULT;
 			break;
 		}
-		if (optname == IP_BLOCK_SOURCE) {
-			omode = MCAST_EXCLUDE;
-			add = 1;
-		} else if (optname == IP_UNBLOCK_SOURCE) {
-			omode = MCAST_EXCLUDE;
-			add = 0;
-		} else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
-			struct ip_mreqn mreq;
-
-			mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
-			mreq.imr_address.s_addr = mreqs.imr_interface;
-			mreq.imr_ifindex = 0;
-			err = ip_mc_join_group(sk, &mreq);
-			if (err && err != -EADDRINUSE)
-				break;
-			omode = MCAST_INCLUDE;
-			add = 1;
-		} else /* IP_DROP_SOURCE_MEMBERSHIP */ {
-			omode = MCAST_INCLUDE;
-			add = 0;
-		}
-		err = ip_mc_source(add, omode, sk, &mreqs, 0);
+		err = ip_mc_source(sk, optname, &mreqs, 0);
 		break;
 	}
 	case MCAST_JOIN_GROUP:
@@ -749,7 +727,6 @@ static int do_ip_setsockopt(struct sock *sk, int level,
 		struct group_source_req greqs;
 		struct ip_mreq_source mreqs;
 		struct sockaddr_in *psin;
-		int omode, add;
 
 		if (optlen != sizeof(struct group_source_req))
 			goto e_inval;
@@ -768,31 +745,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
 		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(sk, &mreq);
-			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);
+		err = ip_mc_source(sk, optname, &mreqs, greqs.gsr_interface);
 		break;
 	}
 	case MCAST_MSFILTER:

---
commit 64c1bb9b0ba8ffb7ac6f666ae2cee08887ae49ee
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Thu Apr 24 21:49:30 2008 +0900

    [IPV4] COMPAT: group_source_req{}-setsockopt compat layer.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 5ebd668..40a43a8 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -872,7 +872,47 @@ static int
 compat_do_ip_setsockopt(struct sock *sk, int level, int optname,
 			char __user *optval, int optlen)
 {
-	return do_ip_setsockopt(sk, level, optname, optval, optlen);
+	int retv;
+
+	switch (optname) {
+	case MCAST_JOIN_SOURCE_GROUP:
+	case MCAST_LEAVE_SOURCE_GROUP:
+	case MCAST_BLOCK_SOURCE:
+	case MCAST_UNBLOCK_SOURCE:
+	    {
+		struct compat_group_source_req __user *ugreqs = (void __user *)optval;
+		struct sockaddr_in sin;
+		struct ip_mreq_source mreqs;
+		u32 interface;
+
+		if (optlen < sizeof(struct compat_group_source_req))
+			return -EINVAL;
+
+		if (access_ok(VERIFY_READ, optval, sizeof(*ugreqs)) ||
+		    __get_user(interface, &ugreqs->gsr_interface))
+			return -EFAULT;
+
+		if (__copy_from_user(&sin, &ugreqs->gsr_group, sizeof(sin)))
+			return -EFAULT;
+		if (sin.sin_family != AF_INET)
+			return -EADDRNOTAVAIL;
+		mreqs.imr_multiaddr = sin.sin_addr.s_addr;
+
+		if (__copy_from_user(&sin, &ugreqs->gsr_source, sizeof(sin)))
+			return -EFAULT;
+		if (sin.sin_family != AF_INET)
+			return -EADDRNOTAVAIL;
+		mreqs.imr_sourceaddr = sin.sin_addr.s_addr;
+
+		mreqs.imr_interface = 0; /* use index for mc_source */
+
+		retv = ip_mc_source(sk, optname, &mreqs, interface);
+		break;
+	    }
+	default:
+		retv = do_ip_setsockopt(sk, level, optname, optval, optlen);
+	}
+	return retv;
 }
 
 int compat_ip_setsockopt(struct sock *sk, int level, int optname,

---
commit 7413ae14041b6b4058d445c1b122d61305554869
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Thu Apr 24 21:56:41 2008 +0900

    [IPV4] COMPAT: group_req{}-setsockopt compat layer.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 40a43a8..ad2fe9b 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -875,6 +875,35 @@ compat_do_ip_setsockopt(struct sock *sk, int level, int optname,
 	int retv;
 
 	switch (optname) {
+	case MCAST_JOIN_GROUP:
+	case MCAST_LEAVE_GROUP:
+	    {
+		struct compat_group_req __user *ugreq = (void __user *)optval;
+		struct ip_mreqn mreq;
+		struct sockaddr_in sin;
+		u32 interface;
+
+		if (optlen < sizeof(struct compat_group_req))
+			return -EINVAL;
+
+		if (access_ok(VERIFY_READ, ugreq, sizeof(*ugreq)) ||
+		    __get_user(interface, &ugreq->gr_interface) ||
+		    __copy_from_user(&sin, &ugreq->gr_group, sizeof(sin)))
+			return -EFAULT;
+
+		if (sin.sin_family != AF_INET)
+			return -EINVAL;
+
+		mreq.imr_multiaddr = sin.sin_addr;
+		mreq.imr_ifindex = interface;
+
+		if (optname == MCAST_JOIN_GROUP)
+			retv = ip_mc_join_group(sk, &mreq);
+		else
+			retv = ip_mc_leave_group(sk, &mreq);
+		break;
+	    }
+
 	case MCAST_JOIN_SOURCE_GROUP:
 	case MCAST_LEAVE_SOURCE_GROUP:
 	case MCAST_BLOCK_SOURCE:

---
commit 6e4ebfa0808931db2e00626f2b67568f4eb94efe
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Fri Apr 25 01:27:13 2008 +0900

    [IPV4] COMPAT: group_filter{}-setsockopt compat layer.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index ad2fe9b..b92b750 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -938,6 +938,64 @@ compat_do_ip_setsockopt(struct sock *sk, int level, int optname,
 		retv = ip_mc_source(sk, optname, &mreqs, interface);
 		break;
 	    }
+	case MCAST_MSFILTER:
+	    {
+		extern int sysctl_igmp_max_msf;
+		struct compat_group_filter __user *ugsf = (void __user *)optval;
+		struct ip_msfilter *msf;
+		struct sockaddr_in sin;
+		u32 gf_interface, gf_numsrc;
+		int i;
+
+		if (optlen < COMPAT_GROUP_FILTER_SIZE(0))
+			return -EINVAL;
+		if (access_ok(VERIFY_READ, ugsf, COMPAT_GROUP_FILTER_SIZE(0)) ||
+		    __get_user(gf_numsrc, &ugsf->gf_numsrc))
+			return -EFAULT;
+
+		if (COMPAT_GROUP_FILTER_SIZE(gf_numsrc) > optlen)
+			retv = -EINVAL;
+
+		if (optlen > sysctl_optmem_max ||
+		    gf_numsrc >= 0x1ffffffU ||
+		    gf_numsrc > sysctl_igmp_max_msf)
+			return -ENOBUFS;
+
+		optlen = IP_MSFILTER_SIZE(gf_numsrc);
+		msf = kmalloc(optlen, GFP_KERNEL);
+		if (!msf)
+			return -ENOBUFS;
+
+		retv = -EFAULT;
+		if (__get_user(gf_interface, &ugsf->gf_interface) ||
+		    __copy_from_user(&sin, &ugsf->gf_group, sizeof(sin)) ||
+		    __get_user(msf->imsf_fmode, &ugsf->gf_fmode) ||
+		    access_ok(VERIFY_READ, ugsf->gf_slist,
+			      sizeof(struct __compat_sockaddr_storage) * gf_numsrc))
+			goto kfree_out;
+
+		if (sin.sin_family != AF_INET) {
+			retv = -EADDRNOTAVAIL;
+			goto kfree_out;
+		}
+
+		msf->imsf_multiaddr = sin.sin_addr.s_addr;
+		msf->imsf_interface = 0;
+		msf->imsf_numsrc = gf_numsrc;
+
+		for (i = 0; i < gf_numsrc; i++) {
+			if (__copy_from_user(&sin, &ugsf->gf_slist[i], sizeof(sin)))
+				retv = -EFAULT;
+			if (sin.sin_family != AF_INET) {
+				retv = -EADDRNOTAVAIL;
+				goto kfree_out;
+			}
+			msf->imsf_slist[i] = sin.sin_addr.s_addr;
+		}
+		retv = ip_mc_msfilter(sk, msf, gf_interface);
+kfree_out:
+		kfree(msf);
+	    }
 	default:
 		retv = do_ip_setsockopt(sk, level, optname, optval, optlen);
 	}

---
commit 38e600fc28a5309be4fe100fede207486106ed70
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Fri Apr 25 00:59:49 2008 +0900

    [IPV4] IGMP: Consolidate common code path of ip_mc_{msf,gsf}get().
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index c7c2994..adc0b03 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -2116,123 +2116,129 @@ done:
 	return err;
 }
 
-int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
-	struct ip_msfilter __user *optval, int __user *optlen)
+int __ip_mc_msf_lookup(struct sock *sk, __be32 group, int interface,
+		       struct ip_sf_socklist **ppsl)
 {
-	int err, len, count, copycount;
-	struct ip_mreqn	imr;
-	__be32 addr = msf->imsf_multiaddr;
-	struct ip_mc_socklist *pmc;
+	struct ip_mreqn imr;
 	struct in_device *in_dev;
+	struct ip_mc_socklist *pmc;
 	struct inet_sock *inet = inet_sk(sk);
-	struct ip_sf_socklist *psl;
-
-	if (!ipv4_is_multicast(addr))
-		return -EINVAL;
-
-	if (sock_net(sk) != &init_net)
-		return -EPROTONOSUPPORT;
+	int ret;
 
 	rtnl_lock();
 
-	imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
-	imr.imr_address.s_addr = msf->imsf_interface;
+	imr.imr_multiaddr.s_addr = group;
+	imr.imr_address.s_addr = interface;
 	imr.imr_ifindex = 0;
 	in_dev = ip_mc_find_dev(&imr);
 
 	if (!in_dev) {
-		err = -ENODEV;
-		goto done;
+		ret = -ENODEV;
+		goto out;
 	}
-	err = -EADDRNOTAVAIL;
 
+	ret = -EADDRNOTAVAIL;
 	for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
-		if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr &&
-		    pmc->multi.imr_ifindex == imr.imr_ifindex)
+		if (pmc->multi.imr_multiaddr.s_addr == group &&
+		    pmc->multi.imr_ifindex == interface)
 			break;
 	}
-	if (!pmc)		/* must have a prior join */
-		goto done;
-	msf->imsf_fmode = pmc->sfmode;
-	psl = pmc->sflist;
-	rtnl_unlock();
-	if (!psl) {
-		len = 0;
-		count = 0;
-	} else {
-		count = psl->sl_count;
-	}
-	copycount = count < msf->imsf_numsrc ? count : msf->imsf_numsrc;
-	len = copycount * sizeof(psl->sl_addr[0]);
-	msf->imsf_numsrc = count;
-	if (put_user(IP_MSFILTER_SIZE(copycount), optlen) ||
-	    copy_to_user(optval, msf, IP_MSFILTER_SIZE(0))) {
-		return -EFAULT;
-	}
-	if (len &&
-	    copy_to_user(&optval->imsf_slist[0], psl->sl_addr, len))
-		return -EFAULT;
+
+        if (pmc) {
+		*ppsl = pmc->sflist;
+		ret = pmc->sfmode;
+	}
+
+out:
+        rtnl_unlock();
+	return ret;
+}
+
+int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
+	struct ip_msfilter __user *optval, int __user *optlen)
+{
+	struct ip_sf_socklist *psl;
+	__be32 group = msf->imsf_multiaddr;
+	int fmode;
+
+	if (sock_net(sk) != &init_net)
+		return -EPROTONOSUPPORT;
+
+	if (!ipv4_is_multicast(group))
+		return -EINVAL;
+
+	fmode = __ip_mc_msf_lookup(sk, group, msf->imsf_interface,
+				   &psl);
+	if (fmode < 0)
+		return fmode;
+
+	if (psl) {
+		int count = psl->sl_count;
+		int copycount;
+
+		copycount = count < msf->imsf_numsrc ? count : msf->imsf_numsrc;
+
+		if (put_user(IP_MSFILTER_SIZE(copycount), optlen) ||
+		    access_ok(VERIFY_WRITE, optval, IP_MSFILTER_SIZE(copycount)) ||
+		    __put_user((u32)fmode, &optval->imsf_fmode) ||
+		    __put_user((u32)count, &optval->imsf_numsrc))
+			return -EFAULT;
+
+		if (copycount &&
+		    __copy_to_user(psl->sl_addr, optval->imsf_slist,
+				   sizeof(psl->sl_addr[0]) * copycount))
+			return -EFAULT;
+	}
 	return 0;
-done:
-	rtnl_unlock();
-	return err;
 }
 
 int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
 	struct group_filter __user *optval, int __user *optlen)
 {
-	int err, i, count, copycount;
-	struct sockaddr_in *psin;
-	__be32 addr;
-	struct ip_mc_socklist *pmc;
-	struct inet_sock *inet = inet_sk(sk);
+	struct sockaddr_in *gf_group = (struct sockaddr_in *)&gsf->gf_group;
 	struct ip_sf_socklist *psl;
+	int fmode;
+
+	if (sock_net(sk) != &init_net)
+		return -EPROTONOSUPPORT;
 
-	psin = (struct sockaddr_in *)&gsf->gf_group;
-	if (psin->sin_family != AF_INET)
+	if (gf_group->sin_family != AF_INET)
 		return -EINVAL;
-	addr = psin->sin_addr.s_addr;
-	if (!ipv4_is_multicast(addr))
+	if (!ipv4_is_multicast(gf_group->sin_addr.s_addr))
 		return -EINVAL;
 
-	if (sock_net(sk) != &init_net)
-		return -EPROTONOSUPPORT;
+	fmode = __ip_mc_msf_lookup(sk, gf_group->sin_addr.s_addr, gsf->gf_interface,
+				   &psl);
+	if (fmode < 0)
+		return fmode;
 
-	rtnl_lock();
+	if (psl) {
+		int count = psl->sl_count;
+		int i, copycount;
+		struct sockaddr_in sin;
 
-	err = -EADDRNOTAVAIL;
+		copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
 
-	for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
-		if (pmc->multi.imr_multiaddr.s_addr == addr &&
-		    pmc->multi.imr_ifindex == gsf->gf_interface)
-			break;
-	}
-	if (!pmc)		/* must have a prior join */
-		goto done;
-	gsf->gf_fmode = pmc->sfmode;
-	psl = pmc->sflist;
-	rtnl_unlock();
-	count = psl ? psl->sl_count : 0;
-	copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
-	gsf->gf_numsrc = count;
-	if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
-	    copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) {
-		return -EFAULT;
-	}
-	for (i=0; i<copycount; i++) {
-		struct sockaddr_storage ss;
-
-		psin = (struct sockaddr_in *)&ss;
-		memset(&ss, 0, sizeof(ss));
-		psin->sin_family = AF_INET;
-		psin->sin_addr.s_addr = psl->sl_addr[i];
-		if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss)))
+		if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
+		    access_ok(VERIFY_WRITE, optval, GROUP_FILTER_SIZE(copycount)) ||
+		    __put_user((u32)fmode, &optval->gf_fmode) ||
+		    __put_user((u32)count, &optval->gf_numsrc))
 			return -EFAULT;
+
+		memset(&sin, 0, sizeof(sin));
+		sin.sin_family = AF_INET;
+
+		for (i = 0; i < copycount; i++) {
+			struct sockaddr_in __user *usin = (void __user *)&optval->gf_slist[i];
+
+			sin.sin_addr.s_addr = psl->sl_addr[i];
+
+			if (__copy_to_user(usin, &sin, sizeof(sin)) ||
+			    __clear_user(usin + 1, sizeof(optval->gf_slist[i]) - sizeof(*usin)))
+				return -EFAULT;
+		}
 	}
 	return 0;
-done:
-	rtnl_unlock();
-	return err;
 }
 
 /*

---
commit 6cb6e044b1d3ead803053b482bd47a84a41b9a58
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Fri Apr 25 01:14:45 2008 +0900

    [IPV4] IGMP: Make ip_mc_gsfget() more generic.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index f240284..106d0d2 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -225,7 +225,8 @@ extern int ip_mc_source(struct sock *sk, int optname,
 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,
 		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 sockaddr_in *gsf_group, u32 gsf_interface, u32 gsf_numsrc,
 		struct group_filter __user *optval, int __user *optlen);
 extern int ip_mc_sf_allow(struct sock *sk, __be32 local, __be32 rmt, int dif);
 extern void ip_mr_init(void);
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index adc0b03..a2bfc6d 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -2192,10 +2192,10 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
 	return 0;
 }
 
-int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
+int ip_mc_gsfget(struct sock *sk,
+	struct sockaddr_in *gf_group, u32 gf_interface, u32 gf_numsrc,
 	struct group_filter __user *optval, int __user *optlen)
 {
-	struct sockaddr_in *gf_group = (struct sockaddr_in *)&gsf->gf_group;
 	struct ip_sf_socklist *psl;
 	int fmode;
 
@@ -2207,7 +2207,7 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
 	if (!ipv4_is_multicast(gf_group->sin_addr.s_addr))
 		return -EINVAL;
 
-	fmode = __ip_mc_msf_lookup(sk, gf_group->sin_addr.s_addr, gsf->gf_interface,
+	fmode = __ip_mc_msf_lookup(sk, gf_group->sin_addr.s_addr, gf_interface,
 				   &psl);
 	if (fmode < 0)
 		return fmode;
@@ -2217,7 +2217,7 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
 		int i, copycount;
 		struct sockaddr_in sin;
 
-		copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
+		copycount = count < gf_numsrc ? count : gf_numsrc;
 
 		if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
 		    access_ok(VERIFY_WRITE, optval, GROUP_FILTER_SIZE(copycount)) ||
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index b92b750..1e24209 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -1178,7 +1178,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
 			release_sock(sk);
 			return -EFAULT;
 		}
-		err = ip_mc_gsfget(sk, &gsf,
+		err = ip_mc_gsfget(sk,
+				   (struct sockaddr_in *)&gsf.gf_group,
+				   gsf.gf_interface, gsf.gf_numsrc,
 				   (struct group_filter __user *)optval, optlen);
 		release_sock(sk);
 		return err;

---
commit 22a5c1e1f922a15b03b7271eb9ce974833755721
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Fri Apr 25 01:41:44 2008 +0900

    [IPV4] COMPAT: group_filter{}-getsockopt compat layer.
    
    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index 106d0d2..6a927dc 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -228,6 +228,11 @@ extern int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
 extern int ip_mc_gsfget(struct sock *sk,
 		struct sockaddr_in *gsf_group, u32 gsf_interface, u32 gsf_numsrc,
 		struct group_filter __user *optval, int __user *optlen);
+#ifdef CONFIG_COMPAT
+extern int ip_mc_compat_gsfget(struct sock *sk,
+		struct sockaddr_in *gsf_group, u32 gsf_interface, u32 gsf_numsrc,
+		struct compat_group_filter __user *optval, int __user *optlen);
+#endif
 extern int ip_mc_sf_allow(struct sock *sk, __be32 local, __be32 rmt, int dif);
 extern void ip_mr_init(void);
 extern void ip_mc_init_dev(struct in_device *);
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index a2bfc6d..3e68dc6 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -2241,6 +2241,57 @@ int ip_mc_gsfget(struct sock *sk,
 	return 0;
 }
 
+#ifdef CONFIG_COMPAST
+int ip_mc_compat_gsfget(struct sock *sk,
+	struct sockaddr_in *gr_group, u32 gf_interface, u32 gf_numsrc,
+	struct compat_group_filter __user *optval, int __user *optlen)
+{
+	struct ip_sf_socklist *psl;
+	int fmode;
+
+	if (sock_net(sk) != &init_net)
+		return -EPROTONOSUPPORT;
+
+	if (gr_group->sin_family != AF_INET)
+		return -EINVAL;
+	if (!ipv4_is_multicast(gr_group->sin_addr.s_addr))
+		return -EINVAL;
+
+	fmode = __ip_mc_msf_lookup(sk, gr_group->sin_addr.s_addr, gf_interface,
+				   &psl);
+	if (fmode < 0)
+		return fmode;
+
+	if (psl) {
+		int count = psl->sl_count;
+		int i, copycount;
+		struct sockaddr_in sin;
+		
+		copycount = count < gf_numsrc ? count : gf_numsrc;
+
+		if (put_user(COMPAT_GROUP_FILTER_SIZE(copycount), optlen) ||
+		    access_ok(VERIFY_WRITE, optval, COMPAT_GROUP_FILTER_SIZE(copycount)) ||
+		    __put_user((u32)fmode, &optval->gf_fmode) ||
+		    __put_user((u32)count, &optval->gf_numsrc))
+			return -EFAULT;
+
+		memset(&sin, 0, sizeof(sin));
+		sin.sin_family = AF_INET;
+
+		for (i = 0; i < copycount; i++) {
+			struct sockaddr_in __user *usin = (void __user *)&optval->gf_slist[i];
+
+			sin.sin_addr.s_addr = psl->sl_addr[i];
+
+			if (__copy_to_user(usin, &sin, sizeof(sin)) ||
+			    __clear_user(usin + 1, sizeof(optval->gf_slist[i]) - sizeof(*usin)))
+				return -EFAULT;
+		}
+	}
+	return 0;
+}
+#endif
+
 /*
  * check if a multicast source filter allows delivery for a given <src,dst,intf>
  */

---

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-24 18:46     ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-24 20:01       ` David Stevens
  2008-04-24 20:43         ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 1 reply; 39+ messages in thread
From: David Stevens @ 2008-04-24 20:01 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明; +Cc: davem, netdev

[comments in-line, where there are no leading ">", with some deleted, but 
I hope
enough for context]

YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org> wrote on 04/24/2008 
11:46:04 AM:


> commit afdf2703bd9423a62cf7d984bdc88273fa205033
> Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> Date:   Tue Apr 22 12:14:20 2008 +0900
> 
>     [IPV6] MCAST: Move mode check for group_source_req{}-sockopt to 
net/ipv6/mcast.c.
> 
>     Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> 
> diff --git a/include/net/ipv6.h b/include/net/ipv6.h
> index 49c4898..aa81120 100644
> --- a/include/net/ipv6.h
> +++ b/include/net/ipv6.h
> @@ -565,7 +565,7 @@ extern const struct proto_ops inet6_dgram_ops;
>  struct group_source_req;
>  struct group_filter;
> 
> -extern int ip6_mc_source(int add, int omode, struct sock *sk,
> +extern int ip6_mc_source(struct sock *sk, int optname,
>            struct group_source_req *pgsr);
>  extern int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf);
>  extern int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
> diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
> index a53aed4..694e031 100644
> --- a/net/ipv6/ipv6_sockglue.c
> +++ b/net/ipv6/ipv6_sockglue.c
> @@ -542,7 +542,6 @@ done:
>     case MCAST_UNBLOCK_SOURCE:
>     {
>        struct group_source_req greqs;
> -      int omode, add;
> 
>        if (optlen < sizeof(struct group_source_req))
>           goto e_inval;
> @@ -550,33 +549,7 @@ done:
>           retv = -EFAULT;
>           break;
>        }
> -      if (greqs.gsr_group.ss_family != AF_INET6 ||
> -          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(sk, greqs.gsr_interface,
> -            &psin6->sin6_addr);
> -         /* 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);
> +      retv = ip6_mc_source(sk, optname, &greqs);
>        break;
>     }
>     case MCAST_MSFILTER:
> diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
> index 54f91ef..5d8d653 100644
> --- a/net/ipv6/mcast.c
> +++ b/net/ipv6/mcast.c
> @@ -355,8 +355,8 @@ void ipv6_sock_mc_close(struct sock *sk)
>     write_unlock_bh(&ipv6_sk_mc_lock);
>  }
> 
> -int ip6_mc_source(int add, int omode, struct sock *sk,
> -   struct group_source_req *pgsr)
> +static int __ip6_mc_source(int add, int omode, struct sock *sk,
> +            struct group_source_req *pgsr)
>  {
>     struct in6_addr *source, *group;
>     struct ipv6_mc_socklist *pmc;
> @@ -496,6 +496,40 @@ done:
>     return err;
>  }
> 
> +int ip6_mc_source(struct sock *sk, int optname,
> +        struct group_source_req *pgsr)
> +{
> +   int omode, add, retv;
> +
> +   if (pgsr->gsr_group.ss_family != AF_INET6 ||
> +       pgsr->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;
> +
> +      psin6 = (struct sockaddr_in6 *)&pgsr->gsr_group;
> +      retv = ipv6_sock_mc_join(sk, pgsr->gsr_interface,
> +                &psin6->sin6_addr);
> +      /* 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, pgsr);
> +}
> +
>  int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
>  {
>     struct in6_addr *group;
> 

        I think this part isn't relevant to compat support, right?
I don't see much advantage in moving this, but it should be a
separate patch and discussion, I think.

> +   switch (optname) {
> +   case MCAST_JOIN_SOURCE_GROUP:
> +   case MCAST_LEAVE_SOURCE_GROUP:
> +   case MCAST_BLOCK_SOURCE:
> +   case MCAST_UNBLOCK_SOURCE:
> +       {
> +      struct group_source_req greqs;

        Are you sure we want this on the kernel stack?

> 
> diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
> index b7ae360..30e6ca1 100644
> --- a/net/ipv6/ipv6_sockglue.c
> +++ b/net/ipv6/ipv6_sockglue.c
> @@ -744,6 +744,32 @@ compat_do_ipv6_setsockopt(struct sock *sk, int 
level, int optname,
>     int retv;
> 
>     switch (optname) {
> +   case MCAST_JOIN_GROUP:
> +   case MCAST_LEAVE_GROUP:
> +       {
> +      struct group_req greq;

        Big for the kernel stack.


> commit 3c055f969f7f3716f6846037c2ec0fa06e0424b3
> Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> Date:   Thu Apr 24 17:32:10 2008 +0900
> 
>     [IPV6] COMPAT: group_filter{}-setsockopt compat layer.
> 
>     Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> 
> diff --git a/include/linux/in.h b/include/linux/in.h
> index 2fbd7c7..ccbfc76 100644
> --- a/include/linux/in.h
> +++ b/include/linux/in.h
> @@ -182,6 +182,19 @@ struct compat_group_req
>     __u32                gr_interface;
>     struct __compat_sockaddr_storage    gr_group;
>  };
> +
> +struct compat_group_filter
> +{
> +   __u32               gf_interface;
> +   struct __compat_sockaddr_storage   gf_group;
> +   __u32               gf_fmode;
> +   __u32               gf_numsrc;
> +   struct __compat_sockaddr_storage   gf_slist[1];
> +};
> +
> +#define COMPAT_GROUP_FILTER_SIZE(numsrc) \
> +   (sizeof(struct compat_group_filter) - sizeof(struct 
__compat_sockaddr_storage) \
> +   + (numsrc) * sizeof(struct __compat_sockaddr_storage))
>  #endif
>  #endif

        Better to compute this just once, in which case no macro needed.
Suggest a variable and do
"len = GROUP_FILTER_SIZE(numsrc) + sizeof(struct compat_group_filter) - 
sizeof(struct group_filter);"
Also, why put these in an include file when there is only one use?

> 
> diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
> index 30e6ca1..0895e7d 100644
> --- a/net/ipv6/ipv6_sockglue.c
> +++ b/net/ipv6/ipv6_sockglue.c
> @@ -794,6 +794,57 @@ compat_do_ipv6_setsockopt(struct sock *sk, int 
level, int optname,
>        retv = ip6_mc_source(sk, optname, &greqs);
>        break;
>         }
> +   case MCAST_MSFILTER:
> +       {
> +      extern int sysctl_mld_max_msf;
> +      struct compat_group_filter __user *ugsf = (void __user *)optval;
> +      struct group_filter *gsf;
> +      u32 gf_numsrc;
> +      int i;
> +
> +      if (optlen < COMPAT_GROUP_FILTER_SIZE(0))
> +         return -EINVAL;
> +
> +      if (access_ok(VERIFY_READ, ugsf, COMPAT_GROUP_FILTER_SIZE(0)) ||
> +          __get_user(gf_numsrc, &ugsf->gf_numsrc))
> +         return -EFAULT;
> +
> +      if (COMPAT_GROUP_FILTER_SIZE(gf_numsrc) > optlen)
> +         return -EINVAL;
> +
> +      if (optlen > sysctl_optmem_max ||
> +          gf_numsrc >= 0x1ffffffU ||
> +          gf_numsrc > sysctl_mld_max_msf)
> +         return -ENOBUFS;

        Missing comment from the original that explains the significance
of "0x1ffffffU", and the version I'm looking at doesn't have the
sysctl_optmem_max check. Though it can be raised, without the check,
we can support very large filters too. Maybe ok, but unrelated to
compat support.

> +
> +      optlen = GROUP_FILTER_SIZE(gf_numsrc);
> +      gsf = kmalloc(optlen, GFP_KERNEL);
> +      if (!gsf) {
> +         retv = -ENOBUFS;
> +         break;
> +      }
> +      retv = -EFAULT;
> +      if (__get_user(gsf->gf_interface, &ugsf->gf_interface) ||
> +          __copy_from_user(&gsf->gf_group, &ugsf->gf_group, 
sizeof(gsf->gf_group)) ||
> +          __get_user(gsf->gf_fmode, &ugsf->gf_fmode) ||
> +          access_ok(VERIFY_READ, ugsf->gf_slist,
> +               sizeof(struct __compat_sockaddr_storage) * gf_numsrc))
> +         goto kfree_out;

        Does access_ok() handle (valid) case numsrc==0 correctly?

> +
> +      gsf->gf_numsrc = gf_numsrc;
> +
> +      for (i = 0; i < gf_numsrc; i++) {
> +         if (__copy_from_user(&gsf->gf_slist[i],
> +                    &ugsf->gf_slist[i],
> +                    sizeof(gsf->gf_slist[i])))
> +            goto kfree_out;
> +      }

        This is adding a lot of separate copies that I believe
are unnecessary. sockaddr_storage is the same size, so you
can copy optlen-GROUP_FILTER_SIZE(0) all at once.

> 
> -int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
> -   struct group_filter __user *optval, int __user *optlen)
> +int __ip6_mc_msf_lookup(struct sock *sk,
> +         struct in6_addr *group, int interface,
> +         struct ip6_sf_socklist **ppsl)
>  {
> -   int err, i, count, copycount;
> -   struct in6_addr *group;
>     struct ipv6_mc_socklist *pmc;
> -   struct inet6_dev *idev;
> -   struct net_device *dev;
>     struct ipv6_pinfo *inet6 = inet6_sk(sk);
> -   struct ip6_sf_socklist *psl;
> +   struct net_device *dev;
> +   struct inet6_dev *idev;
>     struct net *net = sock_net(sk);
> +   int ret = 0;
> 
> -   group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
> -
> -   if (!ipv6_addr_is_multicast(group))
> -      return -EINVAL;
> -
> -   idev = ip6_mc_find_dev(net, group, gsf->gf_interface);
> +   if (!ipv6_addr_is_multicast(group)) {
> +      ret = -EINVAL;
> +      goto out;
> +   }
> 
> -   if (!idev)
> -      return -ENODEV;
> +   idev = ip6_mc_find_dev(net, group, interface);
> +   if (!idev) {
> +      ret = -ENODEV;
> +      goto out;
> +   }

        Looks unrelated to compat support...? As what follows in
this function.

> 
>     dev = idev->dev;
> 
> -   err = -EADDRNOTAVAIL;
> +   ret = -EADDRNOTAVAIL;
>     /*
>      * changes to the ipv6_mc_list require the socket lock and
>      * a read lock on ip6_sk_mc_lock. We have the socket lock,
>      * so reading the list is safe.
>      */
> 
> -   for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) {
> -      if (pmc->ifindex != gsf->gf_interface)
> +   for (pmc = inet6->ipv6_mc_list; pmc; pmc = pmc->next) {
> +      if (pmc->ifindex != interface)
>           continue;
>        if (ipv6_addr_equal(group, &pmc->addr))
>           break;
>     }
> -   if (!pmc)      /* must have a prior join */
> -      goto done;
> -   gsf->gf_fmode = pmc->sfmode;
> -   psl = pmc->sflist;
> -   count = psl ? psl->sl_count : 0;
> +   if (pmc) {
> +      *ppsl = pmc->sflist;
> +      ret = pmc->sfmode;
> +   }
> +
>     read_unlock_bh(&idev->lock);
>     in6_dev_put(idev);
>     dev_put(dev);
> +out:
> +   return ret;
> +}
> 
> -   copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
> -   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, a read lock on
> -    * on ipv6_sk_mc_lock and a write lock on pmc->sflock. We
> -    * have the socket lock, so reading here is safe.
> -    */
> -   for (i=0; i<copycount; i++) {
> -      struct sockaddr_in6 *psin6;
> -      struct sockaddr_storage ss;
> +int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
> +   struct group_filter __user *optval, int __user *optlen)
> +{
> +   struct in6_addr *group;
> +   struct ip6_sf_socklist *psl;
> +   int fmode;
> +
> +   group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
> +
> +   fmode = __ip6_mc_msf_lookup(sk, group, gsf->gf_interface, &psl);
> +   if (fmode < 0)
> +      return fmode;
> 
> -      psin6 = (struct sockaddr_in6 *)&ss;
> -      memset(&ss, 0, sizeof(ss));
> -      psin6->sin6_family = AF_INET6;
> -      psin6->sin6_addr = psl->sl_addr[i];
> -      if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss)))
> +   if (psl) {
> +      int count = psl ? psl->sl_count : 0;
> +      int i, copycount;
> +      struct sockaddr_in6 sin6;
> +
> +      copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
> +
> +      if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
> +          access_ok(VERIFY_WRITE, optval, GROUP_FILTER_SIZE(copycount)) 
||
> +          __put_user((u32)fmode, &optval->gf_fmode) ||
> +          __put_user((u32)count, &optval->gf_numsrc))
>           return -EFAULT;
> +
> +      /* changes to psl require the socket lock, a read lock on
> +       * on ipv6_sk_mc_lock and a write lock on pmc->sflock. We
> +       * have the socket lock, so reading here is safe.
> +       */
> +      memset(&sin6, 0, sizeof(sin6));
> +      sin6.sin6_family = AF_INET6;
> +
> +      for (i = 0; i < copycount; i++) {
> +         struct sockaddr_in6 __user *usin6 = (void __user 
*)&optval->gf_slist[i];
> +
> +         ipv6_addr_copy(&sin6.sin6_addr, &psl->sl_addr[i]);
> +
> +         if (__copy_to_user(usin6, &sin6, sizeof(sin6)) ||
> +             __clear_user(usin6 + 1, sizeof(optval->gf_slist[i]) - 
sizeof(*usin6)))
> +            return -EFAULT;
> +      }
>     }
>     return 0;
> -done:
> -   read_unlock_bh(&idev->lock);
> -   in6_dev_put(idev);
> -   dev_put(dev);
> -   return err;
>  }
> 
>  int inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
> 
> ---
> commit e920bd82cbc6e0b83087513a25220e9379b24b49
> Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> Date:   Wed Apr 23 18:03:14 2008 +0900
> 
>     [IPV6] MCAST: Make ip6_mc_msfget() more generic.
> 
>     Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> 
> diff --git a/include/net/ipv6.h b/include/net/ipv6.h
> index aa81120..103272f 100644
> --- a/include/net/ipv6.h
> +++ b/include/net/ipv6.h
> @@ -568,7 +568,9 @@ struct group_filter;
>  extern int ip6_mc_source(struct sock *sk, int optname,
>            struct group_source_req *pgsr);
>  extern int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf);
> -extern int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
> +extern int ip6_mc_msfget(struct sock *sk,
> +          struct in6_addr *group, u32 gf_interface,
> +          u32 gf_srcnum,
>            struct group_filter __user *optval,
>            int __user *optlen);
> 
> diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
> index 0895e7d..d670078 100644
> --- a/net/ipv6/ipv6_sockglue.c
> +++ b/net/ipv6/ipv6_sockglue.c
> @@ -949,8 +949,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int 
level,
> int optname,
>        if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0)))
>           return -EFAULT;
>        lock_sock(sk);
> -      err = ip6_mc_msfget(sk, &gsf,
> -         (struct group_filter __user *)optval, optlen);
> +      err = ip6_mc_msfget(sk,
> +                &((struct sockaddr_in6 *)&gsf.gf_group)->sin6_addr,
> +                gsf.gf_interface, gsf.gf_numsrc,
> +                (struct group_filter __user *)optval, optlen);
>        release_sock(sk);
>        return err;
>     }
> diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
> index d32d6a3..b781192 100644
> --- a/net/ipv6/mcast.c
> +++ b/net/ipv6/mcast.c
> @@ -670,16 +670,14 @@ out:
>     return ret;
>  }
> 
> -int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
> -   struct group_filter __user *optval, int __user *optlen)
> +int ip6_mc_msfget(struct sock *sk,
> +        struct in6_addr *group, u32 gf_interface, u32 gf_numsrc,
> +        struct group_filter __user *optval, int __user *optlen)
>  {
> -   struct in6_addr *group;
>     struct ip6_sf_socklist *psl;
>     int fmode;
> 
> -   group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
> -
> -   fmode = __ip6_mc_msf_lookup(sk, group, gsf->gf_interface, &psl);
> +   fmode = __ip6_mc_msf_lookup(sk, group, gf_interface, &psl);
>     if (fmode < 0)
>        return fmode;
> 
> @@ -688,7 +686,7 @@ int ip6_mc_msfget(struct sock *sk, struct 
group_filter *gsf,
>        int i, copycount;
>        struct sockaddr_in6 sin6;
> 
> -      copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
> +      copycount = count < gf_numsrc ? count : gf_numsrc;
> 
>        if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
>            access_ok(VERIFY_WRITE, optval, GROUP_FILTER_SIZE(copycount)) 
||
> 
> ---
> commit 9153c1a545f2366f57bf46bcd59d5b99d61016f7
> Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> Date:   Thu Apr 24 17:51:30 2008 +0900
> 
>     [IPV6] COMPAT: group_filter{}-getsockopt compat layer.
> 
>     Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> 
> diff --git a/include/net/ipv6.h b/include/net/ipv6.h
> index 103272f..c98c722 100644
> --- a/include/net/ipv6.h
> +++ b/include/net/ipv6.h
> @@ -574,6 +574,16 @@ extern int ip6_mc_msfget(struct sock *sk,
>            struct group_filter __user *optval,
>            int __user *optlen);
> 
> +#ifdef CONFIG_COMPAT
> +struct compat_group_filter;
> +
> +int ip6_mc_compat_msfget(struct sock *sk,
> +          struct in6_addr *group,
> +          u32 gf_interface, u32 gf_numsrc,
> +          struct compat_group_filter __user *optval,
> +          int __user *optlen);
> +#endif
> +
>  #ifdef CONFIG_PROC_FS
>  extern int  ac6_proc_init(struct net *net);
>  extern void ac6_proc_exit(struct net *net);
> diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
> index d670078..46bafee 100644
> --- a/net/ipv6/ipv6_sockglue.c
> +++ b/net/ipv6/ipv6_sockglue.c
> @@ -1200,7 +1200,33 @@ static int
>  compat_do_ipv6_getsockopt(struct sock *sk, int level, int optname,
>             char __user *optval, int __user *optlen)
>  {
> -   return do_ipv6_getsockopt(sk, level, optname, optval, optlen);
> +   int len, err;
> +
> +   if (get_user(len, optlen))
> +      return -EFAULT;
> +
> +   switch (optname) {
> +   case MCAST_MSFILTER:
> +       {
> +      struct compat_group_filter gsf;
> +      int err;
> +
> +      if (len < COMPAT_GROUP_FILTER_SIZE(0))
> +         return -EINVAL;
> +      if (copy_from_user(&gsf, optval, COMPAT_GROUP_FILTER_SIZE(0)))
> +         return -EFAULT;
> +      lock_sock(sk);
> +      err = ip6_mc_compat_msfget(sk,
> +                  &((struct sockaddr_in6 *)&gsf.gf_group)->sin6_addr,
> +                  gsf.gf_interface, gsf.gf_numsrc,
> +                  (struct compat_group_filter __user *)optval,
> +                  optlen);
> +      release_sock(sk);
> +       }
> +   default:
> +      err = do_ipv6_getsockopt(sk, level, optname, optval, optlen);
> +   }
> +   return err;
>  }
> 
>  int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
> diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
> index b781192..bdfabbb 100644
> --- a/net/ipv6/mcast.c
> +++ b/net/ipv6/mcast.c
> @@ -714,6 +714,54 @@ int ip6_mc_msfget(struct sock *sk,
>     return 0;
>  }
> 
> +#ifdef CONFIG_COMPAT
> +int ip6_mc_compat_msfget(struct sock *sk,
> +          struct in6_addr *group,
> +          u32 gf_interface, u32 gf_numsrc,
> +          struct compat_group_filter __user *optval,
> +          int __user *optlen)
> +{
> +   struct ip6_sf_socklist *psl;
> +   int fmode;
> +
> +   fmode = __ip6_mc_msf_lookup(sk, group, gf_interface, &psl);
> +   if (fmode < 0)
> +      return fmode;
> +
> +   if (psl) {
> +      int count = psl ? psl->sl_count : 0;
> +      int i, copycount;
> +      struct sockaddr_in6 sin6;
> +
> +      copycount = count < gf_numsrc ? count : gf_numsrc;
> +
> +      if (put_user(COMPAT_GROUP_FILTER_SIZE(copycount), optlen) ||
> +          access_ok(VERIFY_WRITE, optval, 
COMPAT_GROUP_FILTER_SIZE(copycount)) ||

        numsrc == 0 case works?

> +          __put_user((u32)fmode, &optval->gf_fmode) ||
> +          __put_user((u32)count, &optval->gf_numsrc))
> +         return -EFAULT;
> +
> +      /* changes to psl require the socket lock, a read lock on
> +       * on ipv6_sk_mc_lock and a write lock on pmc->sflock. We
> +       * have the socket lock, so reading here is safe.
> +       */
> +      memset(&sin6, 0, sizeof(sin6));
> +      sin6.sin6_family = AF_INET6;
> +
> +      for (i = 0; i < copycount; i++) {
> +         struct sockaddr_in6 __user *usin6 = (void __user 
*)&optval->gf_slist[i];
> +
> +         ipv6_addr_copy(&sin6.sin6_addr, &psl->sl_addr[i]);
> +
> +         if (__copy_to_user(usin6, &sin6, sizeof(sin6)) ||
> +             __clear_user(usin6 + 1, sizeof(optval->gf_slist[i]) - 
sizeof(*usin6)))

why explicitly clear the rest of it?

...stopping here; will continue looking at the rest.

                                                +-DLS


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-24 20:01       ` David Stevens
@ 2008-04-24 20:43         ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-24 20:59           ` David Stevens
  0 siblings, 1 reply; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-24 20:43 UTC (permalink / raw)
  To: dlstevens; +Cc: davem, netdev

In article <OFFE40C097.9EE8B821-ON88257435.0067E798-88257435.006E0467@us.ibm.com> (at Thu, 24 Apr 2008 13:01:43 -0700), David Stevens <dlstevens@us.ibm.com> says:


> >  int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
> >  {
> >     struct in6_addr *group;
> > 
> 
>         I think this part isn't relevant to compat support, right?
> I don't see much advantage in moving this, but it should be a
> separate patch and discussion, I think.

Otherwise, we will have too much duplicate code.

> 
> > +   switch (optname) {
> > +   case MCAST_JOIN_SOURCE_GROUP:
> > +   case MCAST_LEAVE_SOURCE_GROUP:
> > +   case MCAST_BLOCK_SOURCE:
> > +   case MCAST_UNBLOCK_SOURCE:
> > +       {
> > +      struct group_source_req greqs;
> 
>         Are you sure we want this on the kernel stack?
:
> > +      struct group_req greq;
> 
>         Big for the kernel stack.

I know, and I plan to fix stack usage in the next stage.

> > +
> > +struct compat_group_filter
> > +{
> > +   __u32               gf_interface;
> > +   struct __compat_sockaddr_storage   gf_group;
> > +   __u32               gf_fmode;
> > +   __u32               gf_numsrc;
> > +   struct __compat_sockaddr_storage   gf_slist[1];
> > +};
> > +
> > +#define COMPAT_GROUP_FILTER_SIZE(numsrc) \
> > +   (sizeof(struct compat_group_filter) - sizeof(struct 
> __compat_sockaddr_storage) \
> > +   + (numsrc) * sizeof(struct __compat_sockaddr_storage))
> >  #endif
> >  #endif
> 
>         Better to compute this just once, in which case no macro needed.
> Suggest a variable and do
> "len = GROUP_FILTER_SIZE(numsrc) + sizeof(struct compat_group_filter) - 
> sizeof(struct group_filter);"
> Also, why put these in an include file when there is only one use?

It is much clearer to mirror original logic.

> > +      optlen = GROUP_FILTER_SIZE(gf_numsrc);
> > +      gsf = kmalloc(optlen, GFP_KERNEL);
> > +      if (!gsf) {
> > +         retv = -ENOBUFS;
> > +         break;
> > +      }
> > +      retv = -EFAULT;
> > +      if (__get_user(gsf->gf_interface, &ugsf->gf_interface) ||
> > +          __copy_from_user(&gsf->gf_group, &ugsf->gf_group, 
> sizeof(gsf->gf_group)) ||
> > +          __get_user(gsf->gf_fmode, &ugsf->gf_fmode) ||
> > +          access_ok(VERIFY_READ, ugsf->gf_slist,
> > +               sizeof(struct __compat_sockaddr_storage) * gf_numsrc))
> > +         goto kfree_out;
> 
>         Does access_ok() handle (valid) case numsrc==0 correctly?

Hmm, not sure...

> > +      gsf->gf_numsrc = gf_numsrc;
> > +
> > +      for (i = 0; i < gf_numsrc; i++) {
> > +         if (__copy_from_user(&gsf->gf_slist[i],
> > +                    &ugsf->gf_slist[i],
> > +                    sizeof(gsf->gf_slist[i])))
> > +            goto kfree_out;
> > +      }
> 
>         This is adding a lot of separate copies that I believe
> are unnecessary. sockaddr_storage is the same size, so you
> can copy optlen-GROUP_FILTER_SIZE(0) all at once.

Okay...

> > -int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
> > -   struct group_filter __user *optval, int __user *optlen)
> > +int __ip6_mc_msf_lookup(struct sock *sk,
:
> > +      goto out;
> > +   }
> 
>         Looks unrelated to compat support...? As what follows in
> this function.

For killing duplicates between native / compat code.

> > +
> > +      copycount = count < gf_numsrc ? count : gf_numsrc;
> > +
> > +      if (put_user(COMPAT_GROUP_FILTER_SIZE(copycount), optlen) ||
> > +          access_ok(VERIFY_WRITE, optval, 
> COMPAT_GROUP_FILTER_SIZE(copycount)) ||
> 
>         numsrc == 0 case works?

???

> > +         ipv6_addr_copy(&sin6.sin6_addr, &psl->sl_addr[i]);
> > +
> > +         if (__copy_to_user(usin6, &sin6, sizeof(sin6)) ||
> > +             __clear_user(usin6 + 1, sizeof(optval->gf_slist[i]) - 
> sizeof(*usin6)))
> 
> why explicitly clear the rest of it?

We mirror original behavior, which clears rest of sockaddr_in6{}.

--yoshfuji

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-24 20:43         ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-24 20:59           ` David Stevens
  0 siblings, 0 replies; 39+ messages in thread
From: David Stevens @ 2008-04-24 20:59 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明; +Cc: davem, netdev

A general comment that I have is that you're making changes
that affect the non-compat case. Which, of course, is why I prefer
the approach I took in the patch I sent. The non-compat code is
completely untouched, and only the translations needed for
compatibility are done. This reduces (really, nearly removes)
the possibility of breaking existing working code, while only
risking bugs in apps that currently don't work at all.

Aside from that, I think the approach is just simpler, easier to
understand and review, and a  smaller patch. I can't
tell trivially by looking at your patch if errno is set the same on
all non-compat cases as it was before your patch, for example.
It may be, but I know it is in my patch without having to test all
those cases, because those are unchanged by the compat
code I added.

                                        +-DLS


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-24 17:52 ` David Stevens
@ 2008-04-25  5:38   ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-25  6:23     ` David Stevens
  2008-04-25  6:31   ` David Miller
  1 sibling, 1 reply; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-25  5:38 UTC (permalink / raw)
  To: dlstevens; +Cc: davem, netdev, yoshfuji

In article <OF3A37018B.9EFE360C-ON88257435.00614684-88257435.00622448@us.ibm.com> (at Thu, 24 Apr 2008 11:52:01 -0600), David Stevens <dlstevens@us.ibm.com> says:

>  #ifdef CONFIG_COMPAT
> +
> +struct compat_group_req {
> +       __u32                                   gr_interface;
> +       struct __kernel_sockaddr_storage        gr_group;
> +} __attribute__ ((packed));
> +
:

I prefer __compat_sockaddr_storage{} which has definition closer to
one on 32bit platform.  "packed" seems overkill to me.

>  int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
>                            char __user *optval, int optlen)
>  {
> @@ -769,6 +791,95 @@ int compat_ipv6_setsockopt(struct sock *
>         if (level != SOL_IPV6)
>                 return -ENOPROTOOPT;
>  
> +                   __put_user(interface, &kgr->gr_interface) ||
> +                   __copy_tofrom_user(&kgr->gr_group, &gr32->gr_group,
> +                               sizeof(kgr->gr_group)))
:

Unfortunately this cannot work.
__copy_tofrom_user is not available on x86_64 etc.

And this will increase copy operation, and I really dislike it, anyway.

--yoshfuji

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-25  5:38   ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-25  6:23     ` David Stevens
  2008-04-25  6:27       ` David Miller
  0 siblings, 1 reply; 39+ messages in thread
From: David Stevens @ 2008-04-25  6:23 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: davem, netdev, netdev-owner, yoshfuji

netdev-owner@vger.kernel.org wrote on 04/24/2008 10:38:45 PM:

> In article 
<OF3A37018B.9EFE360C-ON88257435.00614684-88257435.00622448@us.ibm.
> com> (at Thu, 24 Apr 2008 11:52:01 -0600), David Stevens 
<dlstevens@us.ibm.com> says:
> 
> >  #ifdef CONFIG_COMPAT
> > +
> > +struct compat_group_req {
> > +       __u32                                   gr_interface;
> > +       struct __kernel_sockaddr_storage        gr_group;
> > +} __attribute__ ((packed));
> > +
> :
> 
> I prefer __compat_sockaddr_storage{} which has definition closer to
> one on 32bit platform.  "packed" seems overkill to me.

        Yes, I saw the discussion you and Dave had. I think it ought to be
at least a local-use declaration with these, rather than in a header. For
these 32-bit ints that are being aligned in this case, the effect is
exactly the same, so I don't object.

> Unfortunately this cannot work.
> __copy_tofrom_user is not available on x86_64 etc.

        I don't know if there is a standard user-user copy
available for these. I wanted to allocate the copy on the
user stack to avoid allocating a temporary kernel buffer and
resetting the segment, but I prefer any approach where the
compatibility portion is done in the compat wrapper without
modification to non-compat code. That approach also has the
advantage of allowing the same wrapper to work with both
v6 and v4.

                                                        +-DLS


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-25  6:23     ` David Stevens
@ 2008-04-25  6:27       ` David Miller
  0 siblings, 0 replies; 39+ messages in thread
From: David Miller @ 2008-04-25  6:27 UTC (permalink / raw)
  To: dlstevens; +Cc: yoshfuji, netdev, netdev-owner

From: David Stevens <dlstevens@us.ibm.com>
Date: Thu, 24 Apr 2008 23:23:15 -0700

> I wanted to allocate the copy on the user stack

compat_alloc_user_space(size), as you used it, is the way

On sparc64 I named the "within userspace" copy function
copy_in_user() and this seems to be supported well enough
to be used by the generic compat code elsewhere in the
tree so very likely it's supported everywhere it needs
to be


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-24 17:52 ` David Stevens
  2008-04-25  5:38   ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-25  6:31   ` David Miller
  2008-04-25  6:37     ` YOSHIFUJI Hideaki / 吉藤英明
  1 sibling, 1 reply; 39+ messages in thread
From: David Miller @ 2008-04-25  6:31 UTC (permalink / raw)
  To: dlstevens; +Cc: yoshfuji, netdev, netdev-owner

From: David Stevens <dlstevens@us.ibm.com>
Date: Thu, 24 Apr 2008 11:52:01 -0600

> I was looking at this problem too, and below is what I have at the
> moment. I've only tested the group_req portion, and I was looking
> at putting this in net/compat.c with hooks back, since the code is
> literally identical for IPv4 except the sockopt function you call at
> the end, but I've moved it to ipv6_sockglue.c for this patch.
> 
> This touches a lot less code and I think comes out a lot smaller.
> 
> Method is to translate to padded version on the user stack, make
> the call, and translate back (in the MSFILTER case) on return, as
> is done for some other compat functions.

I've looked at both approaches.

David's might be the better way to do this, as an initial
approximation.

I think we can combine ideas from both implementations, so why don't
we do the following:

1) Use copy_in_user() as that's the proper interaface to copy within
   userspace in COMPAT code.

2) Use aligned(4) attribute instead of packed, this will help RISC
   compat systems a lot

If you make these changes and do some testing, I think this is the
patch I will apply.

Thanks.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-25  6:31   ` David Miller
@ 2008-04-25  6:37     ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-25  6:47       ` David Miller
  0 siblings, 1 reply; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-25  6:37 UTC (permalink / raw)
  To: davem; +Cc: dlstevens, netdev, yoshfuji

In article <20080424.233159.06679047.davem@davemloft.net> (at Thu, 24 Apr 2008 23:31:59 -0700 (PDT)), David Miller <davem@davemloft.net> says:

> 1) Use copy_in_user() as that's the proper interaface to copy within
>    userspace in COMPAT code.

So, don't we care increase of copy operation with it?

--yoshfuji

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-25  6:37     ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-25  6:47       ` David Miller
  2008-04-25  6:59         ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 1 reply; 39+ messages in thread
From: David Miller @ 2008-04-25  6:47 UTC (permalink / raw)
  To: yoshfuji; +Cc: dlstevens, netdev

From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
Date: Fri, 25 Apr 2008 15:37:33 +0900 (JST)

> In article <20080424.233159.06679047.davem@davemloft.net> (at Thu, 24 Apr 2008 23:31:59 -0700 (PDT)), David Miller <davem@davemloft.net> says:
> 
> > 1) Use copy_in_user() as that's the proper interaface to copy within
> >    userspace in COMPAT code.
> 
> So, don't we care increase of copy operation with it?

I think for now it's an OK tradeoff, but yes it does increase
copies.  But then again, many compat layer handlers do.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-25  6:47       ` David Miller
@ 2008-04-25  6:59         ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-25  7:04           ` David Miller
  0 siblings, 1 reply; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-25  6:59 UTC (permalink / raw)
  To: davem; +Cc: dlstevens, netdev

In article <20080424.234748.53723057.davem@davemloft.net> (at Thu, 24 Apr 2008 23:47:48 -0700 (PDT)), David Miller <davem@davemloft.net> says:

> From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
> Date: Fri, 25 Apr 2008 15:37:33 +0900 (JST)
> 
> > In article <20080424.233159.06679047.davem@davemloft.net> (at Thu, 24 Apr 2008 23:31:59 -0700 (PDT)), David Miller <davem@davemloft.net> says:
> > 
> > > 1) Use copy_in_user() as that's the proper interaface to copy within
> > >    userspace in COMPAT code.
> > 
> > So, don't we care increase of copy operation with it?
> 
> I think for now it's an OK tradeoff, but yes it does increase
> copies.  But then again, many compat layer handlers do.

And in IPv4, group_XXX{} handlers are even wrappers for ip_mreqXXX{}
handlers so we we have another copy...

--yoshfuji

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-25  6:59         ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-25  7:04           ` David Miller
  2008-04-25  7:24             ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 1 reply; 39+ messages in thread
From: David Miller @ 2008-04-25  7:04 UTC (permalink / raw)
  To: yoshfuji; +Cc: dlstevens, netdev

From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
Date: Fri, 25 Apr 2008 15:59:22 +0900 (JST)

> In article <20080424.234748.53723057.davem@davemloft.net> (at Thu, 24 Apr 2008 23:47:48 -0700 (PDT)), David Miller <davem@davemloft.net> says:
> 
> > From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
> > Date: Fri, 25 Apr 2008 15:37:33 +0900 (JST)
> > 
> > > In article <20080424.233159.06679047.davem@davemloft.net> (at Thu, 24 Apr 2008 23:31:59 -0700 (PDT)), David Miller <davem@davemloft.net> says:
> > > 
> > > > 1) Use copy_in_user() as that's the proper interaface to copy within
> > > >    userspace in COMPAT code.
> > > 
> > > So, don't we care increase of copy operation with it?
> > 
> > I think for now it's an OK tradeoff, but yes it does increase
> > copies.  But then again, many compat layer handlers do.
> 
> And in IPv4, group_XXX{} handlers are even wrappers for ip_mreqXXX{}
> handlers so we we have another copy...

It is always possible to simplify things and make them more efficient.
And we should always do that.

The priority at the moment should be the simplist bug fix.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-25  7:04           ` David Miller
@ 2008-04-25  7:24             ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-26  0:24               ` David Stevens
  0 siblings, 1 reply; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-25  7:24 UTC (permalink / raw)
  To: davem; +Cc: dlstevens, netdev, yoshfuji

In article <20080425.000418.108840147.davem@davemloft.net> (at Fri, 25 Apr 2008 00:04:18 -0700 (PDT)), David Miller <davem@davemloft.net> says:

> From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
> Date: Fri, 25 Apr 2008 15:59:22 +0900 (JST)
> 
> > In article <20080424.234748.53723057.davem@davemloft.net> (at Thu, 24 Apr 2008 23:47:48 -0700 (PDT)), David Miller <davem@davemloft.net> says:
> > 
> > > From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
> > > Date: Fri, 25 Apr 2008 15:37:33 +0900 (JST)
> > > 
> > > > In article <20080424.233159.06679047.davem@davemloft.net> (at Thu, 24 Apr 2008 23:31:59 -0700 (PDT)), David Miller <davem@davemloft.net> says:
> > > > 
> > > > > 1) Use copy_in_user() as that's the proper interaface to copy within
> > > > >    userspace in COMPAT code.
> > > > 
> > > > So, don't we care increase of copy operation with it?
> > > 
> > > I think for now it's an OK tradeoff, but yes it does increase
> > > copies.  But then again, many compat layer handlers do.
> > 
> > And in IPv4, group_XXX{} handlers are even wrappers for ip_mreqXXX{}
> > handlers so we we have another copy...
> 
> It is always possible to simplify things and make them more efficient.
> And we should always do that.
> 
> The priority at the moment should be the simplist bug fix.

Okay I will pend them for net-2.6.27.

--yoshfuji

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-25  7:24             ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-26  0:24               ` David Stevens
  2008-04-26  3:33                 ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-26  5:14                 ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 2 replies; 39+ messages in thread
From: David Stevens @ 2008-04-26  0:24 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明, davem
  Cc: netdev, yoshfuji

[-- Attachment #1: Type: text/plain, Size: 8620 bytes --]

Here's what I have for setsockopt, v4 & v6 and tested with both.

I discovered that the aligned attribute is only a minimum, so
aligned by itself has no effect on padding. GNU documentation
says you need both aligned and packed to make the pad be
a certain value.  In this case, because the items that
are padded are 32 bit, they are identical with and without on all
architectures, but I stuck in the aligned all the same.

Patch is against 2.6.18, but it applies to 2.6.25 too.

I'll get to work on getsockopt for these now.

                                +-DLS

Patch blurb:

Added 32-bit compatibility support for setsockopt using
MCAST_* socket options.

Signed-off-by: David L Stevens <dlstevens@us.ibm.com>

diff -ruNp linux-2.6.18.ppc64/include/net/compat.h 
linux-2.6.18.ppc64DLS1/include/net/compat.h
--- linux-2.6.18.ppc64/include/net/compat.h     2008-04-21 
13:50:33.000000000 -0700
+++ linux-2.6.18.ppc64DLS1/include/net/compat.h 2008-04-25 
03:13:39.000000000 -0700
@@ -39,4 +39,6 @@ extern int put_cmsg_compat(struct msghdr
 
 extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock 
*, unsigned char *, int);
 
+extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, 
int);
+
 #endif /* NET_COMPAT_H */
diff -ruNp linux-2.6.18.ppc64/net/compat.c 
linux-2.6.18.ppc64DLS1/net/compat.c
--- linux-2.6.18.ppc64/net/compat.c     2008-04-21 13:51:02.000000000 
-0700
+++ linux-2.6.18.ppc64DLS1/net/compat.c 2008-04-25 08:53:47.000000000 
-0700
@@ -26,6 +26,8 @@
 
 #include <net/scm.h>
 #include <net/sock.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
 #include <asm/uaccess.h>
 #include <net/compat.h>
 
@@ -589,6 +591,122 @@ asmlinkage long compat_sys_getsockopt(in
        }
        return err;
 }
+
+struct compat_group_req {
+       __u32                            gr_interface;
+       struct __kernel_sockaddr_storage gr_group
+               __attribute__ ((aligned(4)));
+} __attribute__ ((packed));
+
+struct compat_group_source_req {
+       __u32                            gsr_interface;
+       struct __kernel_sockaddr_storage gsr_group
+               __attribute__ ((aligned(4)));
+       struct __kernel_sockaddr_storage gsr_source
+               __attribute__ ((aligned(4)));
+} __attribute__ ((packed));
+
+struct compat_group_filter {
+       __u32                            gf_interface;
+       struct __kernel_sockaddr_storage gf_group
+               __attribute__ ((aligned(4)));
+       __u32                            gf_fmode;
+       __u32                            gf_numsrc;
+       struct __kernel_sockaddr_storage gf_slist[1]
+               __attribute__ ((aligned(4)));
+} __attribute__ ((packed));
+
+
+int compat_mc_setsockopt(struct sock *sock, int level, int optname,
+       char __user *optval, int optlen)
+{
+       char __user     *koptval = optval;
+       int             koptlen = optlen;
+
+       switch (optname) {
+       case MCAST_JOIN_GROUP:
+       case MCAST_LEAVE_GROUP:
+       {
+               struct compat_group_req __user *gr32 = (void *)optval;
+               struct group_req __user *kgr =
+                       compat_alloc_user_space(sizeof(struct group_req));
+               u32 interface;
+
+               if (!access_ok(VERIFY_READ, gr32, sizeof(*gr32)) ||
+                   !access_ok(VERIFY_WRITE, 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 
*)optval;
+               struct group_source_req *kgsr = compat_alloc_user_space(
+                       sizeof(struct group_source_req));
+               u32 interface;
+
+               if (!access_ok(VERIFY_READ, gsr32, sizeof(*gsr32)) ||
+                   !access_ok(VERIFY_WRITE, 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 *)optval;
+               struct group_filter *kgf;
+               u32 interface, fmode, numsrc;
+
+               if (!access_ok(VERIFY_READ, gf32, sizeof(*gf32)) ||
+                   __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(VERIFY_WRITE, 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;
+       }
+       if (level == SOL_IPV6)
+               return ipv6_setsockopt(sock, level, optname, koptval, 
koptlen);
+       return ip_setsockopt(sock, level, optname, koptval, koptlen);
+}
+
+EXPORT_SYMBOL(compat_mc_setsockopt);
+
+
 /* Argument list sizes for compat_sys_socketcall */
 #define AL(x) ((x) * sizeof(u32))
 static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
diff -ruNp linux-2.6.18.ppc64/net/ipv4/ip_sockglue.c 
linux-2.6.18.ppc64DLS1/net/ipv4/ip_sockglue.c
--- linux-2.6.18.ppc64/net/ipv4/ip_sockglue.c   2008-04-21 
13:51:00.000000000 -0700
+++ linux-2.6.18.ppc64DLS1/net/ipv4/ip_sockglue.c       2008-04-25 
08:16:40.000000000 -0700
@@ -37,6 +37,7 @@
 #include <linux/mroute.h>
 #include <net/route.h>
 #include <net/xfrm.h>
+#include <net/compat.h>
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #include <net/transp_v6.h>
 #endif
@@ -921,6 +922,9 @@ int compat_ip_setsockopt(struct sock *sk
        if (level != SOL_IP)
                return -ENOPROTOOPT;
 
+       if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
+               return compat_mc_setsockopt(sk, level, optname, optval, 
optlen);
+
        err = do_ip_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
        /* we need to exclude all possible ENOPROTOOPTs except default 
case */
diff -ruNp linux-2.6.18.ppc64/net/ipv6/ipv6_sockglue.c 
linux-2.6.18.ppc64DLS1/net/ipv6/ipv6_sockglue.c
--- linux-2.6.18.ppc64/net/ipv6/ipv6_sockglue.c 2008-04-21 
13:51:02.000000000 -0700
+++ linux-2.6.18.ppc64DLS1/net/ipv6/ipv6_sockglue.c     2008-04-25 
05:34:02.000000000 -0700
@@ -52,6 +52,7 @@
 #include <net/tcp.h>
 #include <net/udp.h>
 #include <net/xfrm.h>
+#include <net/compat.h>
 
 #include <asm/uaccess.h>
 
@@ -769,6 +770,9 @@ int compat_ipv6_setsockopt(struct sock *
        if (level != SOL_IPV6)
                return -ENOPROTOOPT;
 
+       if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
+               return compat_mc_setsockopt(sk, level, optname, optval, 
optlen);
+
        err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
        /* we need to exclude all possible ENOPROTOOPTs except default 
case */


[-- Attachment #2: setsockopt2.patch --]
[-- Type: application/octet-stream, Size: 6486 bytes --]

diff -ruNp linux-2.6.18.ppc64/include/net/compat.h linux-2.6.18.ppc64DLS1/include/net/compat.h
--- linux-2.6.18.ppc64/include/net/compat.h	2008-04-21 13:50:33.000000000 -0700
+++ linux-2.6.18.ppc64DLS1/include/net/compat.h	2008-04-25 03:13:39.000000000 -0700
@@ -39,4 +39,6 @@ extern int put_cmsg_compat(struct msghdr
 
 extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, unsigned char *, int);
 
+extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, int);
+
 #endif /* NET_COMPAT_H */
diff -ruNp linux-2.6.18.ppc64/net/compat.c linux-2.6.18.ppc64DLS1/net/compat.c
--- linux-2.6.18.ppc64/net/compat.c	2008-04-21 13:51:02.000000000 -0700
+++ linux-2.6.18.ppc64DLS1/net/compat.c	2008-04-25 08:53:47.000000000 -0700
@@ -26,6 +26,8 @@
 
 #include <net/scm.h>
 #include <net/sock.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
 #include <asm/uaccess.h>
 #include <net/compat.h>
 
@@ -589,6 +591,122 @@ asmlinkage long compat_sys_getsockopt(in
 	}
 	return err;
 }
+
+struct compat_group_req {
+	__u32				 gr_interface;
+	struct __kernel_sockaddr_storage gr_group
+		__attribute__ ((aligned(4)));
+} __attribute__ ((packed));
+
+struct compat_group_source_req {
+	__u32				 gsr_interface;
+	struct __kernel_sockaddr_storage gsr_group
+		__attribute__ ((aligned(4)));
+	struct __kernel_sockaddr_storage gsr_source
+		__attribute__ ((aligned(4)));
+} __attribute__ ((packed));
+
+struct compat_group_filter {
+	__u32				 gf_interface;
+	struct __kernel_sockaddr_storage gf_group
+		__attribute__ ((aligned(4)));
+	__u32				 gf_fmode;
+	__u32				 gf_numsrc;
+	struct __kernel_sockaddr_storage gf_slist[1]
+		__attribute__ ((aligned(4)));
+} __attribute__ ((packed));
+
+
+int compat_mc_setsockopt(struct sock *sock, int level, int optname,
+	char __user *optval, int optlen)
+{
+	char __user	*koptval = optval;
+	int		koptlen = optlen;
+
+	switch (optname) {
+	case MCAST_JOIN_GROUP:
+	case MCAST_LEAVE_GROUP:
+	{
+		struct compat_group_req __user *gr32 = (void *)optval;
+		struct group_req __user *kgr =
+			compat_alloc_user_space(sizeof(struct group_req));
+		u32 interface;
+
+		if (!access_ok(VERIFY_READ, gr32, sizeof(*gr32)) ||
+		    !access_ok(VERIFY_WRITE, 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 *)optval;
+		struct group_source_req *kgsr = compat_alloc_user_space(
+			sizeof(struct group_source_req));
+		u32 interface;
+
+		if (!access_ok(VERIFY_READ, gsr32, sizeof(*gsr32)) ||
+		    !access_ok(VERIFY_WRITE, 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 *)optval;
+		struct group_filter *kgf;
+		u32 interface, fmode, numsrc;
+
+		if (!access_ok(VERIFY_READ, gf32, sizeof(*gf32)) ||
+		    __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(VERIFY_WRITE, 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;
+	}
+	if (level == SOL_IPV6)
+		return ipv6_setsockopt(sock, level, optname, koptval, koptlen);
+	return ip_setsockopt(sock, level, optname, koptval, koptlen);
+}
+
+EXPORT_SYMBOL(compat_mc_setsockopt);
+
+
 /* Argument list sizes for compat_sys_socketcall */
 #define AL(x) ((x) * sizeof(u32))
 static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
diff -ruNp linux-2.6.18.ppc64/net/ipv4/ip_sockglue.c linux-2.6.18.ppc64DLS1/net/ipv4/ip_sockglue.c
--- linux-2.6.18.ppc64/net/ipv4/ip_sockglue.c	2008-04-21 13:51:00.000000000 -0700
+++ linux-2.6.18.ppc64DLS1/net/ipv4/ip_sockglue.c	2008-04-25 08:16:40.000000000 -0700
@@ -37,6 +37,7 @@
 #include <linux/mroute.h>
 #include <net/route.h>
 #include <net/xfrm.h>
+#include <net/compat.h>
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #include <net/transp_v6.h>
 #endif
@@ -921,6 +922,9 @@ int compat_ip_setsockopt(struct sock *sk
 	if (level != SOL_IP)
 		return -ENOPROTOOPT;
 
+	if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
+		return compat_mc_setsockopt(sk, level, optname, optval, optlen);
+
 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
 	/* we need to exclude all possible ENOPROTOOPTs except default case */
diff -ruNp linux-2.6.18.ppc64/net/ipv6/ipv6_sockglue.c linux-2.6.18.ppc64DLS1/net/ipv6/ipv6_sockglue.c
--- linux-2.6.18.ppc64/net/ipv6/ipv6_sockglue.c	2008-04-21 13:51:02.000000000 -0700
+++ linux-2.6.18.ppc64DLS1/net/ipv6/ipv6_sockglue.c	2008-04-25 05:34:02.000000000 -0700
@@ -52,6 +52,7 @@
 #include <net/tcp.h>
 #include <net/udp.h>
 #include <net/xfrm.h>
+#include <net/compat.h>
 
 #include <asm/uaccess.h>
 
@@ -769,6 +770,9 @@ int compat_ipv6_setsockopt(struct sock *
 	if (level != SOL_IPV6)
 		return -ENOPROTOOPT;
 
+	if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
+		return compat_mc_setsockopt(sk, level, optname, optval, optlen);
+
 	err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
 	/* we need to exclude all possible ENOPROTOOPTs except default case */

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  0:24               ` David Stevens
@ 2008-04-26  3:33                 ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-26  4:36                   ` David Stevens
  2008-04-26 23:55                   ` David Stevens
  2008-04-26  5:14                 ` YOSHIFUJI Hideaki / 吉藤英明
  1 sibling, 2 replies; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-26  3:33 UTC (permalink / raw)
  To: dlstevens; +Cc: davem, netdev, yoshfuji

In article <OFAF2C4F7A.77CF5A8D-ON88257437.0001566B-88257437.00023C8D@us.ibm.com> (at Fri, 25 Apr 2008 18:24:28 -0600), David Stevens <dlstevens@us.ibm.com> says:

> I discovered that the aligned attribute is only a minimum, so
> aligned by itself has no effect on padding. GNU documentation
> says you need both aligned and packed to make the pad be
> a certain value.  In this case, because the items that
> are padded are 32 bit, they are identical with and without on all
> architectures, but I stuck in the aligned all the same.

Please define __compat_sockaddr_storage{} with attribute((aligned(4))).
If you do that we do not need attribute(packed).  No?

> +extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, 
> int);
> +

compat_do_inet_mc_setsockopt() or so.

>  #endif /* NET_COMPAT_H */
> diff -ruNp linux-2.6.18.ppc64/net/compat.c 
> linux-2.6.18.ppc64DLS1/net/compat.c
> --- linux-2.6.18.ppc64/net/compat.c     2008-04-21 13:51:02.000000000 
> -0700
> +++ linux-2.6.18.ppc64DLS1/net/compat.c 2008-04-25 08:53:47.000000000 
> -0700

net/compat.c is not the right place.
Please put this in net/ipv4/compat.c or so.
Or at least, these must be guarded by CONFIG_INET.

> @@ -26,6 +26,8 @@
>  
>  #include <net/scm.h>
>  #include <net/sock.h>
> +#include <net/ip.h>
> +#include <net/ipv6.h>
>  #include <asm/uaccess.h>
>  #include <net/compat.h>
>  
> @@ -589,6 +591,122 @@ asmlinkage long compat_sys_getsockopt(in
>         }
>         return err;
>  }
> +
> +struct compat_group_req {
> +       __u32                            gr_interface;
> +       struct __kernel_sockaddr_storage gr_group
> +               __attribute__ ((aligned(4)));
> +} __attribute__ ((packed));
> +
> +struct compat_group_source_req {
> +       __u32                            gsr_interface;
> +       struct __kernel_sockaddr_storage gsr_group
> +               __attribute__ ((aligned(4)));
> +       struct __kernel_sockaddr_storage gsr_source
> +               __attribute__ ((aligned(4)));
> +} __attribute__ ((packed));
> +
> +struct compat_group_filter {
> +       __u32                            gf_interface;
> +       struct __kernel_sockaddr_storage gf_group
> +               __attribute__ ((aligned(4)));
> +       __u32                            gf_fmode;
> +       __u32                            gf_numsrc;
> +       struct __kernel_sockaddr_storage gf_slist[1]
> +               __attribute__ ((aligned(4)));
> +} __attribute__ ((packed));
> +

include/linux/in.n.

> +
> +int compat_mc_setsockopt(struct sock *sock, int level, int optname,
> +       char __user *optval, int optlen)
> +{
> +       char __user     *koptval = optval;
> +       int             koptlen = optlen;
> +
> +       switch (optname) {
> +       case MCAST_JOIN_GROUP:
> +       case MCAST_LEAVE_GROUP:
> +       {
> +               struct compat_group_req __user *gr32 = (void *)optval;
> +               struct group_req __user *kgr =
> +                       compat_alloc_user_space(sizeof(struct group_req));
> +               u32 interface;
> +
> +               if (!access_ok(VERIFY_READ, gr32, sizeof(*gr32)) ||
> +                   !access_ok(VERIFY_WRITE, 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;

__copy_in_user().
Maybe we can use __copy_in_user for u32?

--yoshfuji

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  3:33                 ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-26  4:36                   ` David Stevens
  2008-04-26  4:53                     ` YOSHIFUJI Hideaki / 吉藤英明
                                       ` (2 more replies)
  2008-04-26 23:55                   ` David Stevens
  1 sibling, 3 replies; 39+ messages in thread
From: David Stevens @ 2008-04-26  4:36 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: davem, netdev, yoshfuji

YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org> wrote on 04/25/2008 
08:33:59 PM:

> Please define __compat_sockaddr_storage{} with attribute((aligned(4))).
> If you do that we do not need attribute(packed).  No?

        I just tried it and the compiler seems to do that for this case,
but the gcc documentation says explicitly that you need both packed and
aligned if you want it to be a specific value. aligned(4) is a minimum
only and the compiler would be free to add pad still as long as the
padded value was also 4-byte aligned.
        In this case, we want no pad, so packed is the relevant part. If
had a char followed by a struct as in your previous example, the right
way to get 4-byte alignment is both aligned(4) and packed. Leaving off
packed would allow 8-byte or 16-byte (or 12 :-)) alignment, too.

> 
> > +extern int compat_mc_setsockopt(struct sock *, int, int, char __user 
*, 
> > int);
> > +
> 
> compat_do_inet_mc_setsockopt() or so.
> 
> >  #endif /* NET_COMPAT_H */
> > diff -ruNp linux-2.6.18.ppc64/net/compat.c 
> > linux-2.6.18.ppc64DLS1/net/compat.c
> > --- linux-2.6.18.ppc64/net/compat.c     2008-04-21 13:51:02.000000000 
> > -0700
> > +++ linux-2.6.18.ppc64DLS1/net/compat.c 2008-04-25 08:53:47.000000000 
> > -0700
> 
> net/compat.c is not the right place.
> Please put this in net/ipv4/compat.c or so.
> Or at least, these must be guarded by CONFIG_INET.

        This code is for both v4 and v6, so I'd rather it not be in
net/ipv4/compat.c. I didn't try without CONFIG_INET, -- will look
at that.

> > +struct compat_group_filter {
> > +       __u32                            gf_interface;
> > +       struct __kernel_sockaddr_storage gf_group
> > +               __attribute__ ((aligned(4)));
> > +       __u32                            gf_fmode;
> > +       __u32                            gf_numsrc;
> > +       struct __kernel_sockaddr_storage gf_slist[1]
> > +               __attribute__ ((aligned(4)));
> > +} __attribute__ ((packed));
> > +
> 
> include/linux/in.n.

        I don't understand this comment. If you're suggesting I
put these in an include file, I didn't because this is the only
file that uses them and I don't want them to be visible to any
other code. If you mean something else, please explain.

> 
> __copy_in_user().
> Maybe we can use __copy_in_user for u32?

        I'll look at it.

                                thanks!

                                        +-DLS



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  4:36                   ` David Stevens
@ 2008-04-26  4:53                     ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-26  5:32                       ` David Miller
  2008-04-26  5:09                     ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-26  5:31                     ` David Miller
  2 siblings, 1 reply; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-26  4:53 UTC (permalink / raw)
  To: dlstevens; +Cc: davem, netdev

In article <OF326F3AF9.7322F5FB-ON88257437.00174874-88257437.00194F7B@us.ibm.com> (at Fri, 25 Apr 2008 21:36:29 -0700), David Stevens <dlstevens@us.ibm.com> says:

> > net/compat.c is not the right place.
> > Please put this in net/ipv4/compat.c or so.
> > Or at least, these must be guarded by CONFIG_INET.
> 
>         This code is for both v4 and v6, so I'd rather it not be in
> net/ipv4/compat.c. I didn't try without CONFIG_INET, -- will look
> at that.

We've been putting ipv4/ipv6 common things in net/ipv4/*.c,
so please put it in net/ipv4/compat.c.

> > > +struct compat_group_filter {
> > > +       __u32                            gf_interface;
> > > +       struct __kernel_sockaddr_storage gf_group
> > > +               __attribute__ ((aligned(4)));
> > > +       __u32                            gf_fmode;
> > > +       __u32                            gf_numsrc;
> > > +       struct __kernel_sockaddr_storage gf_slist[1]
> > > +               __attribute__ ((aligned(4)));
> > > +} __attribute__ ((packed));
> > > +
> > 
> > include/linux/in.n.
> 
>         I don't understand this comment. If you're suggesting I
> put these in an include file, I didn't because this is the only
> file that uses them and I don't want them to be visible to any
> other code. If you mean something else, please explain.

We put most (if not all) compat structures in headers.
Sparse definitions are not good.

--yoshfuji


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  4:36                   ` David Stevens
  2008-04-26  4:53                     ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-26  5:09                     ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-26  5:31                     ` David Miller
  2 siblings, 0 replies; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-26  5:09 UTC (permalink / raw)
  To: dlstevens; +Cc: davem, netdev, yoshfuji

In article <OF326F3AF9.7322F5FB-ON88257437.00174874-88257437.00194F7B@us.ibm.com> (at Fri, 25 Apr 2008 21:36:29 -0700), David Stevens <dlstevens@us.ibm.com> says:

> YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org> wrote on 04/25/2008 
> 08:33:59 PM:
> 
> > Please define __compat_sockaddr_storage{} with attribute((aligned(4))).
> > If you do that we do not need attribute(packed).  No?
> 
>         I just tried it and the compiler seems to do that for this case,
> but the gcc documentation says explicitly that you need both packed and
> aligned if you want it to be a specific value. aligned(4) is a minimum
> only and the compiler would be free to add pad still as long as the
> padded value was also 4-byte aligned.
>         In this case, we want no pad, so packed is the relevant part. If
> had a char followed by a struct as in your previous example, the right
> way to get 4-byte alignment is both aligned(4) and packed. Leaving off
> packed would allow 8-byte or 16-byte (or 12 :-)) alignment, too.

__compat_sockaddr_storage{} is intended for defining **self-containing**
sockaddr_stroage{} aligned like 32bit archs - aligned(4).

Even your code - sockaddr_storage{} might be aligned on 64bit even on
32bit archs.  If the alignment rules of 32bit were different between
32bit and 64bit, most (if not all) compat layers must have been changed.
See?

The attribute((packed)) is overkill and evil.  Please remove that.
Per-member attribute is ugly and the definition is not self-contained.
So, please define __compat_sockaddr_storage{}.

--yoshfuji

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  0:24               ` David Stevens
  2008-04-26  3:33                 ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-26  5:14                 ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-26  5:33                   ` David Miller
  1 sibling, 1 reply; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-26  5:14 UTC (permalink / raw)
  To: dlstevens; +Cc: davem, netdev

In article <OFAF2C4F7A.77CF5A8D-ON88257437.0001566B-88257437.00023C8D@us.ibm.com> (at Fri, 25 Apr 2008 18:24:28 -0600), David Stevens <dlstevens@us.ibm.com> says:

> +       }
> +       if (level == SOL_IPV6)
> +               return ipv6_setsockopt(sock, level, optname, koptval, 
> koptlen);
> +       return ip_setsockopt(sock, level, optname, koptval, koptlen);
> +}
> +

Sorry, you cannot do this.  Build with allmodconfig.

--yoshfuji

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  4:36                   ` David Stevens
  2008-04-26  4:53                     ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-26  5:09                     ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-26  5:31                     ` David Miller
  2008-04-26  7:00                       ` David Stevens
  2 siblings, 1 reply; 39+ messages in thread
From: David Miller @ 2008-04-26  5:31 UTC (permalink / raw)
  To: dlstevens; +Cc: yoshfuji, netdev

From: David Stevens <dlstevens@us.ibm.com>
Date: Fri, 25 Apr 2008 21:36:29 -0700

> YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org> wrote on 04/25/2008 
> 08:33:59 PM:
> 
> > Please define __compat_sockaddr_storage{} with attribute((aligned(4))).
> > If you do that we do not need attribute(packed).  No?
> 
>         I just tried it and the compiler seems to do that for this case,
> but the gcc documentation says explicitly that you need both packed and
> aligned if you want it to be a specific value. aligned(4) is a minimum
> only and the compiler would be free to add pad still as long as the
> padded value was also 4-byte aligned.

Regardless, using packed has several other side effects you absolutely
do not want.  It causes words to be loaded using byte loads on RISC
architectures, because no alignment assumptions can be made at all
about packed objects.

Please use aligned(4), it should be totally sufficient.

>         In this case, we want no pad, so packed is the relevant part. If
> had a char followed by a struct as in your previous example, the right
> way to get 4-byte alignment is both aligned(4) and packed. Leaving off
> packed would allow 8-byte or 16-byte (or 12 :-)) alignment, too.

Have you seen this in practice?

>         I don't understand this comment. If you're suggesting I
> put these in an include file, I didn't because this is the only
> file that uses them and I don't want them to be visible to any
> other code. If you mean something else, please explain.

Right.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  4:53                     ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-26  5:32                       ` David Miller
  2008-04-26  5:56                         ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 1 reply; 39+ messages in thread
From: David Miller @ 2008-04-26  5:32 UTC (permalink / raw)
  To: yoshfuji; +Cc: dlstevens, netdev

From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
Date: Sat, 26 Apr 2008 13:53:37 +0900 (JST)

> In article <OF326F3AF9.7322F5FB-ON88257437.00174874-88257437.00194F7B@us.ibm.com> (at Fri, 25 Apr 2008 21:36:29 -0700), David Stevens <dlstevens@us.ibm.com> says:
> 
> > > net/compat.c is not the right place.
> > > Please put this in net/ipv4/compat.c or so.
> > > Or at least, these must be guarded by CONFIG_INET.
> > 
> >         This code is for both v4 and v6, so I'd rather it not be in
> > net/ipv4/compat.c. I didn't try without CONFIG_INET, -- will look
> > at that.
> 
> We've been putting ipv4/ipv6 common things in net/ipv4/*.c,
> so please put it in net/ipv4/compat.c.

I see no reason not to put this in net/compat.c, it's acting
like fs/compat_ioctl.c which even has networking compat
ioctls implemented there, among other things ;-)

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  5:14                 ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-26  5:33                   ` David Miller
  0 siblings, 0 replies; 39+ messages in thread
From: David Miller @ 2008-04-26  5:33 UTC (permalink / raw)
  To: yoshfuji; +Cc: dlstevens, netdev

From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
Date: Sat, 26 Apr 2008 14:14:54 +0900 (JST)

> In article <OFAF2C4F7A.77CF5A8D-ON88257437.0001566B-88257437.00023C8D@us.ibm.com> (at Fri, 25 Apr 2008 18:24:28 -0600), David Stevens <dlstevens@us.ibm.com> says:
> 
> > +       }
> > +       if (level == SOL_IPV6)
> > +               return ipv6_setsockopt(sock, level, optname, koptval, 
> > koptlen);
> > +       return ip_setsockopt(sock, level, optname, koptval, koptlen);
> > +}
> > +
> 
> Sorry, you cannot do this.  Build with allmodconfig.

Indeed, that will not work.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  5:32                       ` David Miller
@ 2008-04-26  5:56                         ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-26  6:01                           ` David Miller
  0 siblings, 1 reply; 39+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2008-04-26  5:56 UTC (permalink / raw)
  To: davem; +Cc: dlstevens, netdev, yoshfuji

In article <20080425.223236.81693355.davem@davemloft.net> (at Fri, 25 Apr 2008 22:32:36 -0700 (PDT)), David Miller <davem@davemloft.net> says:

> From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
> Date: Sat, 26 Apr 2008 13:53:37 +0900 (JST)
> 
> > In article <OF326F3AF9.7322F5FB-ON88257437.00174874-88257437.00194F7B@us.ibm.com> (at Fri, 25 Apr 2008 21:36:29 -0700), David Stevens <dlstevens@us.ibm.com> says:
> > 
> > > > net/compat.c is not the right place.
> > > > Please put this in net/ipv4/compat.c or so.
> > > > Or at least, these must be guarded by CONFIG_INET.
> > > 
> > >         This code is for both v4 and v6, so I'd rather it not be in
> > > net/ipv4/compat.c. I didn't try without CONFIG_INET, -- will look
> > > at that.
> > 
> > We've been putting ipv4/ipv6 common things in net/ipv4/*.c,
> > so please put it in net/ipv4/compat.c.
> 
> I see no reason not to put this in net/compat.c, it's acting
> like fs/compat_ioctl.c which even has networking compat
> ioctls implemented there, among other things ;-)

Okay

--yoshfuji

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  5:56                         ` YOSHIFUJI Hideaki / 吉藤英明
@ 2008-04-26  6:01                           ` David Miller
  2008-04-26  6:25                             ` David Stevens
  0 siblings, 1 reply; 39+ messages in thread
From: David Miller @ 2008-04-26  6:01 UTC (permalink / raw)
  To: yoshfuji; +Cc: dlstevens, netdev

From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
Date: Sat, 26 Apr 2008 14:56:11 +0900 (JST)

> In article <20080425.223236.81693355.davem@davemloft.net> (at Fri, 25 Apr 2008 22:32:36 -0700 (PDT)), David Miller <davem@davemloft.net> says:
> 
> > From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
> > Date: Sat, 26 Apr 2008 13:53:37 +0900 (JST)
> > 
> > > In article <OF326F3AF9.7322F5FB-ON88257437.00174874-88257437.00194F7B@us.ibm.com> (at Fri, 25 Apr 2008 21:36:29 -0700), David Stevens <dlstevens@us.ibm.com> says:
> > > 
> > > > > net/compat.c is not the right place.
> > > > > Please put this in net/ipv4/compat.c or so.
> > > > > Or at least, these must be guarded by CONFIG_INET.
> > > > 
> > > >         This code is for both v4 and v6, so I'd rather it not be in
> > > > net/ipv4/compat.c. I didn't try without CONFIG_INET, -- will look
> > > > at that.
> > > 
> > > We've been putting ipv4/ipv6 common things in net/ipv4/*.c,
> > > so please put it in net/ipv4/compat.c.
> > 
> > I see no reason not to put this in net/compat.c, it's acting
> > like fs/compat_ioctl.c which even has networking compat
> > ioctls implemented there, among other things ;-)
> 
> Okay

Actually, it is a problem.  I take back what I said.

There is no easy way to call into the ipv6 code from
this built-in code if IPV6 is built modular.

So it really does have to go into ipv4 and ipv6 subdirectories,
respectively.

David, please make this adjustment.  Thanks!

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  6:01                           ` David Miller
@ 2008-04-26  6:25                             ` David Stevens
  2008-04-26  6:31                               ` David Miller
  0 siblings, 1 reply; 39+ messages in thread
From: David Stevens @ 2008-04-26  6:25 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, yoshfuji

OK, what about this:

I considered passing the the real setsockopt function as an argument
to what is now compat_mc_setsockopt() before putting the direct calls in.
By doing that, we can only get in there with a v6 setsockopt when v6 is
loaded, since the calls to it are from the corresponding 
compat_setsockopt.
Is that too ugly?

        Could also make these  return pointers to the new koptval and 
koptlen,
which is really the only thing we need outside the 
compat_ipXX_setsockopt()'s.

                                                +-DLS


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  6:25                             ` David Stevens
@ 2008-04-26  6:31                               ` David Miller
  2008-04-26  7:13                                 ` David Stevens
  0 siblings, 1 reply; 39+ messages in thread
From: David Miller @ 2008-04-26  6:31 UTC (permalink / raw)
  To: dlstevens; +Cc: netdev, yoshfuji

From: David Stevens <dlstevens@us.ibm.com>
Date: Fri, 25 Apr 2008 23:25:39 -0700

> I considered passing the the real setsockopt function as an argument
> to what is now compat_mc_setsockopt() before putting the direct calls in.
> By doing that, we can only get in there with a v6 setsockopt when v6 is
> loaded, since the calls to it are from the corresponding 
> compat_setsockopt.
> Is that too ugly?
> 
>         Could also make these  return pointers to the new koptval and 
> koptlen,
> which is really the only thing we need outside the 
> compat_ipXX_setsockopt()'s.

If you're going to use callbacks, and I'd be also asking you to
guard this code with CONFIG_IPV6 as appropriate, why not simply
put it into the ipv6 socket option handling code?

I really see no value putting this in some generic location
if we have to deal with those issues, by adding callbacks and
ifdef'ery.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  5:31                     ` David Miller
@ 2008-04-26  7:00                       ` David Stevens
  2008-04-26  7:09                         ` David Miller
  0 siblings, 1 reply; 39+ messages in thread
From: David Stevens @ 2008-04-26  7:00 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, netdev-owner, yoshfuji

Dave Miller wrote on 04/25/2008 10:31:30 PM:

> Regardless, using packed has several other side effects you absolutely
> do not want.  It causes words to be loaded using byte loads on RISC
> architectures, because no alignment assumptions can be made at all
> about packed objects.

        Isn't that appropriate in the general case? If the architecture
actually required 8-byte alignment to access the type and we only have
4-byte alignment because the app was compiled with different (32 bit)
alignment rules, then we'd fault if we forced the alignment and accessed
it directly. That's why we don't access it directly. In these, we're
using the (byte) copy functions to get the (totally inappropriately
aligned because it wasn't compiled in 64-bit mode) data to the well
behaved and aligned in-kernel copy of the struct and from there (only)
do we access them as structured objects. If the copy code is clever
enough to use word copies when alignment allows, and byte copies for
the ends, then all we have to care about is that the byte-offset we copy
from is correct for the app-compiled architecture.

        I don't think the point is that important here, because all of 
these
methods identically describe the 32-bit data layout (which is all we
need-- could even use a char array for the source data since we know
the offsets very well!), and we use the copy functions to get it to the
layout we need. So I may be missing your point, but I don't need to 
belabor
it all the same. :-)
        The only reason I didn't want to use aligned(4) for 
sockaddr_storage
is because it's just another single-use declaration. It seems to me plain
old packed is defined by the C language to have the proper offsets on all
architectures for these declarations, and all of these others 
modifications
are no better since they all lead to the same thing. But so would
(char *)optval + 4; as the start of the gr_group, say. We don't use the
compat struct's for anything but a handy way to describe those byte 
offsets.

        Anyway, I belabored. I'll change to aligned(4) with 
compat_sockaddr_storage
declaration...

> 
> Please use aligned(4), it should be totally sufficient.
> 
> >         In this case, we want no pad, so packed is the relevant part. 
If
> > had a char followed by a struct as in your previous example, the right
> > way to get 4-byte alignment is both aligned(4) and packed. Leaving off
> > packed would allow 8-byte or 16-byte (or 12 :-)) alignment, too.
> 
> Have you seen this in practice?

        I did see that plain old structure field __attribute((4))__
wasn't correct (still padded), and gcc docs use the same language
to define the behavior for structure field attribute "aligned" and
type attribute "aligned" (at least with my limited research). So,
it surprised me that the field attribute results in pad and the
type attribute doesn't, and I wonder if it's good to rely on that
difference given the same documentation for both as "minimum alignment".
But if the compiler changes its notion of when to pad this in a way
that breaks it, we can always revisit it later. :-)

                                                                +-DLS

 


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  7:00                       ` David Stevens
@ 2008-04-26  7:09                         ` David Miller
  0 siblings, 0 replies; 39+ messages in thread
From: David Miller @ 2008-04-26  7:09 UTC (permalink / raw)
  To: dlstevens; +Cc: netdev, netdev-owner, yoshfuji

From: David Stevens <dlstevens@us.ibm.com>
Date: Sat, 26 Apr 2008 00:00:49 -0700

>         I did see that plain old structure field __attribute((4))__
> wasn't correct (still padded), and gcc docs use the same language
> to define the behavior for structure field attribute "aligned" and
> type attribute "aligned" (at least with my limited research). So,
> it surprised me that the field attribute results in pad and the
> type attribute doesn't, and I wonder if it's good to rely on that
> difference given the same documentation for both as "minimum alignment".
> But if the compiler changes its notion of when to pad this in a way
> that breaks it, we can always revisit it later. :-)

You're right about this point of course:

--------------------
struct foo {
	int a;
	unsigned long b;
};

struct foo_align4 {
	int a;
	unsigned long b;
} __attribute__((aligned(4)));

int main(void)
{
	printf("Normal: 'b' offset is %Zu\n",
	       __builtin_offsetof(struct foo, b));
	printf("Align4: 'b' offset is %Zu\n",
	       __builtin_offsetof(struct foo_align4, b));
	return 0;
}
--------------------

gives:

--------------------
Normal: 'b' offset is 8
Align4: 'b' offset is 8
--------------------

on sparc64.

So if we need to use packed because of that specific problem here,
fine.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  6:31                               ` David Miller
@ 2008-04-26  7:13                                 ` David Stevens
  2008-04-26  9:07                                   ` David Miller
  0 siblings, 1 reply; 39+ messages in thread
From: David Stevens @ 2008-04-26  7:13 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, netdev-owner, yoshfuji

> If you're going to use callbacks, and I'd be also asking you to
> guard this code with CONFIG_IPV6 as appropriate, why not simply
> put it into the ipv6 socket option handling code?

        Well, I didn't want to make 2 copies of completely identical
code and then just change the protocol setsockopt code we call at
the end of it. If we did this:

compat_ipv6_setsockopt()
{
        if (got an MCAST* optval)
                return compat_mc_setsockopt(....optval, optlen, 
ipv6_setsockopt);
}

and in compat_mc_setsockopt:

int compat_mc_setsockopt(...., func decl) {
        ...all common code that twiddles args for both IPv6 and IPv4
        return func(....);
}

        Then there are no cross references to anything else in the
compat code and we can't get there with func==ipv6_setsockopt unless
we were called from compat_ipv6_setsockopt(), which means we must have
ipv6_setsockopt(). Right? The protocol setsockopt functions are already
external, so I'd expect compat would need any CONFIG guarding. Am I
missing something?

                                                        +-DLS


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  7:13                                 ` David Stevens
@ 2008-04-26  9:07                                   ` David Miller
  0 siblings, 0 replies; 39+ messages in thread
From: David Miller @ 2008-04-26  9:07 UTC (permalink / raw)
  To: dlstevens; +Cc: netdev, netdev-owner, yoshfuji

From: David Stevens <dlstevens@us.ibm.com>
Date: Sat, 26 Apr 2008 00:13:34 -0700

> The protocol setsockopt functions are already external, so I'd
> expect compat would need any CONFIG guarding. Am I missing
> something?

Fair enough, code this up so I can see what it looks like.

Thanks for all of this work David!

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26  3:33                 ` YOSHIFUJI Hideaki / 吉藤英明
  2008-04-26  4:36                   ` David Stevens
@ 2008-04-26 23:55                   ` David Stevens
  2008-04-27  8:06                     ` David Miller
  1 sibling, 1 reply; 39+ messages in thread
From: David Stevens @ 2008-04-26 23:55 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: davem, netdev, yoshfuji

[-- Attachment #1: Type: text/plain, Size: 8833 bytes --]

YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org> wrote on 04/25/2008 
08:33:59 PM:

> __copy_in_user().
> Maybe we can use __copy_in_user for u32?

        __copy_in_user seems to be an alias for copy_from_user
(ie, user-to-kernel), which is totally different from
copy_in_user (user to user).
        It's terrible naming, but I think copy_in_user() is
what we want.

        On aligned(4) and packed, I think the net effect of
the discussion last night is to leave it alone.
        The only change in this version is to replace cross
references of the setsockopt routines with passing those as
an argument from the callers.

                                                +-DLS

Add support on 64-bit kernels for seting 32-bit compatible
MCAST* socket options.

Signed-off-by: David L Stevens <dlstevens@us.ibm.com>

diff -ruNp linux-2.6.18.ppc64/include/net/compat.h 
linux-2.6.18.ppc64DLS2/include/net/compat.h
--- linux-2.6.18.ppc64/include/net/compat.h     2008-04-21 
13:50:33.000000000 -0700
+++ linux-2.6.18.ppc64DLS2/include/net/compat.h 2008-04-26 
04:34:04.000000000 -0700
@@ -39,4 +39,7 @@ extern int put_cmsg_compat(struct msghdr
 
 extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock 
*, unsigned char *, int);
 
+extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, 
int,
+       int (*)(struct sock *, int, int, char __user *, int));
+
 #endif /* NET_COMPAT_H */
diff -ruNp linux-2.6.18.ppc64/net/compat.c 
linux-2.6.18.ppc64DLS2/net/compat.c
--- linux-2.6.18.ppc64/net/compat.c     2008-04-21 13:51:02.000000000 
-0700
+++ linux-2.6.18.ppc64DLS2/net/compat.c 2008-04-26 04:35:22.000000000 
-0700
@@ -26,6 +26,8 @@
 
 #include <net/scm.h>
 #include <net/sock.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
 #include <asm/uaccess.h>
 #include <net/compat.h>
 
@@ -589,6 +591,121 @@ asmlinkage long compat_sys_getsockopt(in
        }
        return err;
 }
+
+struct compat_group_req {
+       __u32                            gr_interface;
+       struct __kernel_sockaddr_storage gr_group
+               __attribute__ ((aligned(4)));
+} __attribute__ ((packed));
+
+struct compat_group_source_req {
+       __u32                            gsr_interface;
+       struct __kernel_sockaddr_storage gsr_group
+               __attribute__ ((aligned(4)));
+       struct __kernel_sockaddr_storage gsr_source
+               __attribute__ ((aligned(4)));
+} __attribute__ ((packed));
+
+struct compat_group_filter {
+       __u32                            gf_interface;
+       struct __kernel_sockaddr_storage gf_group
+               __attribute__ ((aligned(4)));
+       __u32                            gf_fmode;
+       __u32                            gf_numsrc;
+       struct __kernel_sockaddr_storage gf_slist[1]
+               __attribute__ ((aligned(4)));
+} __attribute__ ((packed));
+
+
+int compat_mc_setsockopt(struct sock *sock, int level, int optname,
+       char __user *optval, int optlen,
+       int (*setsockopt)(struct sock *,int,int,char __user *,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 *)optval;
+               struct group_req __user *kgr =
+                       compat_alloc_user_space(sizeof(struct group_req));
+               u32 interface;
+
+               if (!access_ok(VERIFY_READ, gr32, sizeof(*gr32)) ||
+                   !access_ok(VERIFY_WRITE, 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 
*)optval;
+               struct group_source_req *kgsr = compat_alloc_user_space(
+                       sizeof(struct group_source_req));
+               u32 interface;
+
+               if (!access_ok(VERIFY_READ, gsr32, sizeof(*gsr32)) ||
+                   !access_ok(VERIFY_WRITE, 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 *)optval;
+               struct group_filter *kgf;
+               u32 interface, fmode, numsrc;
+
+               if (!access_ok(VERIFY_READ, gf32, sizeof(*gf32)) ||
+                   __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(VERIFY_WRITE, 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);
+
+
 /* Argument list sizes for compat_sys_socketcall */
 #define AL(x) ((x) * sizeof(u32))
 static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
diff -ruNp linux-2.6.18.ppc64/net/ipv4/ip_sockglue.c 
linux-2.6.18.ppc64DLS2/net/ipv4/ip_sockglue.c
--- linux-2.6.18.ppc64/net/ipv4/ip_sockglue.c   2008-04-21 
13:51:00.000000000 -0700
+++ linux-2.6.18.ppc64DLS2/net/ipv4/ip_sockglue.c       2008-04-26 
04:36:24.000000000 -0700
@@ -37,6 +37,7 @@
 #include <linux/mroute.h>
 #include <net/route.h>
 #include <net/xfrm.h>
+#include <net/compat.h>
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #include <net/transp_v6.h>
 #endif
@@ -921,6 +922,10 @@ int compat_ip_setsockopt(struct sock *sk
        if (level != SOL_IP)
                return -ENOPROTOOPT;
 
+       if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
+               return compat_mc_setsockopt(sk, level, optname, optval, 
optlen,
+                       ip_setsockopt);
+
        err = do_ip_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
        /* we need to exclude all possible ENOPROTOOPTs except default 
case */
diff -ruNp linux-2.6.18.ppc64/net/ipv6/ipv6_sockglue.c 
linux-2.6.18.ppc64DLS2/net/ipv6/ipv6_sockglue.c
--- linux-2.6.18.ppc64/net/ipv6/ipv6_sockglue.c 2008-04-21 
13:51:02.000000000 -0700
+++ linux-2.6.18.ppc64DLS2/net/ipv6/ipv6_sockglue.c     2008-04-26 
04:35:19.000000000 -0700
@@ -52,6 +52,7 @@
 #include <net/tcp.h>
 #include <net/udp.h>
 #include <net/xfrm.h>
+#include <net/compat.h>
 
 #include <asm/uaccess.h>
 
@@ -769,6 +770,10 @@ int compat_ipv6_setsockopt(struct sock *
        if (level != SOL_IPV6)
                return -ENOPROTOOPT;
 
+       if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
+               return compat_mc_setsockopt(sk, level, optname, optval, 
optlen,
+                       ipv6_setsockopt);
+
        err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
        /* we need to exclude all possible ENOPROTOOPTs except default 
case */


[-- Attachment #2: setsockopt3.patch --]
[-- Type: application/octet-stream, Size: 6551 bytes --]

diff -ruNp linux-2.6.18.ppc64/include/net/compat.h linux-2.6.18.ppc64DLS2/include/net/compat.h
--- linux-2.6.18.ppc64/include/net/compat.h	2008-04-21 13:50:33.000000000 -0700
+++ linux-2.6.18.ppc64DLS2/include/net/compat.h	2008-04-26 04:34:04.000000000 -0700
@@ -39,4 +39,7 @@ extern int put_cmsg_compat(struct msghdr
 
 extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, unsigned char *, int);
 
+extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, int,
+	int (*)(struct sock *, int, int, char __user *, int));
+
 #endif /* NET_COMPAT_H */
diff -ruNp linux-2.6.18.ppc64/net/compat.c linux-2.6.18.ppc64DLS2/net/compat.c
--- linux-2.6.18.ppc64/net/compat.c	2008-04-21 13:51:02.000000000 -0700
+++ linux-2.6.18.ppc64DLS2/net/compat.c	2008-04-26 04:35:22.000000000 -0700
@@ -26,6 +26,8 @@
 
 #include <net/scm.h>
 #include <net/sock.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
 #include <asm/uaccess.h>
 #include <net/compat.h>
 
@@ -589,6 +591,121 @@ asmlinkage long compat_sys_getsockopt(in
 	}
 	return err;
 }
+
+struct compat_group_req {
+	__u32				 gr_interface;
+	struct __kernel_sockaddr_storage gr_group
+		__attribute__ ((aligned(4)));
+} __attribute__ ((packed));
+
+struct compat_group_source_req {
+	__u32				 gsr_interface;
+	struct __kernel_sockaddr_storage gsr_group
+		__attribute__ ((aligned(4)));
+	struct __kernel_sockaddr_storage gsr_source
+		__attribute__ ((aligned(4)));
+} __attribute__ ((packed));
+
+struct compat_group_filter {
+	__u32				 gf_interface;
+	struct __kernel_sockaddr_storage gf_group
+		__attribute__ ((aligned(4)));
+	__u32				 gf_fmode;
+	__u32				 gf_numsrc;
+	struct __kernel_sockaddr_storage gf_slist[1]
+		__attribute__ ((aligned(4)));
+} __attribute__ ((packed));
+
+
+int compat_mc_setsockopt(struct sock *sock, int level, int optname,
+	char __user *optval, int optlen,
+	int (*setsockopt)(struct sock *,int,int,char __user *,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 *)optval;
+		struct group_req __user *kgr =
+			compat_alloc_user_space(sizeof(struct group_req));
+		u32 interface;
+
+		if (!access_ok(VERIFY_READ, gr32, sizeof(*gr32)) ||
+		    !access_ok(VERIFY_WRITE, 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 *)optval;
+		struct group_source_req *kgsr = compat_alloc_user_space(
+			sizeof(struct group_source_req));
+		u32 interface;
+
+		if (!access_ok(VERIFY_READ, gsr32, sizeof(*gsr32)) ||
+		    !access_ok(VERIFY_WRITE, 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 *)optval;
+		struct group_filter *kgf;
+		u32 interface, fmode, numsrc;
+
+		if (!access_ok(VERIFY_READ, gf32, sizeof(*gf32)) ||
+		    __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(VERIFY_WRITE, 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);
+
+
 /* Argument list sizes for compat_sys_socketcall */
 #define AL(x) ((x) * sizeof(u32))
 static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
diff -ruNp linux-2.6.18.ppc64/net/ipv4/ip_sockglue.c linux-2.6.18.ppc64DLS2/net/ipv4/ip_sockglue.c
--- linux-2.6.18.ppc64/net/ipv4/ip_sockglue.c	2008-04-21 13:51:00.000000000 -0700
+++ linux-2.6.18.ppc64DLS2/net/ipv4/ip_sockglue.c	2008-04-26 04:36:24.000000000 -0700
@@ -37,6 +37,7 @@
 #include <linux/mroute.h>
 #include <net/route.h>
 #include <net/xfrm.h>
+#include <net/compat.h>
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #include <net/transp_v6.h>
 #endif
@@ -921,6 +922,10 @@ int compat_ip_setsockopt(struct sock *sk
 	if (level != SOL_IP)
 		return -ENOPROTOOPT;
 
+	if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
+		return compat_mc_setsockopt(sk, level, optname, optval, optlen,
+			ip_setsockopt);
+
 	err = do_ip_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
 	/* we need to exclude all possible ENOPROTOOPTs except default case */
diff -ruNp linux-2.6.18.ppc64/net/ipv6/ipv6_sockglue.c linux-2.6.18.ppc64DLS2/net/ipv6/ipv6_sockglue.c
--- linux-2.6.18.ppc64/net/ipv6/ipv6_sockglue.c	2008-04-21 13:51:02.000000000 -0700
+++ linux-2.6.18.ppc64DLS2/net/ipv6/ipv6_sockglue.c	2008-04-26 04:35:19.000000000 -0700
@@ -52,6 +52,7 @@
 #include <net/tcp.h>
 #include <net/udp.h>
 #include <net/xfrm.h>
+#include <net/compat.h>
 
 #include <asm/uaccess.h>
 
@@ -769,6 +770,10 @@ int compat_ipv6_setsockopt(struct sock *
 	if (level != SOL_IPV6)
 		return -ENOPROTOOPT;
 
+	if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
+		return compat_mc_setsockopt(sk, level, optname, optval, optlen,
+			ipv6_setsockopt);
+
 	err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
 #ifdef CONFIG_NETFILTER
 	/* we need to exclude all possible ENOPROTOOPTs except default case */

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels.
  2008-04-26 23:55                   ` David Stevens
@ 2008-04-27  8:06                     ` David Miller
  0 siblings, 0 replies; 39+ messages in thread
From: David Miller @ 2008-04-27  8:06 UTC (permalink / raw)
  To: dlstevens; +Cc: yoshfuji, netdev

From: David Stevens <dlstevens@us.ibm.com>
Date: Sat, 26 Apr 2008 17:55:44 -0600

> Add support on 64-bit kernels for seting 32-bit compatible
> MCAST* socket options.
> 
> Signed-off-by: David L Stevens <dlstevens@us.ibm.com>

Ok, this is a lot cleaner than I imagined it might be :-)

Patch applied, thanks everyone!

After this sits for a bit, I'll queue it up to -stable
as well.

Thanks again!

^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2008-04-27  8:06 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-04-24  8:02 [GIT PULL] [IPV6] COMPAT: Fix SSM applications on 64bit kernels YOSHIFUJI Hideaki / 吉藤英明
2008-04-24  8:08 ` David Miller
2008-04-24  8:16   ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-24  8:49     ` David Miller
2008-04-24  9:28       ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-24  9:35         ` David Miller
2008-04-24 18:46     ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-24 20:01       ` David Stevens
2008-04-24 20:43         ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-24 20:59           ` David Stevens
2008-04-24 17:52 ` David Stevens
2008-04-25  5:38   ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-25  6:23     ` David Stevens
2008-04-25  6:27       ` David Miller
2008-04-25  6:31   ` David Miller
2008-04-25  6:37     ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-25  6:47       ` David Miller
2008-04-25  6:59         ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-25  7:04           ` David Miller
2008-04-25  7:24             ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-26  0:24               ` David Stevens
2008-04-26  3:33                 ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-26  4:36                   ` David Stevens
2008-04-26  4:53                     ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-26  5:32                       ` David Miller
2008-04-26  5:56                         ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-26  6:01                           ` David Miller
2008-04-26  6:25                             ` David Stevens
2008-04-26  6:31                               ` David Miller
2008-04-26  7:13                                 ` David Stevens
2008-04-26  9:07                                   ` David Miller
2008-04-26  5:09                     ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-26  5:31                     ` David Miller
2008-04-26  7:00                       ` David Stevens
2008-04-26  7:09                         ` David Miller
2008-04-26 23:55                   ` David Stevens
2008-04-27  8:06                     ` David Miller
2008-04-26  5:14                 ` YOSHIFUJI Hideaki / 吉藤英明
2008-04-26  5:33                   ` David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).