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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0727 Β· 13 files
FileTypeDescriptionSize
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
README.md readme human-readable reproduction guide
↓ download 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

  1. 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.ko Verify: sysctl net.graph.msg_version should print 8.

  2. Build the PoC: ./build.sh

  3. 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 scripts
  • fix.diff β€” the one-line fix (lasthook != NULL && guard)
  • VERDICT.md β€” detailed analysis
  • panic.txt / panic_full.txt β€” panic signature from boot.log
  • env.txt β€” guest environment
  • run.log / build.log β€” run and build logs
  • manifest.json β€” artifact catalog
VERDICT.md verdict detailed analysis: mechanism, reachability, fix validation
↓ download raw

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 legacy sys/netgraph/ (NG_VERSION=2), whose ng_tee.c does not have the NGM_FLOW_COOKIE handler at all. Netgraph7 is opt-in (option NETGRAPH7 in the kernel config, or loading the netgraph7 KLD modules built from sys/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

  1. Build and load the netgraph7 modules (netgraph.ko, ng_socket.ko, ng_tee.ko from sys/netgraph7/).
  2. cc -o ng_tee_flow_nullderef ng_tee_flow_nullderef.c
  3. ./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 != NULL guard): 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

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED: 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.
↓ fix.diffDragonFly 6.5-DEVELOPMENT #0 with netgraph7 modules (ng_tee.ko with fix.diff applied)

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).