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); }