Author: trociny
Date: Thu Nov 10 20:28:30 2011
New Revision: 227428
URL: http://svn.freebsd.org/changeset/base/227428

Log:
  MFC r227204, 227206, 227207:
  
  r227204:
  
  Fix the typo made in r157474.
  
  r227206:
  
  Before dereferencing intotw() check for NULL, the same way as it is
  done for in_pcb (see r157474).
  
  r227207:
  
  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.jo...@gmail.com>, Arnaud Lacombe 
<lacom...@gmail.com>
  Suggested by: rwatson
  Glanced by:   rwatson
  Tested by:    dave jones <s.dave.jo...@gmail.com>
  
  Approved by:  re (kib)

Modified:
  stable/9/sys/netinet/in_pcb.c
  stable/9/sys/netinet/in_pcb.h
  stable/9/sys/netinet/ip_output.c
  stable/9/sys/netinet6/in6_pcb.c
  stable/9/sys/netinet6/ip6_output.c
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/amd64/include/xen/   (props changed)
  stable/9/sys/boot/   (props changed)
  stable/9/sys/boot/i386/efi/   (props changed)
  stable/9/sys/boot/ia64/efi/   (props changed)
  stable/9/sys/boot/ia64/ski/   (props changed)
  stable/9/sys/boot/powerpc/boot1.chrp/   (props changed)
  stable/9/sys/boot/powerpc/ofw/   (props changed)
  stable/9/sys/cddl/contrib/opensolaris/   (props changed)
  stable/9/sys/conf/   (props changed)
  stable/9/sys/contrib/dev/acpica/   (props changed)
  stable/9/sys/contrib/octeon-sdk/   (props changed)
  stable/9/sys/contrib/pf/   (props changed)
  stable/9/sys/contrib/x86emu/   (props changed)

Modified: stable/9/sys/netinet/in_pcb.c
==============================================================================
--- stable/9/sys/netinet/in_pcb.c       Thu Nov 10 20:15:35 2011        
(r227427)
+++ stable/9/sys/netinet/in_pcb.c       Thu Nov 10 20:28:30 2011        
(r227428)
@@ -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);
@@ -590,19 +589,19 @@ in_pcbbind_setup(struct inpcb *inp, stru
                                 * being in use (for now).  This is better
                                 * than a panic, but not desirable.
                                 */
-                               tw = intotw(inp);
+                               tw = intotw(t);
                                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: stable/9/sys/netinet/in_pcb.h
==============================================================================
--- stable/9/sys/netinet/in_pcb.h       Thu Nov 10 20:15:35 2011        
(r227427)
+++ stable/9/sys/netinet/in_pcb.h       Thu Nov 10 20:28:30 2011        
(r227428)
@@ -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: stable/9/sys/netinet/ip_output.c
==============================================================================
--- stable/9/sys/netinet/ip_output.c    Thu Nov 10 20:15:35 2011        
(r227427)
+++ stable/9/sys/netinet/ip_output.c    Thu Nov 10 20:28:30 2011        
(r227428)
@@ -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: stable/9/sys/netinet6/in6_pcb.c
==============================================================================
--- stable/9/sys/netinet6/in6_pcb.c     Thu Nov 10 20:15:35 2011        
(r227427)
+++ stable/9/sys/netinet6/in6_pcb.c     Thu Nov 10 20:28:30 2011        
(r227428)
@@ -187,6 +187,7 @@ in6_pcbbind(register struct inpcb *inp, 
                }
                if (lport) {
                        struct inpcb *t;
+                       struct tcptw *tw;
 
                        /* GROSS */
                        if (ntohs(lport) <= V_ipport_reservedhigh &&
@@ -206,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
@@ -233,10 +234,21 @@ in6_pcbbind(register struct inpcb *inp, 
                        }
                        t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr,
                            lport, lookupflags, cred);
-                       if (t && (reuseport & ((t->inp_flags & INP_TIMEWAIT) ?
-                           intotw(t)->tw_so_options :
-                           t->inp_socket->so_options)) == 0)
+                       if (t && (t->inp_flags & INP_TIMEWAIT)) {
+                               /*
+                                * XXXRW: If an incpb has had its timewait
+                                * state recycled, we treat the address as
+                                * being in use (for now).  This is better
+                                * than a panic, but not desirable.
+                                */
+                               tw = intotw(t);
+                               if (tw == NULL ||
+                                   (reuseport & tw->tw_so_options) == 0)
+                                       return (EADDRINUSE);
+                       } else if (t && (reuseport & t->inp_socket->so_options)
+                           == 0) {
                                return (EADDRINUSE);
+                       }
 #ifdef INET
                        if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 &&
                            IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
@@ -246,19 +258,19 @@ in6_pcbbind(register struct inpcb *inp, 
                                t = in_pcblookup_local(pcbinfo, sin.sin_addr,
                                    lport, lookupflags, cred);
                                if (t && t->inp_flags & INP_TIMEWAIT) {
-                                       if ((reuseport &
-                                           intotw(t)->tw_so_options) == 0 &&
-                                           (ntohl(t->inp_laddr.s_addr) !=
+                                       tw = intotw(t);
+                                       if (tw == NULL)
+                                               return (EADDRINUSE);
+                                       if ((reuseport & tw->tw_so_options) == 0
+                                           && (ntohl(t->inp_laddr.s_addr) !=
                                             INADDR_ANY || ((inp->inp_vflag &
                                             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: stable/9/sys/netinet6/ip6_output.c
==============================================================================
--- stable/9/sys/netinet6/ip6_output.c  Thu Nov 10 20:15:35 2011        
(r227427)
+++ stable/9/sys/netinet6/ip6_output.c  Thu Nov 10 20:28:30 2011        
(r227428)
@@ -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);
 }
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to