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);
}