⬢ DragonFlyBSD Kernel Audit
DF-3524 / fix.diff
← back to finding ↓ download raw
DF-3524 is fixed by restoring the callout_stop(9) contract in
sys/kern/kern_timeout.c (the DF-3521-validated fix): an inline-completed
armed stop must report CALLOUT_PREVENTED so ng_uncallout() returns
non-zero and ng_l2cap_lp_untimeout()/ng_l2cap_command_untimeout() stop
misreading successfully-cancelled timers as "already fired".

Validated in-guest on a rebuilt kernel (kernel #1, make nativekernel
RC=0) with STOCK ng7 modules: LP_ConnectCfm accepted (CON_OPEN), L2CAP
echo round-trips, teardown panic gone.

--- 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
+	 * and makes conditional cleanup in consumers (e.g. netgraph
+	 * ng_uncallout -> ng_l2cap_lp_untimeout, which reads 0 as
+	 * "timer already fired" and returns ETIMEDOUT) misbehave: every
+	 * L2CAP LP_ConnectCfm is discarded and the stuck-timeout con
+	 * panics ng_l2cap_free_con at node teardown.
+	 */
+	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);