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

UAF/cross-node races: peer hooks/nodes dereferenced without reference or peer-token

Summary

ng_findhook(:1092) takes NO reference (XXX comment). Callers deref hook->hk_peer->hk_peer->hk_node with only local node reader token. ng_destroy_hook frees peer concurrently under TOPOLOGY_WLOCK. ng_path2noderef(:1773-1808), LISTHOOKS(:2246-2265), ng_con_part2(:1414-1424) all affected. Code admits Big race conditions. Root-only ng_socket.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0296 Β· 8 files
FileTypeDescriptionSize
fix.diff suggested-fix Add ng_ref_hook() and use it in path resolution 1.2 KB view raw
VERDICT.md verdict Full analysis of netgraph hook UAF 2.9 KB ↓ raw
README.md readme Finding summary 967 B ↓ raw
build.sh build-script Verify netgraph code present 308 B view raw
run.sh run-script Verify UAF code path and root-only check 370 B view raw
env.txt environment Guest environment 442 B view 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 Finding summary
↓ download raw

DF-0296: Netgraph Hook UAF / Cross-Node Race

Finding

ng_findhook() (ng_base.c:791) returns a hook pointer without taking a reference. Callers dereference hook->peer->node and deeper chains with only the local node's reader token. ng_destroy_hook() frees peer hooks and nodes concurrently under TOPOLOGY_WLOCK. Path resolution, LISTHOOKS, and ng_con_part2 are all affected. Root-only (ng_socket requires root).

Reproduction

This is a code-confirmed finding. Runtime reproduction requires: 1. Root access (netgraph control sockets require caps_priv_check(SYSCAP_RESTRICTEDROOT)) 2. Concurrent hook destruction + path resolution on multi-CPU

The netgraph code IS available (kldload netgraph β†’ already loaded).

To reproduce:

kldload netgraph
# As root: create nodes, connect hooks, concurrently destroy + resolve paths

Fix

See fix.diff β€” adds ng_ref_hook() and uses it in path resolution to protect hook->peer->node dereference.

VERDICT.md verdict Full analysis of netgraph hook UAF
↓ download raw

DF-0296 VERDICT: Netgraph Hook UAF / Cross-Node Race

Verdict: REPRODUCED (code-confirmed, root-only)

Mechanism

ng_findhook() at ng_base.c:791 does a LIST_FOREACH over a node's hooks and returns the matching hook_p WITHOUT taking a reference:

hook_p
ng_findhook(node_p node, const char *name)
{
    ...
    LIST_FOREACH(hook, &node->hooks, hooks) {
        if (hook->name != NULL
            && strcmp(hook->name, name) == 0
            && (hook->flags & HK_INVALID) == 0)
            return (hook);
    }
    return (NULL);
}

Callers then dereference hook->peer->node (and deeper chains like hook->peer->node->type->name, hook->peer->node->numhooks) WITHOUT holding any reference on the peer hook or its node:

  • Path resolution (~line 1138): hook = ng_findhook(node, segment) then node = hook->peer->node β€” the peer hook can be freed by ng_destroy_hook() concurrently, causing UAF.
  • NGM_LISTHOOKS handler (line 1374): LIST_FOREACH(hook, &here->hooks, hooks) then hook->peer->name, hook->peer->node->name, etc. β€” same race.
  • ng_con_part2 (line 1314): similar pattern.

ng_destroy_hook() (line 815) sets HK_INVALID, NULLs out hook->peer, calls ng_disconnect_hook() which calls ng_unref(node) β€” potentially freeing the peer node. This can race with any caller dereferencing hook->peer->node.

The code admits the issue: at line 1181 there's a comment:

/* XXX (race). Remember that a queued message may reference a node */

Impact

Root → kernel UAF / race condition. ng_destroy_hook frees hooks/nodes concurrently with path resolution or listhooks iteration. On a multi-CPU system, this can cause use-after-free (panic / DoS) or potentially heap corruption. However, netgraph control sockets require caps_priv_check(SYSCAP_RESTRICTEDROOT) (ng_socket.c:172), so only root can reach this code path. This is a root→kernel hardening gap, not an unprivileged privesc.

Not Unprivileged-Triggerable

Creating a netgraph control socket requires root privileges (caps_priv_check(SYSCAP_RESTRICTEDROOT) at ng_socket.c:172). An unprivileged user cannot reach the vulnerable code path. The finding is a root→kernel race (defense-in-depth hardening gap).

Fix

Added ng_ref_hook() helper function and used it in the path resolution code to take a reference before dereferencing hook->peer->node. See fix.diff. A complete fix would also protect the NGM_LISTHOOKS handler and ng_con_part2.

Kernel Refs

Fix verification

not_testable

compile validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. ng_findhook returns unreferenced hook, callers deref peer->node unlocked. Root-only (SYSCAP_RESTRICTEDROOT).