β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3509

ng_fec_rmnode vs ng_fec_tick callout race: callout_stop does not wait for an in-flight tick, IFF_RUNNING is never cleared on the rmnode path, and tick re-arms itself -> callout can fire on &priv->fec_timeout after kfree(priv)

Summary

ng_fec_tick re-arms itself while the fec ifp has IFF_RUNNING (:614-616). rmnode calls ng_fec_stop (plain callout_stop) and then kfree(priv) without clearing IFF_RUNNING and without sync semantics: a tick executing concurrently on another CPU completes its ioctl loop, sees IFF_RUNNING still set, and callout_resets onto &priv->fec_timeout - after priv is freed the armed callout fires into freed memory (ng_fec_tick(priv) UAF) and walks a freed portlist. Narrow-window UAF on node teardown while the ticker is mid-flight (complements the known DF-0525 list-race). Fix: clear IFF_RUNNING before stop + callout_stop_sync/drain before kfree. [VERIFIED pass2: doubly blocked - stock ctor kfree NULL panic all builds plus harness rmnode INVARIANTS serializer assert before the window; callout analysis stands]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3509 Β· 9 files
FileTypeDescriptionSize
README.md β€” 398 B ↓ raw
VERDICT.md β€” 1.9 KB ↓ raw
env.txt β€” 309 B view raw
fec-sequence.log β€” 5.7 KB view raw
fix.diff β€” 507 B view raw
manifest.json β€” 782 B view raw
panic.txt β€” 13.4 KB view raw
run.log β€” 430 B view raw
verdict.json β€” 2.1 KB view raw

DF-3509 - v1 ng_fec tick/rmnode callout race

Blocked on stock bits (ctor kfree(NULL) panic - see DF-3506) and on the INVARIANTS guest even after ctor repair (rmnode -> ng_fec_stop serializer assert panics before the race window). Code-certain analysis in VERDICT.md.

Kernel: DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

VERDICT.md
↓ download raw

DF-3509 - VERDICT

status: not_reproduced | reproduced: 0 | impact: none | confidence: likely

Not reproducible on this guest - doubly blocked before the race window can open. (1) On ANY kernel config the shipped ng_fec module cannot create a node (gcc-UB-eliminated NULL guard -> kfree(NULL) panic in ng_fec_get_unit, see DF-3506 pack - deterministic, non-INVARIANTS-gated). (2) Even on the constructor-repaired harness module, teardown cannot run on the INVARIANTS guest: ng_fec_rmnode -> ng_fec_stop hits 'panic: assertion IS_SERIALIZED((s)) failed in lwkt_serialize_exit' (captured in boot.log) BEFORE kfree(priv), so the tick-vs-rmnode window is never entered (on production kernels the teardown runs unserialized and the race is live). Code-certain analysis stands: ng_fec_tick re-arms itself while IFF_RUNNING (ng_fec.c:614-616), rmnode uses plain callout_stop via ng_fec_stop and never clears IFF_RUNNING (:1213-1240), and DFly callout_stop does not wait for an in-flight callout - a tick executing on another CPU re-arms onto &priv->fec_timeout and fires into freed memory. Narrow-window UAF, root-gated (node setup), panic ceiling.

Kernel references

Evidence

boot.log (serializer panic in ng_fec_stop from rmnode - teardown blocked), fec-sequence.log, source :614-616/:1213-1240

clear IFF_RUNNING and use callout_stop_sync/drain before kfree(priv) in ng_fec_rmnode; repair the get_unit bcopy UB so the module builds.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
↓ fix.diffper-fix-DF-3509

Confirmed kernel references

Detail

Evidence (decisive lines)

boot.log (serializer panic in ng_fec_stop from rmnode - teardown blocked), fec-sequence.log, source :614-616/:1213-1240

PoC changes

none (blocked; analysis only)

Verified recommended fix

clear IFF_RUNNING and use callout_stop_sync/drain before kfree(priv) in ng_fec_rmnode; repair the get_unit bcopy UB so the module builds.

Verdict

Not reproducible on this guest - doubly blocked before the race window can open. (1) On ANY kernel config the shipped ng_fec module cannot create a node (gcc-UB-eliminated NULL guard -> kfree(NULL) panic in ng_fec_get_unit, see DF-3506 pack - deterministic, non-INVARIANTS-gated). (2) Even on the constructor-repaired harness module, teardown cannot run on the INVARIANTS guest: ng_fec_rmnode -> ng_fec_stop hits 'panic: assertion IS_SERIALIZED((s)) failed in lwkt_serialize_exit' (captured in boot.log) BEFORE kfree(priv), so the tick-vs-rmnode window is never entered (on production kernels the teardown runs unserialized and the race is live). Code-certain analysis stands: ng_fec_tick re-arms itself while IFF_RUNNING (ng_fec.c:614-616), rmnode uses plain callout_stop via ng_fec_stop and never clears IFF_RUNNING (:1213-1240), and DFly callout_stop does not wait for an in-flight callout - a tick executing on another CPU re-arms onto &priv->fec_timeout and fires into freed memory. Narrow-window UAF, root-gated (node setup), panic ceiling.