Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 21 Jul 2015 09:54:32 +0000 (UTC)
From:      Randall Stewart <rrs@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r285740 - in head/sys: netinet netinet6
Message-ID:  <201507210954.t6L9sWtm073695@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rrs
Date: Tue Jul 21 09:54:31 2015
New Revision: 285740
URL: https://svnweb.freebsd.org/changeset/base/285740

Log:
  When a tunneling protocol is being used with UDP we must release the
  lock on the INP before calling the tunnel protocol, else a LOR
  may occur (it does with SCTP for sure). Instead we must acquire a
  ref count and release the lock, taking care to allow for the case
  where the UDP socket has gone away and *not* unlocking since the
  refcnt decrement on the inp will do the unlock in that case.
  
  Reviewed by:	tuexen
  MFC after:	3 weeks

Modified:
  head/sys/netinet/udp_usrreq.c
  head/sys/netinet6/udp6_usrreq.c

Modified: head/sys/netinet/udp_usrreq.c
==============================================================================
--- head/sys/netinet/udp_usrreq.c	Tue Jul 21 09:44:45 2015	(r285739)
+++ head/sys/netinet/udp_usrreq.c	Tue Jul 21 09:54:31 2015	(r285740)
@@ -293,8 +293,17 @@ udplite_destroy(void)
  * contains the source address.  If the socket ends up being an IPv6 socket,
  * udp_append() will convert to a sockaddr_in6 before passing the address
  * into the socket code.
+ *
+ * In the normal case udp_append() will return 0, indicating that you
+ * must unlock the inp. However if a tunneling protocol is in place we increment
+ * the inpcb refcnt and unlock the inp, on return from the tunneling protocol we
+ * then decrement the reference count. If the inp_rele returns 1, indicating the
+ * inp is gone, we return that to the caller to tell them *not* to unlock
+ * the inp. In the case of multi-cast this will cause the distribution
+ * to stop (though most tunneling protocols known currently do *not* use
+ * multicast).
  */
-static void
+static int
 udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off,
     struct sockaddr_in *udp_in)
 {
@@ -313,9 +322,12 @@ udp_append(struct inpcb *inp, struct ip 
 	 */
 	up = intoudpcb(inp);
 	if (up->u_tun_func != NULL) {
+		in_pcbref(inp);
+		INP_RUNLOCK(inp);
 		(*up->u_tun_func)(n, off, inp, (struct sockaddr *)udp_in,
 		    up->u_tun_ctx);
-		return;
+		INP_RLOCK(inp);
+		return (in_pcbrele_rlocked(inp));
 	}
 
 	off += sizeof(struct udphdr);
@@ -324,7 +336,7 @@ udp_append(struct inpcb *inp, struct ip 
 	/* Check AH/ESP integrity. */
 	if (ipsec4_in_reject(n, inp)) {
 		m_freem(n);
-		return;
+		return (0);
 	}
 #ifdef IPSEC_NAT_T
 	up = intoudpcb(inp);
@@ -332,14 +344,14 @@ udp_append(struct inpcb *inp, struct ip 
 	if (up->u_flags & UF_ESPINUDP_ALL) {	/* IPSec UDP encaps. */
 		n = udp4_espdecap(inp, n, off);
 		if (n == NULL)				/* Consumed. */
-			return;
+			return (0);
 	}
 #endif /* IPSEC_NAT_T */
 #endif /* IPSEC */
 #ifdef MAC
 	if (mac_inpcb_check_deliver(inp, n) != 0) {
 		m_freem(n);
-		return;
+		return (0);
 	}
 #endif /* MAC */
 	if (inp->inp_flags & INP_CONTROLOPTS ||
@@ -373,6 +385,7 @@ udp_append(struct inpcb *inp, struct ip 
 		UDPSTAT_INC(udps_fullsock);
 	} else
 		sorwakeup_locked(so);
+	return (0);
 }
 
 int
@@ -579,8 +592,10 @@ udp_input(struct mbuf **mp, int *offp, i
 				if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
 					UDP_PROBE(receive, NULL, last, ip,
 					    last, uh);
-					udp_append(last, ip, n, iphlen,
-					    &udp_in);
+					if (udp_append(last, ip, n, iphlen,
+						&udp_in)) {
+						goto inp_lost;
+					}
 				}
 				INP_RUNLOCK(last);
 			}
@@ -611,8 +626,9 @@ udp_input(struct mbuf **mp, int *offp, i
 			goto badunlocked;
 		}
 		UDP_PROBE(receive, NULL, last, ip, last, uh);
-		udp_append(last, ip, m, iphlen, &udp_in);
-		INP_RUNLOCK(last);
+		if (udp_append(last, ip, m, iphlen, &udp_in) == 0) 
+			INP_RUNLOCK(last);
+	inp_lost:
 		INP_INFO_RUNLOCK(pcbinfo);
 		return (IPPROTO_DONE);
 	}
@@ -700,8 +716,8 @@ udp_input(struct mbuf **mp, int *offp, i
 	}
 
 	UDP_PROBE(receive, NULL, inp, ip, inp, uh);
-	udp_append(inp, ip, m, iphlen, &udp_in);
-	INP_RUNLOCK(inp);
+	if (udp_append(inp, ip, m, iphlen, &udp_in) == 0) 
+		INP_RUNLOCK(inp);
 	return (IPPROTO_DONE);
 
 badunlocked:

Modified: head/sys/netinet6/udp6_usrreq.c
==============================================================================
--- head/sys/netinet6/udp6_usrreq.c	Tue Jul 21 09:44:45 2015	(r285739)
+++ head/sys/netinet6/udp6_usrreq.c	Tue Jul 21 09:54:31 2015	(r285740)
@@ -136,7 +136,7 @@ __FBSDID("$FreeBSD$");
 extern struct protosw	inetsw[];
 static void		udp6_detach(struct socket *so);
 
-static void
+static int
 udp6_append(struct inpcb *inp, struct mbuf *n, int off,
     struct sockaddr_in6 *fromsa)
 {
@@ -151,21 +151,24 @@ udp6_append(struct inpcb *inp, struct mb
 	 */
 	up = intoudpcb(inp);
 	if (up->u_tun_func != NULL) {
+		in_pcbref(inp);
+		INP_RUNLOCK(inp);
 		(*up->u_tun_func)(n, off, inp, (struct sockaddr *)fromsa,
 		    up->u_tun_ctx);
-		return;
+		INP_RLOCK(inp);
+		return (in_pcbrele_rlocked(inp));
 	}
 #ifdef IPSEC
 	/* Check AH/ESP integrity. */
 	if (ipsec6_in_reject(n, inp)) {
 		m_freem(n);
-		return;
+		return (0);
 	}
 #endif /* IPSEC */
 #ifdef MAC
 	if (mac_inpcb_check_deliver(inp, n) != 0) {
 		m_freem(n);
-		return;
+		return (0);
 	}
 #endif
 	opts = NULL;
@@ -185,6 +188,7 @@ udp6_append(struct inpcb *inp, struct mb
 		UDPSTAT_INC(udps_fullsock);
 	} else
 		sorwakeup_locked(so);
+	return (0);
 }
 
 int
@@ -367,7 +371,8 @@ udp6_input(struct mbuf **mp, int *offp, 
 					INP_RLOCK(last);
 					UDP_PROBE(receive, NULL, last, ip6,
 					    last, uh);
-					udp6_append(last, n, off, &fromsa);
+					if (udp6_append(last, n, off, &fromsa))
+						goto inp_lost;
 					INP_RUNLOCK(last);
 				}
 			}
@@ -398,8 +403,9 @@ udp6_input(struct mbuf **mp, int *offp, 
 		INP_RLOCK(last);
 		INP_INFO_RUNLOCK(pcbinfo);
 		UDP_PROBE(receive, NULL, last, ip6, last, uh);
-		udp6_append(last, m, off, &fromsa);
-		INP_RUNLOCK(last);
+		if (udp6_append(last, m, off, &fromsa)) 
+			INP_RUNLOCK(last);
+	inp_lost:
 		return (IPPROTO_DONE);
 	}
 	/*
@@ -477,8 +483,8 @@ udp6_input(struct mbuf **mp, int *offp, 
 		}
 	}
 	UDP_PROBE(receive, NULL, inp, ip6, inp, uh);
-	udp6_append(inp, m, off, &fromsa);
-	INP_RUNLOCK(inp);
+	if (udp6_append(inp, m, off, &fromsa) == 0)
+		INP_RUNLOCK(inp);
 	return (IPPROTO_DONE);
 
 badheadlocked:



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