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: _______________________________________________ svn-src-head@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-head To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"