Author: trociny
Date: Thu Jul  4 18:38:00 2013
New Revision: 252710
URL: http://svnweb.freebsd.org/changeset/base/252710

Log:
  In r227207, to fix the issue with possible NULL inp_socket pointer
  dereferencing, when checking for SO_REUSEPORT option (and SO_REUSEADDR
  for multicast), INP_REUSEPORT flag was introduced to cache the socket
  option.  It was decided then that one flag would be enough to cache
  both SO_REUSEPORT and SO_REUSEADDR: when processing SO_REUSEADDR
  setsockopt(2), it was checked if it was called for a multicast address
  and INP_REUSEPORT was set accordingly.
  
  Unfortunately that approach does not work when setsockopt(2) is called
  before binding to a multicast address: the multicast check fails and
  INP_REUSEPORT is not set.
  
  Fix this by adding INP_REUSEADDR flag to unconditionally cache
  SO_REUSEADDR.
  
  PR:           179901
  Submitted by: Michael Gmelin freebsd grem.de (initial version)
  Reviewed by:  rwatson
  MFC after:    1 week

Modified:
  head/sys/netinet/in_pcb.c
  head/sys/netinet/in_pcb.h
  head/sys/netinet/ip_output.c
  head/sys/netinet6/in6_pcb.c
  head/sys/netinet6/ip6_output.c

Modified: head/sys/netinet/in_pcb.c
==============================================================================
--- head/sys/netinet/in_pcb.c   Thu Jul  4 18:00:27 2013        (r252709)
+++ head/sys/netinet/in_pcb.c   Thu Jul  4 18:38:00 2013        (r252710)
@@ -467,6 +467,23 @@ in_pcb_lport(struct inpcb *inp, struct i
 
        return (0);
 }
+
+/*
+ * Return cached socket options.
+ */
+short
+inp_so_options(const struct inpcb *inp)
+{
+   short so_options;
+
+   so_options = 0;
+
+   if ((inp->inp_flags2 & INP_REUSEPORT) != 0)
+          so_options |= SO_REUSEPORT;
+   if ((inp->inp_flags2 & INP_REUSEADDR) != 0)
+          so_options |= SO_REUSEADDR;
+   return (so_options);
+}
 #endif /* INET || INET6 */
 
 #ifdef INET
@@ -595,8 +612,7 @@ in_pcbbind_setup(struct inpcb *inp, stru
                                if (tw == NULL ||
                                    (reuseport & tw->tw_so_options) == 0)
                                        return (EADDRINUSE);
-                       } else if (t && (reuseport == 0 ||
-                           (t->inp_flags2 & INP_REUSEPORT) == 0)) {
+                       } else if (t && (reuseport & inp_so_options(t)) == 0) {
 #ifdef INET6
                                if (ntohl(sin->sin_addr.s_addr) !=
                                    INADDR_ANY ||

Modified: head/sys/netinet/in_pcb.h
==============================================================================
--- head/sys/netinet/in_pcb.h   Thu Jul  4 18:00:27 2013        (r252709)
+++ head/sys/netinet/in_pcb.h   Thu Jul  4 18:38:00 2013        (r252710)
@@ -442,6 +442,7 @@ struct tcpcb *
        inp_inpcbtotcpcb(struct inpcb *inp);
 void   inp_4tuple_get(struct inpcb *inp, uint32_t *laddr, uint16_t *lp,
                uint32_t *faddr, uint16_t *fp);
+short  inp_so_options(const struct inpcb *inp);
 
 #endif /* _KERNEL */
 
@@ -543,6 +544,7 @@ void        inp_4tuple_get(struct inpcb *inp, 
 #define        INP_PCBGROUPWILD        0x00000004 /* in pcbgroup wildcard list 
*/
 #define        INP_REUSEPORT           0x00000008 /* SO_REUSEPORT option is 
set */
 #define        INP_FREED               0x00000010 /* inp itself is not valid */
+#define        INP_REUSEADDR           0x00000020 /* SO_REUSEADDR option is 
set */
 
 /*
  * Flags passed to in_pcblookup*() functions.

Modified: head/sys/netinet/ip_output.c
==============================================================================
--- head/sys/netinet/ip_output.c        Thu Jul  4 18:00:27 2013        
(r252709)
+++ head/sys/netinet/ip_output.c        Thu Jul  4 18:38:00 2013        
(r252710)
@@ -900,13 +900,10 @@ ip_ctloutput(struct socket *so, struct s
                        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;
-                               }
+                               if ((so->so_options & SO_REUSEADDR) != 0)
+                                       inp->inp_flags2 |= INP_REUSEADDR;
+                               else
+                                       inp->inp_flags2 &= ~INP_REUSEADDR;
                                INP_WUNLOCK(inp);
                                error = 0;
                                break;

Modified: head/sys/netinet6/in6_pcb.c
==============================================================================
--- head/sys/netinet6/in6_pcb.c Thu Jul  4 18:00:27 2013        (r252709)
+++ head/sys/netinet6/in6_pcb.c Thu Jul  4 18:38:00 2013        (r252710)
@@ -243,8 +243,7 @@ in6_pcbbind(register struct inpcb *inp, 
                                if (tw == NULL ||
                                    (reuseport & tw->tw_so_options) == 0)
                                        return (EADDRINUSE);
-                       } else if (t && (reuseport == 0 ||
-                           (t->inp_flags2 & INP_REUSEPORT) == 0)) {
+                       } else if (t && (reuseport & inp_so_options(t)) == 0) {
                                return (EADDRINUSE);
                        }
 #ifdef INET
@@ -265,8 +264,8 @@ in6_pcbbind(register struct inpcb *inp, 
                                             INP_IPV6PROTO) ==
                                             (t->inp_vflag & INP_IPV6PROTO))))
                                                return (EADDRINUSE);
-                               } else if (t && (reuseport == 0 ||
-                                   (t->inp_flags2 & INP_REUSEPORT) == 0) &&
+                               } else if (t &&
+                                   (reuseport & inp_so_options(t)) == 0 &&
                                    (ntohl(t->inp_laddr.s_addr) != INADDR_ANY ||
                                    (t->inp_vflag & INP_IPV6PROTO) != 0))
                                        return (EADDRINUSE);

Modified: head/sys/netinet6/ip6_output.c
==============================================================================
--- head/sys/netinet6/ip6_output.c      Thu Jul  4 18:00:27 2013        
(r252709)
+++ head/sys/netinet6/ip6_output.c      Thu Jul  4 18:38:00 2013        
(r252710)
@@ -1477,13 +1477,10 @@ ip6_ctloutput(struct socket *so, struct 
                        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;
-                               }
+                               if ((so->so_options & SO_REUSEADDR) != 0)
+                                       in6p->inp_flags2 |= INP_REUSEADDR;
+                               else
+                                       in6p->inp_flags2 &= ~INP_REUSEADDR;
                                INP_WUNLOCK(in6p);
                                error = 0;
                                break;
_______________________________________________
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