On 16/05/18(Wed) 08:06, Harald Dunkel wrote:
> Hi folks,

Thanks for the report.
 
> hopefully its allowed to repost this message here:
> 
> One gateway running 6.3 ran into the debugger last night. Last words:
> 
> login: kernel: protection fault trap, code=0
> Stopped at      export_sa+0x5c: movl    0(%rcx),%ecx
> ddb{0}> show panic
> the kernel did not panic
> ddb{0}> trace
> export_sa(10,ffff800033445e70) at export_sa+0x5c
> pfkeyv2_expire(ffff8000013d4c00,ffff8000013d4c00) at pfkeyv2_expire+0x14e
> tdb_timeout(ffff800033446020) at tdb_timeout+0x39
> softclock_thread(0) at softclock_thread+0xc6
> end trace frame: 0x0, count: -4
> ddb{0}> show registers
> rdi               0xffff800033445e98
> rsi               0xffff8000013d4c00
> rbp               0xffff800033445e70
> rbx               0xffff800033445e98
> rdx               0xffffffff81abdff0    cpu_info_full_primary+0x1ff0
> rcx               0xdeadbeefdeadbeef
                    ^^^^^^^^^^^^^^^^^^
That means that the TDB has already been freed.  This is possible
because the timeout sleeps on the NET_LOCK().  Diff below should prevent
that by introducing a tdb_reaper() function like we do in other parts of
the stack.

Index: netinet/ip_ipsp.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_ipsp.c,v
retrieving revision 1.229
diff -u -p -r1.229 ip_ipsp.c
--- netinet/ip_ipsp.c   6 Nov 2017 15:12:43 -0000       1.229
+++ netinet/ip_ipsp.c   16 May 2018 08:17:59 -0000
@@ -79,10 +79,11 @@ void tdb_hashstats(void);
 #endif
 
 void           tdb_rehash(void);
-void           tdb_timeout(void *v);
-void           tdb_firstuse(void *v);
-void           tdb_soft_timeout(void *v);
-void           tdb_soft_firstuse(void *v);
+void           tdb_reaper(void *);
+void           tdb_timeout(void *);
+void           tdb_firstuse(void *);
+void           tdb_soft_timeout(void *);
+void           tdb_soft_firstuse(void *);
 int            tdb_hash(u_int, u_int32_t, union sockaddr_union *, u_int8_t);
 
 int ipsec_in_use = 0;
@@ -541,14 +542,13 @@ tdb_timeout(void *v)
 {
        struct tdb *tdb = v;
 
-       if (!(tdb->tdb_flags & TDBF_TIMER))
-               return;
-
        NET_LOCK();
-       /* If it's an "invalid" TDB do a silent expiration. */
-       if (!(tdb->tdb_flags & TDBF_INVALID))
-               pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
-       tdb_delete(tdb);
+       if (tdb->tdb_flags & TDBF_TIMER) {
+               /* If it's an "invalid" TDB do a silent expiration. */
+               if (!(tdb->tdb_flags & TDBF_INVALID))
+                       pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
+               tdb_delete(tdb);
+       }
        NET_UNLOCK();
 }
 
@@ -557,14 +557,13 @@ tdb_firstuse(void *v)
 {
        struct tdb *tdb = v;
 
-       if (!(tdb->tdb_flags & TDBF_SOFT_FIRSTUSE))
-               return;
-
        NET_LOCK();
-       /* If the TDB hasn't been used, don't renew it. */
-       if (tdb->tdb_first_use != 0)
-               pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
-       tdb_delete(tdb);
+       if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) {
+               /* If the TDB hasn't been used, don't renew it. */
+               if (tdb->tdb_first_use != 0)
+                       pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
+               tdb_delete(tdb);
+       }
        NET_UNLOCK();
 }
 
@@ -573,13 +572,12 @@ tdb_soft_timeout(void *v)
 {
        struct tdb *tdb = v;
 
-       if (!(tdb->tdb_flags & TDBF_SOFT_TIMER))
-               return;
-
        NET_LOCK();
-       /* Soft expirations. */
-       pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
-       tdb->tdb_flags &= ~TDBF_SOFT_TIMER;
+       if (tdb->tdb_flags & TDBF_SOFT_TIMER) {
+               /* Soft expirations. */
+               pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
+               tdb->tdb_flags &= ~TDBF_SOFT_TIMER;
+       }
        NET_UNLOCK();
 }
 
@@ -588,14 +586,13 @@ tdb_soft_firstuse(void *v)
 {
        struct tdb *tdb = v;
 
-       if (!(tdb->tdb_flags & TDBF_SOFT_FIRSTUSE))
-               return;
-
        NET_LOCK();
-       /* If the TDB hasn't been used, don't renew it. */
-       if (tdb->tdb_first_use != 0)
-               pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
-       tdb->tdb_flags &= ~TDBF_SOFT_FIRSTUSE;
+       if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) {
+               /* If the TDB hasn't been used, don't renew it. */
+               if (tdb->tdb_first_use != 0)
+                       pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
+               tdb->tdb_flags &= ~TDBF_SOFT_FIRSTUSE;
+       }
        NET_UNLOCK();
 }
 
@@ -841,14 +838,6 @@ tdb_free(struct tdb *tdbp)
                ipo->ipo_last_searched = 0; /* Force a re-search. */
        }
 
-       /* Remove expiration timeouts. */
-       tdbp->tdb_flags &= ~(TDBF_FIRSTUSE | TDBF_SOFT_FIRSTUSE | TDBF_TIMER |
-           TDBF_SOFT_TIMER);
-       timeout_del(&tdbp->tdb_timer_tmo);
-       timeout_del(&tdbp->tdb_first_tmo);
-       timeout_del(&tdbp->tdb_stimer_tmo);
-       timeout_del(&tdbp->tdb_sfirst_tmo);
-
        if (tdbp->tdb_ids) {
                ipsp_ids_free(tdbp->tdb_ids);
                tdbp->tdb_ids = NULL;
@@ -866,6 +855,23 @@ tdb_free(struct tdb *tdbp)
 
        if ((tdbp->tdb_inext) && (tdbp->tdb_inext->tdb_onext == tdbp))
                tdbp->tdb_inext->tdb_onext = NULL;
+
+       /* Remove expiration timeouts. */
+       tdbp->tdb_flags &= ~(TDBF_FIRSTUSE | TDBF_SOFT_FIRSTUSE | TDBF_TIMER |
+           TDBF_SOFT_TIMER);
+       timeout_del(&tdbp->tdb_timer_tmo);
+       timeout_del(&tdbp->tdb_first_tmo);
+       timeout_del(&tdbp->tdb_stimer_tmo);
+       timeout_del(&tdbp->tdb_sfirst_tmo);
+
+       timeout_set_proc(&tdbp->tdb_timer_tmo, tdb_reaper, tdbp);
+       timeout_add(&tdbp->tdb_timer_tmo, 0);
+}
+
+void
+tdb_reaper(void *xtdbp)
+{
+       struct tdb *tdbp = xtdbp;
 
        free(tdbp, M_TDB, 0);
 }

Reply via email to