DF-3521 / fix.diff
--- a/sys/kern/kern_timeout.c +++ b/sys/kern/kern_timeout.c @@ -885,10 +885,28 @@ * If the operation is still in-progress then re-acquire the spin-lock * and block if necessary. Also initiate the lock cancel. */ - if (sync == 0 || (c->flags & (CALLOUT_INPROG | CALLOUT_SET)) == 0) { + /* + * If the operation completed inline above (the armed callout was + * removed from the wheel and will never execute), report whether + * execution was PREVENTED. Returning 0 for a successfully + * cancelled pending callout violates the callout_stop(9) contract + * ("non-zero if the callout was pending and had not yet run") and + * makes every conditional cleanup (`if (callout_stop(&c)) ...`) + * leak the resources it pairs with the callback -- e.g. netgraph + * ng_pptpgre's rack/sack timer accounting (node refs, + * priv->timers, the timer argument), which permanently pins + * netgraph nodes and node-private data. + */ + if (sync == 0) { exis_drop_gd(gd); return 0; } + if ((c->flags & (CALLOUT_INPROG | CALLOUT_SET)) == 0) { + /* inline-completed stop/cancel: report PREVENTED, not 0 */ + res = ((c->flags & CALLOUT_PREVENTED) != 0); + exis_drop_gd(gd); + return (res); + } if (c->flags & CALLOUT_AUTOLOCK) lockmgr(c->lk, LK_CANCEL_BEG); spin_lock(&c->spin); |