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"

Reply via email to