⬢ DragonFlyBSD Kernel Audit
DF-3528 / fix.diff
← back to finding ↓ download raw
DF-3528 fix = the already-validated DF-3523 layer-2 fix (ng_uncallout must
actually free the callout item) PLUS freeing the orphaned previous item on
rearm. Apply together with the kern_timeout contract fix (DF-3521/DF-3515,
kern_timeout.diff in findings/poc/DF-3523/) — each layer alone was measured
insufficient in-guest by DF-3523.

For ng_car.c itself the actionable change is a port to DragonFly primitives
(bintime -> systimer/tvtohz or binuptime shim, mtx -> spinlock/lockmgr) or
deletion of the dead file; the leak is in ng_base.c, not in ng_car.c.

--- a/sys/netgraph7/netgraph/ng_base.c
+++ b/sys/netgraph7/netgraph/ng_base.c
@@ -3263,6 +3263,12 @@ ng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
 	NGI_FN(item) = fn;
 	NGI_ARG1(item) = arg1;
 	NGI_ARG2(item) = arg2;
+	/*
+	 * If the callout was still armed with another item, that item (and
+	 * its node/hook references) would be orphaned by the reset below.
+	 * DF-3528/DF-3523: free it now.  ng_free_item() releases the
+	 * NG_NODE_REF/NG_HOOK_REF taken at arm time.
+	 */
 	oitem = callout_arg(c);
 	callout_reset(c, ticks, &ng_callout_trampoline, item);
+	if (oitem != NULL && oitem != item && callout_func(c) == &ng_callout_trampoline)
+		ng_free_item(oitem);
 	return (0);
 }
@@ -3288,7 +3294,8 @@ ng_uncallout(struct callout *c, node_p node)
 		 * We successfully removed it from the queue before it ran
 		 * So now we need to unreference everything that was
 		 * given extra references. (NG_FREE_ITEM does this).
 		 */
 		NG_FREE_ITEM(item);
+		ng_free_item(item);	/* NG_FREE_ITEM is flag-only in ng7 */
 	}
 	callout_set_arg(c, NULL);
 
 	return (rval);