Author: markj
Date: Mon Sep 10 19:00:29 2018
New Revision: 338571
URL: https://svnweb.freebsd.org/changeset/base/338571

Log:
  Fix synchronization of LB group access.
  
  Lookups are protected by an epoch section, so the LB group linkage must
  be a CK_LIST rather than a plain LIST.  Furthermore, we were not
  deferring LB group frees, so in_pcbremlbgrouphash() could race with
  readers and cause a use-after-free.
  
  Reviewed by:  sbruno, Johannes Lundberg <johal...@gmail.com>
  Tested by:    gallatin
  Approved by:  re (gjb)
  Sponsored by: The FreeBSD Foundation
  Differential Revision:        https://reviews.freebsd.org/D17031

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

Modified: head/sys/netinet/in_pcb.c
==============================================================================
--- head/sys/netinet/in_pcb.c   Mon Sep 10 18:59:23 2018        (r338570)
+++ head/sys/netinet/in_pcb.c   Mon Sep 10 19:00:29 2018        (r338571)
@@ -235,18 +235,28 @@ in_pcblbgroup_alloc(struct inpcblbgrouphead *hdr, u_ch
        grp->il_lport = port;
        grp->il_dependladdr = *addr;
        grp->il_inpsiz = size;
-       LIST_INSERT_HEAD(hdr, grp, il_list);
+       CK_LIST_INSERT_HEAD(hdr, grp, il_list);
        return (grp);
 }
 
 static void
-in_pcblbgroup_free(struct inpcblbgroup *grp)
+in_pcblbgroup_free_deferred(epoch_context_t ctx)
 {
+       struct inpcblbgroup *grp;
 
-       LIST_REMOVE(grp, il_list);
+       grp = __containerof(ctx, struct inpcblbgroup, il_epoch_ctx);
        free(grp, M_PCB);
 }
 
+static void
+in_pcblbgroup_free(struct inpcblbgroup *grp)
+{
+
+       CK_LIST_REMOVE(grp, il_list);
+       epoch_call(net_epoch_preempt, &grp->il_epoch_ctx,
+           in_pcblbgroup_free_deferred);
+}
+
 static struct inpcblbgroup *
 in_pcblbgroup_resize(struct inpcblbgrouphead *hdr,
     struct inpcblbgroup *old_grp, int size)
@@ -347,7 +357,7 @@ in_pcbinslbgrouphash(struct inpcb *inp)
        hdr = &pcbinfo->ipi_lbgrouphashbase[
            INP_PCBLBGROUP_PORTHASH(inp->inp_lport,
                pcbinfo->ipi_lbgrouphashmask)];
-       LIST_FOREACH(grp, hdr, il_list) {
+       CK_LIST_FOREACH(grp, hdr, il_list) {
                if (grp->il_vflag == inp->inp_vflag &&
                    grp->il_lport == inp->inp_lport &&
                    memcmp(&grp->il_dependladdr,
@@ -409,7 +419,7 @@ in_pcbremlbgrouphash(struct inpcb *inp)
            INP_PCBLBGROUP_PORTHASH(inp->inp_lport,
                pcbinfo->ipi_lbgrouphashmask)];
 
-       LIST_FOREACH(grp, hdr, il_list) {
+       CK_LIST_FOREACH(grp, hdr, il_list) {
                for (i = 0; i < grp->il_inpcnt; ++i) {
                        if (grp->il_inp[i] != inp)
                                continue;
@@ -1972,7 +1982,7 @@ in_pcblookup_lbgroup(const struct inpcbinfo *pcbinfo,
         * - Load balanced group does not contain IPv4 mapped INET6 wild sockets
         */
        local_wild = NULL;
-       LIST_FOREACH(grp, hdr, il_list) {
+       CK_LIST_FOREACH(grp, hdr, il_list) {
 #ifdef INET6
                if (!(grp->il_vflag & INP_IPV4))
                        continue;

Modified: head/sys/netinet/in_pcb.h
==============================================================================
--- head/sys/netinet/in_pcb.h   Mon Sep 10 18:59:23 2018        (r338570)
+++ head/sys/netinet/in_pcb.h   Mon Sep 10 19:00:29 2018        (r338571)
@@ -70,6 +70,7 @@
  */
 CK_LIST_HEAD(inpcbhead, inpcb);
 CK_LIST_HEAD(inpcbporthead, inpcbport);
+CK_LIST_HEAD(inpcblbgrouphead, inpcblbgroup);
 typedef        uint64_t        inp_gen_t;
 
 /*
@@ -566,7 +567,8 @@ struct inpcbgroup {
  * is dynamically resized as processes bind/unbind to that specific group.
  */
 struct inpcblbgroup {
-       LIST_ENTRY(inpcblbgroup) il_list;
+       CK_LIST_ENTRY(inpcblbgroup) il_list;
+       struct epoch_context il_epoch_ctx;
        uint16_t        il_lport;                       /* (c) */
        u_char          il_vflag;                       /* (c) */
        u_char          il_pad;
@@ -578,7 +580,6 @@ struct inpcblbgroup {
        uint32_t        il_inpcnt; /* cur count in il_inp[] (h) */
        struct inpcb    *il_inp[];                      /* (h) */
 };
-LIST_HEAD(inpcblbgrouphead, inpcblbgroup);
 
 #define INP_LOCK_INIT(inp, d, t) \
        rw_init_flags(&(inp)->inp_lock, (t), RW_RECURSE |  RW_DUPOK)

Modified: head/sys/netinet6/in6_pcb.c
==============================================================================
--- head/sys/netinet6/in6_pcb.c Mon Sep 10 18:59:23 2018        (r338570)
+++ head/sys/netinet6/in6_pcb.c Mon Sep 10 19:00:29 2018        (r338571)
@@ -889,7 +889,7 @@ in6_pcblookup_lbgroup(const struct inpcbinfo *pcbinfo,
         * - Load balanced group does not contain jailed sockets.
         * - Load balanced does not contain IPv4 mapped INET6 wild sockets.
         */
-       LIST_FOREACH(grp, hdr, il_list) {
+       CK_LIST_FOREACH(grp, hdr, il_list) {
 #ifdef INET
                if (!(grp->il_vflag & INP_IPV6))
                        continue;
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to