6877954 7410C: Kernel panic occurring due to a null pointer dereference in the 'ip' module [2009Q2.5.0]
authorAnders Persson <Anders.Persson@Sun.COM>
Fri, 18 Sep 2009 12:50:18 -0700
changeset 10582 cdf5d98e419e
parent 10581 c2151e45dda5
child 10583 1058268e7f53
6877954 7410C: Kernel panic occurring due to a null pointer dereference in the 'ip' module [2009Q2.5.0]
usr/src/uts/common/inet/tcp/tcp.c
--- a/usr/src/uts/common/inet/tcp/tcp.c	Fri Sep 18 12:43:49 2009 -0700
+++ b/usr/src/uts/common/inet/tcp/tcp.c	Fri Sep 18 12:50:18 2009 -0700
@@ -7834,12 +7834,11 @@
 
 	PRESERVE(tcp->tcp_family);
 	if (tcp->tcp_family == AF_INET6) {
-		tcp->tcp_ipversion = IPV6_VERSION;
 		tcp->tcp_mss = tcps->tcps_mss_def_ipv6;
 	} else {
-		tcp->tcp_ipversion = IPV4_VERSION;
 		tcp->tcp_mss = tcps->tcps_mss_def_ipv4;
 	}
+	PRESERVE(tcp->tcp_ipversion);		/* Init in tcp_init_values */
 
 	tcp->tcp_bound_if = 0;
 	tcp->tcp_ipv6_recvancillary = 0;
@@ -7983,7 +7982,7 @@
 	tcp->tcp_loopback_peer = NULL;
 
 	/* Initialize the header template */
-	if (tcp->tcp_ipversion == IPV4_VERSION) {
+	if (tcp->tcp_family == AF_INET) {
 		err = tcp_header_init_ipv4(tcp);
 	} else {
 		err = tcp_header_init_ipv6(tcp);
@@ -8048,9 +8047,19 @@
 	connp->conn_mlp_type = mlptSingle;
 	connp->conn_ulp_labeled = !is_system_labeled();
 	ASSERT(tcp->tcp_iphc_len >= TCP_MAX_COMBINED_HEADER_LENGTH);
+
+	/*
+	 * tcp_do_get{sock,peer}name constructs the sockaddr from the
+	 * ip header, and decides which header to use based on ip version.
+	 * That operation happens outside the squeue, so we hold the lock
+	 * here to ensure that the ip version and header remain consistent.
+	 */
+	mutex_enter(&connp->conn_lock);
+	tcp->tcp_ipversion = IPV4_VERSION;
 	tcp->tcp_ipha = (ipha_t *)tcp->tcp_iphc;
 	tcp->tcp_ip6h = NULL;
-	tcp->tcp_ipversion = IPV4_VERSION;
+	mutex_exit(&connp->conn_lock);
+
 	tcp->tcp_hdr_len = sizeof (ipha_t) + sizeof (tcph_t);
 	tcp->tcp_tcp_hdr_len = sizeof (tcph_t);
 	tcp->tcp_ip_hdr_len = sizeof (ipha_t);
@@ -8120,12 +8129,21 @@
 	connp->conn_ulp_labeled = !is_system_labeled();
 
 	ASSERT(tcp->tcp_iphc_len >= TCP_MAX_COMBINED_HEADER_LENGTH);
-	tcp->tcp_ipversion = IPV6_VERSION;
 	tcp->tcp_hdr_len = IPV6_HDR_LEN + sizeof (tcph_t);
 	tcp->tcp_tcp_hdr_len = sizeof (tcph_t);
 	tcp->tcp_ip_hdr_len = IPV6_HDR_LEN;
+
+	/*
+	 * tcp_do_get{sock,peer}name constructs the sockaddr from the
+	 * ip header, and decides which header to use based on ip version.
+	 * That operation happens outside the squeue, so we hold the lock
+	 * here to ensure that the ip version and header remain consistent.
+	 */
+	mutex_enter(&connp->conn_lock);
+	tcp->tcp_ipversion = IPV6_VERSION;
 	tcp->tcp_ip6h = (ip6_t *)tcp->tcp_iphc;
 	tcp->tcp_ipha = NULL;
+	mutex_exit(&connp->conn_lock);
 
 	/* Initialize the header template */
 
@@ -17884,12 +17902,14 @@
 		sin6->sin6_family = AF_INET6;
 		if (tcp->tcp_state >= TCPS_BOUND) {
 			sin6->sin6_port = tcp->tcp_lport;
+			mutex_enter(&tcp->tcp_connp->conn_lock);
 			if (tcp->tcp_ipversion == IPV4_VERSION) {
 				IN6_IPADDR_TO_V4MAPPED(tcp->tcp_ipha->ipha_src,
 				    &sin6->sin6_addr);
 			} else {
 				sin6->sin6_addr = tcp->tcp_ip6h->ip6_src;
 			}
+			mutex_exit(&tcp->tcp_connp->conn_lock);
 		}
 		*salenp = sizeof (sin6_t);
 		break;
@@ -17930,10 +17950,12 @@
 		sin6->sin6_family = AF_INET6;
 		sin6->sin6_port = tcp->tcp_fport;
 		sin6->sin6_addr = tcp->tcp_remote_v6;
+		mutex_enter(&tcp->tcp_connp->conn_lock);
 		if (tcp->tcp_ipversion == IPV6_VERSION) {
 			sin6->sin6_flowinfo = tcp->tcp_ip6h->ip6_vcf &
 			    ~IPV6_VERS_AND_FLOW_MASK;
 		}
+		mutex_exit(&tcp->tcp_connp->conn_lock);
 		*salenp = sizeof (sin6_t);
 		break;
 	}
@@ -26090,7 +26112,7 @@
 	in_port_t requested_port;
 	ipaddr_t	v4addr;
 	in6_addr_t	v6addr;
-	uint_t	origipversion;
+	uint_t	ipversion;
 	int	error = 0;
 
 	ASSERT((uintptr_t)len <= (uintptr_t)INT_MAX);
@@ -26104,7 +26126,6 @@
 		}
 		return (-TOUTSTATE);
 	}
-	origipversion = tcp->tcp_ipversion;
 
 	ASSERT(sa != NULL && len != 0);
 
@@ -26132,7 +26153,7 @@
 			return (EAFNOSUPPORT);
 		}
 		requested_port = ntohs(sin->sin_port);
-		tcp->tcp_ipversion = IPV4_VERSION;
+		ipversion = IPV4_VERSION;
 		v4addr = sin->sin_addr.s_addr;
 		IN6_IPADDR_TO_V4MAPPED(v4addr, &v6addr);
 		break;
@@ -26144,7 +26165,7 @@
 			return (EAFNOSUPPORT);
 		}
 		requested_port = ntohs(sin6->sin6_port);
-		tcp->tcp_ipversion = IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ?
+		ipversion = IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ?
 		    IPV4_VERSION : IPV6_VERSION;
 		v6addr = sin6->sin6_addr;
 		break;
@@ -26161,9 +26182,9 @@
 	tcp->tcp_bound_source_v6 = v6addr;
 
 	/* Check for change in ipversion */
-	if (origipversion != tcp->tcp_ipversion) {
+	if (tcp->tcp_ipversion != ipversion) {
 		ASSERT(tcp->tcp_family == AF_INET6);
-		error = tcp->tcp_ipversion == IPV6_VERSION ?
+		error = (ipversion == IPV6_VERSION) ?
 		    tcp_header_init_ipv6(tcp) : tcp_header_init_ipv4(tcp);
 		if (error) {
 			return (ENOMEM);
@@ -27075,14 +27096,12 @@
 				sin = (sin_t *)&addr;
 				*sin = sin_null;
 				sin->sin_family = AF_INET;
-				tcp->tcp_ipversion = IPV4_VERSION;
 			} else {
 				ASSERT(tcp->tcp_family == AF_INET6);
 				len = sizeof (sin6_t);
 				sin6 = (sin6_t *)&addr;
 				*sin6 = sin6_null;
 				sin6->sin6_family = AF_INET6;
-				tcp->tcp_ipversion = IPV6_VERSION;
 			}
 			sa = (struct sockaddr *)&addr;
 		}