Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/net/npf - npf_conn_establish: remove a rare race conditi...



details:   https://anonhg.NetBSD.org/src/rev/02b7e9aa635a
branches:  trunk
changeset: 335952:02b7e9aa635a
user:      rmind <rmind%NetBSD.org@localhost>
date:      Sun Feb 01 22:41:22 2015 +0000

description:
- npf_conn_establish: remove a rare race condition when we might destroy a
  connection when it is still referenced by another thread.
- npf_conn_destroy: remove the backwards entry using the saved key, PR/49488.
- Sprinkle some asserts.

diffstat:

 sys/net/npf/npf_conn.c |  74 +++++++++++++++++++++++++++++++++----------------
 1 files changed, 49 insertions(+), 25 deletions(-)

diffs (169 lines):

diff -r 217a46deb99a -r 02b7e9aa635a sys/net/npf/npf_conn.c
--- a/sys/net/npf/npf_conn.c    Sun Feb 01 19:32:59 2015 +0000
+++ b/sys/net/npf/npf_conn.c    Sun Feb 01 22:41:22 2015 +0000
@@ -1,7 +1,7 @@
-/*     $NetBSD: npf_conn.c,v 1.14 2014/12/20 16:19:43 rmind Exp $      */
+/*     $NetBSD: npf_conn.c,v 1.15 2015/02/01 22:41:22 rmind Exp $      */
 
 /*-
- * Copyright (c) 2014 Mindaugas Rasiukevicius <rmind at netbsd org>
+ * Copyright (c) 2014-2015 Mindaugas Rasiukevicius <rmind at netbsd org>
  * Copyright (c) 2010-2014 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
@@ -99,7 +99,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.14 2014/12/20 16:19:43 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.15 2015/02/01 22:41:22 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -369,7 +369,6 @@
        /* Check if connection is active and not expired. */
        flags = con->c_flags;
        ok = (flags & (CONN_ACTIVE | CONN_EXPIRE)) == CONN_ACTIVE;
-
        if (__predict_false(!ok)) {
                atomic_dec_uint(&con->c_refcnt);
                return NULL;
@@ -453,6 +452,7 @@
 {
        const nbuf_t *nbuf = npc->npc_nbuf;
        npf_conn_t *con;
+       int error = 0;
 
        KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
 
@@ -468,16 +468,16 @@
        NPF_PRINTF(("NPF: create conn %p\n", con));
        npf_stats_inc(NPF_STAT_CONN_CREATE);
 
-       /* Reference count and flags (indicate direction). */
        mutex_init(&con->c_lock, MUTEX_DEFAULT, IPL_SOFTNET);
        con->c_flags = (di & PFIL_ALL);
-       con->c_refcnt = 1;
+       con->c_refcnt = 0;
        con->c_rproc = NULL;
        con->c_nat = NULL;
 
-       /* Initialize protocol state. */
+       /* Initialize the protocol state. */
        if (!npf_state_init(npc, &con->c_state)) {
-               goto err;
+               npf_conn_destroy(con);
+               return NULL;
        }
 
        KASSERT(npf_iscached(npc, NPC_IP46));
@@ -488,45 +488,65 @@
         * Construct "forwards" and "backwards" keys.  Also, set the
         * interface ID for this connection (unless it is global).
         */
-       if (!npf_conn_conkey(npc, fw, true)) {
-               goto err;
-       }
-       if (!npf_conn_conkey(npc, bk, false)) {
-               goto err;
+       if (!npf_conn_conkey(npc, fw, true) ||
+           !npf_conn_conkey(npc, bk, false)) {
+               npf_conn_destroy(con);
+               return NULL;
        }
        fw->ck_backptr = bk->ck_backptr = con;
        con->c_ifid = per_if ? nbuf->nb_ifid : 0;
        con->c_proto = npc->npc_proto;
 
-       /* Set last activity time for a new connection. */
+       /*
+        * Set last activity time for a new connection and acquire
+        * a reference for the caller before we make it visible.
+        */
        getnanouptime(&con->c_atime);
+       con->c_refcnt = 1;
 
        /*
         * Insert both keys (entries representing directions) of the
-        * connection.  At this point, it becomes visible.
+        * connection.  At this point it becomes visible, but we activate
+        * the connection later.
         */
+       mutex_enter(&con->c_lock);
        if (!npf_conndb_insert(conn_db, fw, con)) {
+               error = EISCONN;
                goto err;
        }
        if (!npf_conndb_insert(conn_db, bk, con)) {
-               /* We have hit the duplicate. */
-               npf_conndb_remove(conn_db, fw);
+               npf_conn_t *ret __diagused;
+               ret = npf_conndb_remove(conn_db, fw);
+               KASSERT(ret == con);
+               error = EISCONN;
+               goto err;
+       }
+err:
+       /*
+        * If we have hit the duplicate: mark the connection as expired
+        * and let the G/C thread to take care of it.  We cannot do it
+        * here since there might be references acquired already.
+        */
+       if (error) {
+               const u_int dflags = CONN_REMOVED | CONN_EXPIRE;
+               atomic_or_uint(&con->c_flags, dflags);
                npf_stats_inc(NPF_STAT_RACE_CONN);
-               goto err;
+       } else {
+               NPF_PRINTF(("NPF: establish conn %p\n", con));
        }
 
        /* Finally, insert into the connection list. */
-       NPF_PRINTF(("NPF: establish conn %p\n", con));
        npf_conndb_enqueue(conn_db, con);
-       return con;
-err:
-       npf_conn_destroy(con);
-       return NULL;
+       mutex_exit(&con->c_lock);
+
+       return error ? NULL : con;
 }
 
 static void
 npf_conn_destroy(npf_conn_t *con)
 {
+       KASSERT(con->c_refcnt == 0);
+
        if (con->c_nat) {
                /* Release any NAT structures. */
                npf_nat_destroy(con->c_nat);
@@ -582,6 +602,8 @@
                mutex_exit(&con->c_lock);
                return EINVAL;
        }
+       KASSERT((con->c_flags & CONN_REMOVED) == 0);
+
        if (__predict_false(con->c_nat != NULL)) {
                /* Race with a duplicate packet. */
                mutex_exit(&con->c_lock);
@@ -590,7 +612,7 @@
        }
 
        /* Remove the "backwards" entry. */
-       ret = npf_conndb_remove(conn_db, &key);
+       ret = npf_conndb_remove(conn_db, &con->c_back_entry);
        KASSERT(ret == con);
 
        /* Set the source/destination IDs to the translation values. */
@@ -606,7 +628,9 @@
                 * Race: we have hit the duplicate, remove the "forwards"
                 * entry and expire our connection; it is no longer valid.
                 */
-               (void)npf_conndb_remove(conn_db, &con->c_forw_entry);
+               ret = npf_conndb_remove(conn_db, &con->c_forw_entry);
+               KASSERT(ret == con);
+
                atomic_or_uint(&con->c_flags, CONN_REMOVED | CONN_EXPIRE);
                mutex_exit(&con->c_lock);
 



Home | Main Index | Thread Index | Old Index