DF-0597 / fix.diff
diff --git a/sys/netgraph7/pptpgre/ng_pptpgre.c b/sys/netgraph7/pptpgre/ng_pptpgre.c --- a/sys/netgraph7/pptpgre/ng_pptpgre.c +++ b/sys/netgraph7/pptpgre/ng_pptpgre.c @@ -185,6 +185,8 @@ static int ng_pptpgre_xmit(hpriv_p hpriv, item_p item); static void ng_pptpgre_start_send_ack_timer(hpriv_p hpriv); static void ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv); +static void ng_pptpgre_free_session(node_p node, hook_p hook, + void *arg1, int arg2); static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2); static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, @@ -494,8 +496,19 @@ ng_pptpgre_reset(hpriv); LIST_REMOVE(hpriv, sessions); - mtx_uninit(&hpriv->mtx); - kfree(hpriv, M_NETGRAPH); + /* + * Defer the free of hpriv (and its mutex uninit) to a + * WRITER function item enqueued AFTER ng_pptpgre_reset(). + * ng_uncallout() does NOT dequeue an already-dispatched + * callout trampoline (callout_stop() returns 0 in that + * case), so the corresponding ng_pptpgre_*_ack_timeout + * item may already be queued on cpu0's msgport and could + * run after we return. Running it against freed hpriv is + * a use-after-free; queueing the free behind it via + * ng_send_fn (FIFO on the same msgport) guarantees the + * in-flight timer callback observes a live hpriv. + */ + ng_send_fn(node, NULL, ng_pptpgre_free_session, hpriv, 0); } /* Go away if no longer connected to anything */ @@ -970,6 +983,20 @@ } /* + * Deferred session free — runs as a WRITER after any already-dispatched + * timer callback items have drained from the msgport. Pair with the + * ng_send_fn() call in ng_pptpgre_disconnect(). + */ +static void +ng_pptpgre_free_session(node_p node, hook_p hook, void *arg1, int arg2) +{ + hpriv_p hpriv = arg1; + + mtx_uninit(&hpriv->mtx); + kfree(hpriv, M_NETGRAPH); +} + +/* * Return the current time scaled & translated to our internally used format. */ static pptptime_t |