Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 2778

kernel-2.6.18-238.el5.src.rpm

From: Jiri Pirko <jpirko@redhat.com>
Date: Wed, 11 Feb 2009 09:26:09 +0100
Subject: [net] ipv6: fix getsockopt for sticky options
Message-id: 20090211082608.GH5829@psychotron.englab.brq.redhat.com
O-Subject: [RHEL5.4 patch] BZ483790 BZ484105 net: ipv6: Fix getsockopt for sticky options.
Bugzilla: 484105 483790
RH-Acked-by: Neil Horman <nhorman@redhat.com>
RH-Acked-by: David Miller <davem@redhat.com>
RH-Acked-by: Thomas Graf <tgraf@redhat.com>

BZ483790
BZ484105
https://bugzilla.redhat.com/show_bug.cgi?id=483790
https://bugzilla.redhat.com/show_bug.cgi?id=484105

This patch is a backport of following two upstream patches:

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=05335c2220c4911b69cb1bdd79e603ab08088372

From: Yang Hongyang <yanghy@cn.fujitsu.com>
Date: Wed, 28 May 2008 08:23:47 +0000 (+0800)

	[IPV6]: Fix the return value of get destination options with NULL data pointer

	If we pass NULL data buffer to getsockopt(), it will return 0,
	and the option length is set to -EFAULT:
	    getsockopt(sk, IPPROTO_IPV6, IPV6_DSTOPTS, NULL, &len);

	This is because ipv6_getsockopt_sticky() will return -EFAULT or
	-EINVAL if some error occur.

	This patch fix this problem.

	Signed-off-by: Yang Hongyang <yanghy@cn.fujitsu.com>
	Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index a5f7226..f689c24 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -806,13 +806,32 @@ EXPORT_SYMBOL(compat_ipv6_setsockopt);
 #endif
 
 static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
-				  char __user *optval, int len)
+				  int optname, char __user *optval, int len)
 {
 	struct ipv6_opt_hdr *hdr;
 
-	if (!opt || !opt->hopopt)
+	if (!opt)
+		return 0;
+
+	switch(optname) {
+	case IPV6_HOPOPTS:
+		hdr = opt->hopopt;
+		break;
+	case IPV6_RTHDRDSTOPTS:
+		hdr = opt->dst0opt;
+		break;
+	case IPV6_RTHDR:
+		hdr = (struct ipv6_opt_hdr *)opt->srcrt;
+		break;
+	case IPV6_DSTOPTS:
+		hdr = opt->dst1opt;
+		break;
+	default:
+		return -EINVAL;	/* should not happen */
+	}
+
+	if (!hdr)
 		return 0;
-	hdr = opt->hopopt;
 
 	len = min_t(int, len, ipv6_optlen(hdr));
 	if (copy_to_user(optval, hdr, ipv6_optlen(hdr)))
@@ -954,8 +973,11 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
 
 		lock_sock(sk);
 		len = ipv6_getsockopt_sticky(sk, np->opt,
-					     optval, len);
+					     optname, optval, len);
 		release_sock(sk);
+		/* check if ipv6_getsockopt_sticky() returns err code */
+		if (len < 0)
+			return len;
 		return put_user(len, optlen);
 	}