diff options
author | trociny <trociny@FreeBSD.org> | 2011-11-06 10:47:20 +0000 |
---|---|---|
committer | trociny <trociny@FreeBSD.org> | 2011-11-06 10:47:20 +0000 |
commit | f9135967f2f39cdfec7694004e7e3edd6d8663d9 (patch) | |
tree | ad093059413cfaf1624b1628f762fddc52bbc878 /sys/netinet6 | |
parent | ddbde914da94b7822affd2f7a4d229a6df022334 (diff) | |
download | FreeBSD-src-f9135967f2f39cdfec7694004e7e3edd6d8663d9.zip FreeBSD-src-f9135967f2f39cdfec7694004e7e3edd6d8663d9.tar.gz |
Cache SO_REUSEPORT socket option in inpcb-layer in order to avoid
inp_socket->so_options dereference when we may not acquire the lock on
the inpcb.
This fixes the crash due to NULL pointer dereference in
in_pcbbind_setup() when inp_socket->so_options in a pcb returned by
in_pcblookup_local() was checked.
Reported by: dave jones <s.dave.jones@gmail.com>, Arnaud Lacombe <lacombar@gmail.com>
Suggested by: rwatson
Glanced by: rwatson
Tested by: dave jones <s.dave.jones@gmail.com>
Diffstat (limited to 'sys/netinet6')
-rw-r--r-- | sys/netinet6/in6_pcb.c | 14 | ||||
-rw-r--r-- | sys/netinet6/ip6_output.c | 35 |
2 files changed, 38 insertions, 11 deletions
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index b0326bc..d29762d 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -207,8 +207,8 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam, IN6_IS_ADDR_UNSPECIFIED(&t->in6p_faddr)) && (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || - (t->inp_socket->so_options & SO_REUSEPORT) - == 0) && (inp->inp_cred->cr_uid != + (t->inp_flags2 & INP_REUSEPORT) == 0) && + (inp->inp_cred->cr_uid != t->inp_cred->cr_uid)) return (EADDRINUSE); #ifdef INET @@ -267,12 +267,10 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam, INP_IPV6PROTO) == (t->inp_vflag & INP_IPV6PROTO)))) return (EADDRINUSE); - } - else if (t && - (reuseport & t->inp_socket->so_options) - == 0 && (ntohl(t->inp_laddr.s_addr) != - INADDR_ANY || INP_SOCKAF(so) == - INP_SOCKAF(t->inp_socket))) + } else if (t && (reuseport == 0 || + (t->inp_flags2 & INP_REUSEPORT) == 0) && + (ntohl(t->inp_laddr.s_addr) != INADDR_ANY || + (t->inp_vflag & INP_IPV6PROTO) == 0)) return (EADDRINUSE); } #endif diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index e4d5172..5372438 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -1421,7 +1421,38 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt) optval = 0; uproto = (int)so->so_proto->pr_protocol; - if (level == IPPROTO_IPV6) { + if (level != IPPROTO_IPV6) { + error = EINVAL; + + if (sopt->sopt_level == SOL_SOCKET && + sopt->sopt_dir == SOPT_SET) { + switch (sopt->sopt_name) { + case SO_REUSEADDR: + INP_WLOCK(in6p); + if (IN_MULTICAST(ntohl(in6p->inp_laddr.s_addr))) { + if ((so->so_options & + (SO_REUSEADDR | SO_REUSEPORT)) != 0) + in6p->inp_flags2 |= INP_REUSEPORT; + else + in6p->inp_flags2 &= ~INP_REUSEPORT; + } + INP_WUNLOCK(in6p); + error = 0; + break; + case SO_REUSEPORT: + INP_WLOCK(in6p); + if ((so->so_options & SO_REUSEPORT) != 0) + in6p->inp_flags2 |= INP_REUSEPORT; + else + in6p->inp_flags2 &= ~INP_REUSEPORT; + INP_WUNLOCK(in6p); + error = 0; + break; + default: + break; + } + } + } else { /* level == IPPROTO_IPV6 */ switch (op) { case SOPT_SET: @@ -2044,8 +2075,6 @@ do { \ } break; } - } else { /* level != IPPROTO_IPV6 */ - error = EINVAL; } return (error); } |