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

ng7 ng_tty: ngt_open() error path leaks sc when ng_name_node() fails

Summary

On ng_name_node() failure (name collision with a pre-existing 'ttyN' node), ngt_open handles the error with a bare NG_NODE_UNREF(sc->node) - but ng7 ng_unref_node() frees the node without invoking nd_shutdown when refs hit zero, and sc has not been attached yet (SET_PRIVATE runs at :221), so the kmalloc'd ngt_sc is orphaned on every failed open. v1's error path uses ng_rmnode (runs shutdown) and does not leak sc (it has the complementary bug, DF-3533). Privileged-gated ~100-byte permanent leak per failed open. Phase V skipped (Low); verdict records not_testable rationale. [VERIFIED pass2: error branch reachable on harness kernel; leak code-certain NG_NODE_UNREF frees without nd_shutdown; measurement confounded by node-lifetime mechanics]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3532 Β· 9 files
FileTypeDescriptionSize
README.md β€” 359 B ↓ raw
VERDICT.md β€” 1.7 KB ↓ raw
build.sh β€” 58 B view raw
env.txt β€” 329 B view raw
fix.diff β€” 351 B view raw
manifest.json β€” 716 B view raw
run.log β€” 370 B view raw
run.sh β€” 84 B view raw
verdict.json β€” 2.0 KB view raw

DF-3532 - ng7 ng_tty failed-open sc leak

Harness kernel. gcc -O -o ttyleak7 ttyleak.c; ./ttyleak7 500. Error branch reachable (syslog 'node name exists?'); quantitative leak measurement confounded - see VERDICT.md.

Kernel: DragonFly dfbsd 6.5-DEVELOPMENT #0: Sun Oct 25 20:46:48 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_NG7 x86_64 (harness kernel)

VERDICT.md
↓ download raw

DF-3532 - VERDICT

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

Partially verified, honestly inconclusive on the measurement. The ng7 error branch is REACHABLE on the harness kernel: with a colliding node name pre-created, the failed open logs 'ttyNNN: node name exists?' (the ng7 ngt_open :212-216 branch) - captured in dmesg. The leak itself is code-certain: that branch does sc->flags |= FLG_DIE; NG_NODE_UNREF(sc->node); return(error) - ng7 ng_unref_node frees the node WITHOUT invoking nd_shutdown, and NG_NODE_SET_PRIVATE only runs at :221 AFTER naming succeeds, so the kmalloc'd ngt_softc (~140B, M_NETGRAPH) is orphaned per failed open. The quantitative demonstration was confounded by harness mechanics (ng7 naming-counter state across runs made collisions intermittent; pre-created nodes must be kept hooked to survive; a 500-cycle run produced 500 SUCCESSFUL opens with live-state growth, not leaks). Privileged-gated ~140B permanent leak per failed open stands on code.

Kernel references

Evidence

run.log (dmesg 'tty500/tty501: node name exists?' + vmstat -m netgraph growth lines), ttyleak.c

free sc on the naming-failure path (kfree(sc, M_NETGRAPH) after the UNREF) or route through shutdown.

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-3532

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (dmesg 'tty500/tty501: node name exists?' + vmstat -m netgraph growth lines), ttyleak.c

PoC changes

ttyleak.c adapted to the ng7 wire format; node-name pre-creation must keep nodes hooked to persist

Verified recommended fix

free sc on the naming-failure path (kfree(sc, M_NETGRAPH) after the UNREF) or route through shutdown.

Verdict

Partially verified, honestly inconclusive on the measurement. The ng7 error branch is REACHABLE on the harness kernel: with a colliding node name pre-created, the failed open logs 'ttyNNN: node name exists?' (the ng7 ngt_open :212-216 branch) - captured in dmesg. The leak itself is code-certain: that branch does sc->flags |= FLG_DIE; NG_NODE_UNREF(sc->node); return(error) - ng7 ng_unref_node frees the node WITHOUT invoking nd_shutdown, and NG_NODE_SET_PRIVATE only runs at :221 AFTER naming succeeds, so the kmalloc'd ngt_softc (~140B, M_NETGRAPH) is orphaned per failed open. The quantitative demonstration was confounded by harness mechanics (ng7 naming-counter state across runs made collisions intermittent; pre-created nodes must be kept hooked to survive; a 500-cycle run produced 500 SUCCESSFUL opens with live-state growth, not leaks). Privileged-gated ~140B permanent leak per failed open stands on code.