NULL-pointer dereference in NGM_FLOW_COOKIE handler when lasthook is NULL and left/right hook disconnected
Summary
ng_tee.c:263 if(lasthook==sc->left.hook||lasthook==sc->right.hook) β NO lasthook!=NULL guard. :264 hi_p const hinfo=NG_HOOK_PRIVATE(lasthook) expands to lasthook->hk_private NULL deref. lasthook legitimately NULL for messages addressed by node ID (ng_address_ID ng_base.c:3125 NGI_CLR_HOOK) or origin hook invalidated pre-dispatch (ng_base.c:2030-2037). If sc->left.hook also NULL (hook not connected or disconnected :375) NULL==NULL=TRUE. Defensive if(hinfo&&...) :265 is dead code crash precedes it. Sibling ng_UI.c:145 has && lasthook guard ng_tee omits it. Trigger: root ngctl mkpeer tee .left2right tee (left/right never connected) then ngctl msg tee0: flow 1 addressed by node name lasthook=NULL sc->left.hook=NULL match. Impact: deterministic kernel panic local DoS root-only. Fix: if(lasthook!=NULL&&(lasthook==sc->left.hook||lasthook==sc->right.hook)).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0727 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| ng_tee_flow_nullderef.c | trigger-source | raw-sendto PoC: creates tee node (left/right never connected), sends NGM_FLOW_COOKIE by node name -> lasthook=NULL -> NULL deref panic | 6.6 KB | view raw |
| build.sh | build-script | cc -o ng_tee_flow_nullderef ng_tee_flow_nullderef.c | 161 B | view raw |
| run.sh | run-script | loads ng_tee module, runs the PoC as root | 269 B | view raw |
| build.log | build-log | final successful build output | 135 B | view raw |
| run.log | run-log | baseline panic + patched no-panic runs | 1.9 KB | view raw |
| panic.txt | panic-signature | fatal trap 12 in ng_tee_rcvmsg+0x83, fault va=0x20 | 273 B | view raw |
| panic_full.txt | panic-signature | full panic dump from boot.log | 657 B | view raw |
| fix.diff | suggested-fix | add lasthook != NULL guard to NGM_FLOW_COOKIE case | 457 B | view raw |
| env.txt | environment | uname, netgraph7 module versions, fix verification | 460 B | view raw |
| VERDICT.md | verdict | detailed analysis: mechanism, reachability, fix validation | 4.0 KB | β raw |
| README.md | readme | human-readable reproduction guide | 2.5 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-0727 β NULL-pointer dereference in ng_tee NGM_FLOW_COOKIE handler
Summary
A NULL-pointer dereference in the NGM_FLOW_COOKIE handler of the
netgraph7 ng_tee node type (sys/netgraph7/tee/ng_tee.c:263-264).
When a NGM_FLOW_COOKIE control message is addressed by node name (no
hook segment in the path), lasthook is NULL. If the tee node's left
and right hooks were never connected, sc->left.hook==NULL and
sc->right.hook==NULL, so NULL==NULL is TRUE and
NG_HOOK_PRIVATE(NULL) dereferences NULL β kernel panic.
Reachability
- Root-only: AF_NETGRAPH control socket requires
caps_priv_check(SYSCAP_RESTRICTEDROOT). - Netgraph7 required: the bug is in
sys/netgraph7/tee/ng_tee.c. The default DragonFly netgraph (sys/netgraph/) does not have this code. Netgraph7 is opt-in (load the netgraph7 KLD modules).
Impact
Deterministic kernel panic (local DoS, root β kernel). No escalation (NULL deref = read, no write primitive). Low severity.
How to reproduce
Prerequisites
-
Build and load the netgraph7 modules from
sys/netgraph7/:cd /usr/src/sys/netgraph7/netgraph && make && kldload ./netgraph.ko cd /usr/src/sys/netgraph7/socket && make && kldload ./ng_socket.ko cd /usr/src/sys/netgraph7/tee && make && kldload ./ng_tee.koVerify:sysctl net.graph.msg_versionshould print8. -
Build the PoC:
./build.sh -
Run as root:
./run.sh
Expected output (bug present)
[+] opened AF_NETGRAPH control socket (fd=3) [+] mkpeer tee left2right<->left2right (left/right hooks NOT connected) [+] named the tee node 'tee0' [+] sending NGM_FLOW_COOKIE to 'tee0:' (addressed by name => lasthook=NULL) [+] EXPECT: kernel panic / fatal trap 12 (page fault) in ng_tee_rcvmsg (ssh dies β kernel panicked)
Serial console shows:
Fatal trap 12: page fault while in kernel mode fault virtual address = 0x20 Stopped at ng_tee_rcvmsg+0x83: movq 0x20(%rdx),%rax
Expected output (bug fixed)
[+] sending NGM_FLOW_COOKIE to 'tee0:' ... [?] sendto returned without panic -> bug NOT triggered on this kernel RUN_EXIT=0
Files
ng_tee_flow_nullderef.cβ the PoC (raw sendto, netgraph7 struct layout)build.sh/run.shβ build and run scriptsfix.diffβ the one-line fix (lasthook != NULL &&guard)VERDICT.mdβ detailed analysispanic.txt/panic_full.txtβ panic signature from boot.logenv.txtβ guest environmentrun.log/build.logβ run and build logsmanifest.jsonβ artifact catalog
DF-0727 β NULL-pointer dereference in NGM_FLOW_COOKIE handler (ng_tee)
Verdict: REPRODUCED (panic / local DoS, root-only)
The bug
sys/netgraph7/tee/ng_tee.c:262-271 β the NGM_FLOW_COOKIE case in
ng_tee_rcvmsg():
case NGM_FLOW_COOKIE:
if (lasthook == sc->left.hook || lasthook == sc->right.hook) { // line 263
hi_p const hinfo = NG_HOOK_PRIVATE(lasthook); // line 264 <-- NULL deref
if (hinfo && hinfo->dest) {
...
}
}
break;
When lasthook is NULL (message addressed by node name β no hook segment
in the path) and the tee node's left/right hooks were never
connected (sc->left.hook == NULL, sc->right.hook == NULL), the
condition NULL == NULL evaluates TRUE, and
NG_HOOK_PRIVATE(lasthook) expands to lasthook->hk_private =
NULL->hk_private (offset 0x20) β page fault / kernel panic.
The defensive if (hinfo && hinfo->dest) on line 265 is dead code β the
crash on line 264 precedes it.
How lasthook becomes NULL
ng_address_path() (sys/netgraph7/netgraph/ng_base.c:1689-1822) calls
ng_path_parse(). When the address is a bare node name with trailing colon
(e.g. "tee0:"), ng_path_parse yields nodename="tee0", path=NULL.
ng_path2noderef then sets *lasthook = (hook ? NG_HOOK_PEER(hook) : NULL)
= NULL (line 1819-1820, hook was never assigned since no path segments
were traversed). The item is delivered to ng_tee_rcvmsg with
lasthook=NULL.
ng_address_ID() (ng_base.c:3125) also clears the hook via
NGI_CLR_HOOK(item), so messages addressed by numeric node ID also have
lasthook=NULL.
Sibling guard
The sibling ng_UI.c:145 has the correct guard:
if ((msg->header.typecookie == NGM_FLOW_COOKIE) && lasthook) {
ng_tee omits the lasthook (NULL) check.
Reachability
- Root-only. The AF_NETGRAPH control socket requires
caps_priv_check(SYSCAP_RESTRICTEDROOT)(ng_socket.c:182). An unprivileged user cannot open it. - Netgraph7 required. The bug is in
sys/netgraph7/tee/ng_tee.c. DragonFly's default netgraph is the legacysys/netgraph/(NG_VERSION=2), whoseng_tee.cdoes not have theNGM_FLOW_COOKIEhandler at all. Netgraph7 is opt-in (option NETGRAPH7in the kernel config, or loading the netgraph7 KLD modules built fromsys/netgraph7/). An admin who chooses netgraph7 is vulnerable.
Impact
Deterministic kernel panic (local DoS, root β kernel). No escalation: NULL deref is a read, no write primitive. The finding's Low severity is appropriate (root-only DoS on a non-default netgraph stack).
Reproduction
- Build and load the netgraph7 modules (
netgraph.ko,ng_socket.ko,ng_tee.kofromsys/netgraph7/). cc -o ng_tee_flow_nullderef ng_tee_flow_nullderef.c./ng_tee_flow_nullderef(as root)
The PoC creates a tee node with only the left2right hook connected
(left/right never connected), names it tee0, then sends an
NGM_FLOW_COOKIE message addressed by node name (tee0:, no hook segment)
so lasthook=NULL. The kernel panics:
Fatal trap 12: page fault while in kernel mode fault virtual address = 0x20 Stopped at ng_tee_rcvmsg+0x83: movq 0x20(%rdx),%rax
The fault address 0x20 = offset of hk_private in struct ng_hook;
%rdx = 0 (NULL lasthook).
Fix
Add a lasthook != NULL guard (matches the sibling ng_UI.c:145 pattern):
--- a/sys/netgraph7/tee/ng_tee.c
+++ b/sys/netgraph7/tee/ng_tee.c
@@ -260,7 +260,7 @@
}
break;
case NGM_FLOW_COOKIE:
- if (lasthook == sc->left.hook || lasthook == sc->right.hook) {
+ if (lasthook != NULL && (lasthook == sc->left.hook || lasthook == sc->right.hook)) {
hi_p const hinfo = NG_HOOK_PRIVATE(lasthook);
Fix validation
- Baseline (unfixed netgraph7 ng_tee): PoC panics the kernel
(
ng_tee_rcvmsg+0x83: movq 0x20(%rdx),%rax, fault va=0x20). Guest down. - Patched (fixed ng_tee with
lasthook != NULLguard): PoC exits 0 cleanly, no panic, guest stays up. Confirmed over 2 runs.
See fix.diff for the standalone git-apply-able diff.
Fix verification
fixedVALIDATED: baseline panic at ng_tee_rcvmsg+0x83 (fault va=0x20); patched PoC exits 0, guest up x2.
BEFORE: Fatal trap 12 at ng_tee_rcvmsg+0x83: movq 0x20(%rdx),%rax. AFTER: RUN_EXIT=0, guest up x2.
Confirmed kernel references
Detail
Exploit chain
none -- NULL-pointer dereference (read). No write, no corruption. Root-only DoS.
Evidence (decisive lines)
BASELINE: Fatal trap 12 at ng_tee_rcvmsg+0x83: movq 0x20(%rdx),%rax, fault va=0x20, guest down. PATCHED: PoC exits 0, guest up x2.
PoC changes
Rewrote PoC from scratch using raw sendto with netgraph7 struct layout (libnetgraph incompatible). Built netgraph7 KLD modules from source. Added fix.diff, build.sh, run.sh, VERDICT.md, manifest.json.
Verified recommended fix
Add lasthook!=NULL guard at ng_tee.c:263: if (lasthook != NULL && (lasthook == sc->left.hook || lasthook == sc->right.hook)). Matches finding proposal + sibling ng_UI.c:145. Full git-apply-able diff in findings/poc/DF-0727/fix.diff.
Verdict
REPRODUCED. ng_tee.c:263-264 NGM_FLOW_COOKIE handler does if (lasthook == sc->left.hook || lasthook == sc->right.hook) without lasthook!=NULL guard. When message addressed by node name (lasthook=NULL), NULL==NULL is TRUE, NG_HOOK_PRIVATE(NULL) derefs offset 0x20 -> panic. Root-only (netgraph7, AF_NETGRAPH).
No comments yet.