Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 10 Nov 2011 20:28:30 +0000 (UTC)
From:      Mikolaj Golub <trociny@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r227428 - in stable/9/sys: netinet netinet6
Message-ID:  <201111102028.pAAKSUTB055094@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: trociny
Date: Thu Nov 10 20:28:30 2011
New Revision: 227428
URL: http://svn.freebsd.org/changeset/base/227428

Log:
  MFC r227204, 227206, 227207:
  
  r227204:
  
  Fix the typo made in r157474.
  
  r227206:
  
  Before dereferencing intotw() check for NULL, the same way as it is
  done for in_pcb (see r157474).
  
  r227207:
  
  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>
  
  Approved by:	re (kib)

Modified:
  stable/9/sys/netinet/in_pcb.c
  stable/9/sys/netinet/in_pcb.h
  stable/9/sys/netinet/ip_output.c
  stable/9/sys/netinet6/in6_pcb.c
  stable/9/sys/netinet6/ip6_output.c
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/amd64/include/xen/   (props changed)
  stable/9/sys/boot/   (props changed)
  stable/9/sys/boot/i386/efi/   (props changed)
  stable/9/sys/boot/ia64/efi/   (props changed)
  stable/9/sys/boot/ia64/ski/   (props changed)
  stable/9/sys/boot/powerpc/boot1.chrp/   (props changed)
  stable/9/sys/boot/powerpc/ofw/   (props changed)
  stable/9/sys/cddl/contrib/opensolaris/   (props changed)
  stable/9/sys/conf/   (props changed)
  stable/9/sys/contrib/dev/acpica/   (props changed)
  stable/9/sys/contrib/octeon-sdk/   (props changed)
  stable/9/sys/contrib/pf/   (props changed)
  stable/9/sys/contrib/x86emu/   (props changed)

Modified: stable/9/sys/netinet/in_pcb.c
==============================================================================
--- stable/9/sys/netinet/in_pcb.c	Thu Nov 10 20:15:35 2011	(r227427)
+++ stable/9/sys/netinet/in_pcb.c	Thu Nov 10 20:28:30 2011	(r227428)
@@ -575,8 +575,7 @@ in_pcbbind_setup(struct inpcb *inp, stru
 				     ntohl(t->inp_faddr.s_addr) == INADDR_ANY) &&
 				    (ntohl(sin->sin_addr.s_addr) != INADDR_ANY ||
 				     ntohl(t->inp_laddr.s_addr) != INADDR_ANY ||
-				     (t->inp_socket->so_options &
-					 SO_REUSEPORT) == 0) &&
+				     (t->inp_flags2 & INP_REUSEPORT) == 0) &&
 				    (inp->inp_cred->cr_uid !=
 				     t->inp_cred->cr_uid))
 					return (EADDRINUSE);
@@ -590,19 +589,19 @@ in_pcbbind_setup(struct inpcb *inp, stru
 				 * being in use (for now).  This is better
 				 * than a panic, but not desirable.
 				 */
-				tw = intotw(inp);
+				tw = intotw(t);
 				if (tw == NULL ||
 				    (reuseport & tw->tw_so_options) == 0)
 					return (EADDRINUSE);
-			} else if (t &&
-			    (reuseport & t->inp_socket->so_options) == 0) {
+			} else if (t && (reuseport == 0 ||
+			    (t->inp_flags2 & INP_REUSEPORT) == 0)) {
 #ifdef INET6
 				if (ntohl(sin->sin_addr.s_addr) !=
 				    INADDR_ANY ||
 				    ntohl(t->inp_laddr.s_addr) !=
 				    INADDR_ANY ||
-				    INP_SOCKAF(so) ==
-				    INP_SOCKAF(t->inp_socket))
+				    (inp->inp_vflag & INP_IPV6PROTO) == 0 ||
+				    (t->inp_vflag & INP_IPV6PROTO) == 0)
 #endif
 				return (EADDRINUSE);
 			}

Modified: stable/9/sys/netinet/in_pcb.h
==============================================================================
--- stable/9/sys/netinet/in_pcb.h	Thu Nov 10 20:15:35 2011	(r227427)
+++ stable/9/sys/netinet/in_pcb.h	Thu Nov 10 20:28:30 2011	(r227428)
@@ -540,6 +540,7 @@ void 	inp_4tuple_get(struct inpcb *inp, 
 #define	INP_LLE_VALID		0x00000001 /* cached lle is valid */	
 #define	INP_RT_VALID		0x00000002 /* cached rtentry is valid */
 #define	INP_PCBGROUPWILD	0x00000004 /* in pcbgroup wildcard list */
+#define	INP_REUSEPORT		0x00000008 /* SO_REUSEPORT option is set */
 
 /*
  * Flags passed to in_pcblookup*() functions.

Modified: stable/9/sys/netinet/ip_output.c
==============================================================================
--- stable/9/sys/netinet/ip_output.c	Thu Nov 10 20:15:35 2011	(r227427)
+++ stable/9/sys/netinet/ip_output.c	Thu Nov 10 20:28:30 2011	(r227428)
@@ -895,12 +895,43 @@ ip_ctloutput(struct socket *so, struct s
 
 	error = optval = 0;
 	if (sopt->sopt_level != IPPROTO_IP) {
-		if ((sopt->sopt_level == SOL_SOCKET) &&
-		    (sopt->sopt_name == SO_SETFIB)) {
-			inp->inp_inc.inc_fibnum = so->so_fibnum;
-			return (0);
+		error = EINVAL;
+
+		if (sopt->sopt_level == SOL_SOCKET &&
+		    sopt->sopt_dir == SOPT_SET) {
+			switch (sopt->sopt_name) {
+			case SO_REUSEADDR:
+				INP_WLOCK(inp);
+				if (IN_MULTICAST(ntohl(inp->inp_laddr.s_addr))) {
+					if ((so->so_options &
+					    (SO_REUSEADDR | SO_REUSEPORT)) != 0)
+						inp->inp_flags2 |= INP_REUSEPORT;
+					else
+						inp->inp_flags2 &= ~INP_REUSEPORT;
+				}
+				INP_WUNLOCK(inp);
+				error = 0;
+				break;
+			case SO_REUSEPORT:
+				INP_WLOCK(inp);
+				if ((so->so_options & SO_REUSEPORT) != 0)
+					inp->inp_flags2 |= INP_REUSEPORT;
+				else
+					inp->inp_flags2 &= ~INP_REUSEPORT;
+				INP_WUNLOCK(inp);
+				error = 0;
+				break;
+			case SO_SETFIB:
+				INP_WLOCK(inp);
+				inp->inp_inc.inc_fibnum = so->so_fibnum;
+				INP_WUNLOCK(inp);
+				error = 0;
+				break;
+			default:
+				break;
+			}
 		}
-		return (EINVAL);
+		return (error);
 	}
 
 	switch (sopt->sopt_dir) {

Modified: stable/9/sys/netinet6/in6_pcb.c
==============================================================================
--- stable/9/sys/netinet6/in6_pcb.c	Thu Nov 10 20:15:35 2011	(r227427)
+++ stable/9/sys/netinet6/in6_pcb.c	Thu Nov 10 20:28:30 2011	(r227428)
@@ -187,6 +187,7 @@ in6_pcbbind(register struct inpcb *inp, 
 		}
 		if (lport) {
 			struct inpcb *t;
+			struct tcptw *tw;
 
 			/* GROSS */
 			if (ntohs(lport) <= V_ipport_reservedhigh &&
@@ -206,8 +207,8 @@ in6_pcbbind(register struct inpcb *inp, 
 				     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
@@ -233,10 +234,21 @@ in6_pcbbind(register struct inpcb *inp, 
 			}
 			t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr,
 			    lport, lookupflags, cred);
-			if (t && (reuseport & ((t->inp_flags & INP_TIMEWAIT) ?
-			    intotw(t)->tw_so_options :
-			    t->inp_socket->so_options)) == 0)
+			if (t && (t->inp_flags & INP_TIMEWAIT)) {
+				/*
+				 * XXXRW: If an incpb has had its timewait
+				 * state recycled, we treat the address as
+				 * being in use (for now).  This is better
+				 * than a panic, but not desirable.
+				 */
+				tw = intotw(t);
+				if (tw == NULL ||
+				    (reuseport & tw->tw_so_options) == 0)
+					return (EADDRINUSE);
+			} else if (t && (reuseport & t->inp_socket->so_options)
+			    == 0) {
 				return (EADDRINUSE);
+			}
 #ifdef INET
 			if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 &&
 			    IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
@@ -246,19 +258,19 @@ in6_pcbbind(register struct inpcb *inp, 
 				t = in_pcblookup_local(pcbinfo, sin.sin_addr,
 				    lport, lookupflags, cred);
 				if (t && t->inp_flags & INP_TIMEWAIT) {
-					if ((reuseport &
-					    intotw(t)->tw_so_options) == 0 &&
-					    (ntohl(t->inp_laddr.s_addr) !=
+					tw = intotw(t);
+					if (tw == NULL)
+						return (EADDRINUSE);
+					if ((reuseport & tw->tw_so_options) == 0
+					    && (ntohl(t->inp_laddr.s_addr) !=
 					     INADDR_ANY || ((inp->inp_vflag &
 					     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

Modified: stable/9/sys/netinet6/ip6_output.c
==============================================================================
--- stable/9/sys/netinet6/ip6_output.c	Thu Nov 10 20:15:35 2011	(r227427)
+++ stable/9/sys/netinet6/ip6_output.c	Thu Nov 10 20:28:30 2011	(r227428)
@@ -1421,7 +1421,38 @@ ip6_ctloutput(struct socket *so, struct 
 	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);
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201111102028.pAAKSUTB055094>