svn commit: r227207 - in head/sys: netinet netinet6

Mikolaj Golub trociny at FreeBSD.org
Sun Nov 6 10:47:21 UTC 2011


Author: trociny
Date: Sun Nov  6 10:47:20 2011
New Revision: 227207
URL: http://svn.freebsd.org/changeset/base/227207

Log:
  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 at gmail.com>, Arnaud Lacombe <lacombar at gmail.com>
  Suggested by:	rwatson
  Glanced by:	rwatson
  Tested by:	dave jones <s.dave.jones at gmail.com>

Modified:
  head/sys/netinet/in_pcb.c
  head/sys/netinet/in_pcb.h
  head/sys/netinet/ip_output.c
  head/sys/netinet6/in6_pcb.c
  head/sys/netinet6/ip6_output.c

Modified: head/sys/netinet/in_pcb.c
==============================================================================
--- head/sys/netinet/in_pcb.c	Sun Nov  6 09:29:52 2011	(r227206)
+++ head/sys/netinet/in_pcb.c	Sun Nov  6 10:47:20 2011	(r227207)
@@ -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);
@@ -594,15 +593,15 @@ in_pcbbind_setup(struct inpcb *inp, stru
 				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: head/sys/netinet/in_pcb.h
==============================================================================
--- head/sys/netinet/in_pcb.h	Sun Nov  6 09:29:52 2011	(r227206)
+++ head/sys/netinet/in_pcb.h	Sun Nov  6 10:47:20 2011	(r227207)
@@ -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: head/sys/netinet/ip_output.c
==============================================================================
--- head/sys/netinet/ip_output.c	Sun Nov  6 09:29:52 2011	(r227206)
+++ head/sys/netinet/ip_output.c	Sun Nov  6 10:47:20 2011	(r227207)
@@ -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: head/sys/netinet6/in6_pcb.c
==============================================================================
--- head/sys/netinet6/in6_pcb.c	Sun Nov  6 09:29:52 2011	(r227206)
+++ head/sys/netinet6/in6_pcb.c	Sun Nov  6 10:47:20 2011	(r227207)
@@ -207,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
@@ -267,12 +267,10 @@ in6_pcbbind(register struct inpcb *inp, 
 					     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: head/sys/netinet6/ip6_output.c
==============================================================================
--- head/sys/netinet6/ip6_output.c	Sun Nov  6 09:29:52 2011	(r227206)
+++ head/sys/netinet6/ip6_output.c	Sun Nov  6 10:47:20 2011	(r227207)
@@ -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);
 }


More information about the svn-src-all mailing list