SLIST_REMOVE on never-inserted element in newhook error paths corrupts list head
Summary
ng_device_newhook (:286-309): error branches at :290 (make_dev fail) and :300 (readq kmalloc fail) call SLIST_REMOVE(&sc->head,new_connection,...) but SLIST_INSERT_HEAD happens only at :309 AFTER these branches. SLIST_REMOVE on non-member walks list finds nothing dereferences &sc->head as if next field of list element -> list head corruption. Trigger: force make_dev/readq fail via memory pressure. Next netgraph op touching softc panics. Fix: remove dead SLIST_REMOVE calls.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0663 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df0663_slist_sim.c | trigger-source | userspace harness replicating SLIST_REMOVE on non-member | 1.8 KB | view raw |
| build.sh | build-script | cc -O2 -o df0663_slist_sim df0663_slist_sim.c -I/usr/src/sys | 213 B | view raw |
| run.sh | run-script | runs harness; segfault = SLIST_REMOVE NULL-deref confirmed | 70 B | view raw |
| build.log | build-log | final successful build | 137 B | view raw |
| run.log | run-log | Segmentation fault (core dumped), exit 139 | 265 B | view raw |
| env.txt | environment | uname, cc, kldstat | 360 B | view raw |
| VERDICT.md | verdict | source trace + dead-code analysis + harness | 4.7 KB | β raw |
| README.md | readme | dead code note + how to run harness | 1.2 KB | β raw |
| fix.diff | suggested-fix | remove bogus SLIST_REMOVE calls; note file is dead code | 1.3 KB | 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 |
DF-0663 β SLIST_REMOVE on never-inserted element in ng_device_newhook
Bug
sys/netgraph/ng_device.c:286-309 β ng_device_newhook() error branches
at :290 (make_dev fail) and :300 (readq kmalloc fail) call
SLIST_REMOVE(&sc->head, new_connection, ngd_connection, links) but the
element was never inserted: SLIST_INSERT_HEAD(&sc->head, ...) happens
only at :309, AFTER both error checks.
SLIST_REMOVE (sys/sys/queue.h:208-220) walks the list with no NULL
guard, so on a non-member it dereferences NULL β panic.
IMPORTANT: dead code
The cited file sys/netgraph/ng_device.c is NOT in sys/conf/files and
is built by no Makefile β it is dead code on all standard kernels. Only
sys/netgraph7/ng_device.c (a complete rewrite without this bug) is
built (optional netgraph7_device). The bug is real in the dead source,
unreachable in any standard build.
Build / Run
cc -O2 -o df0663_slist_sim df0663_slist_sim.c -I/usr/src/sys
./df0663_slist_sim
# Expected: "Segmentation fault" β proving the SLIST_REMOVE macro
# NULL-derefs on a non-member with an empty list.
Expected
- Harness:
Segmentation fault (core dumped)on the empty-list case. - Live kernel: nothing β the cited file is not built. See VERDICT.md.
DF-0663 β VERDICT
Verdict: REPRODUCED at source level; cited file is DEAD CODE (not built)
The SLIST_REMOVE-on-never-inserted-element logic at
sys/netgraph/ng_device.c:290 and :300 is real and would panic the
kernel if executed β verified by source trace and by a userspace harness
that reproduces the exact macro expansion. However, the cited file
sys/netgraph/ng_device.c (the OLD netgraph v0 implementation) is not
built by any standard kernel configuration or module: it does not appear
in sys/conf/files or any module Makefile. Only the rewritten
sys/netgraph7/ng_device.c is built (optional netgraph7_device), and
that file has completely different code with no SLIST_REMOVE bug.
Mechanism (cited line-by-line)
sys/netgraph/ng_device.c:273ng_device_newhook(). Allocates a freshnew_connection(not yet on any list).- Line 286
make_dev(...)error branch. Ifmake_devreturns NULL the code jumps to cleanup at line 290:c 288: if (new_connection->ngddev == NULL) { 290: SLIST_REMOVE(&sc->head, new_connection, ngd_connection, links); 291: kfree(new_connection, M_DEVBUF);new_connectionwas never inserted intosc->head(theSLIST_INSERT_HEAD(&sc->head, new_connection, links)happens only at line 309, after all error checks). - Line 295
kmalloc(... readq ...)error branch at line 300 has the same incorrectSLIST_REMOVEcall. sys/sys/queue.h:208-220SLIST_REMOVEmacro:c if (SLIST_FIRST((head)) == (elm)) { SLIST_REMOVE_HEAD(...); } else { struct type *curelm = SLIST_FIRST((head)); while (SLIST_NEXT(curelm, field) != (elm)) curelm = SLIST_NEXT(curelm, field); /* <-- no NULL check */ SLIST_REMOVE_AFTER(curelm, field); }Withelmnot in the list, thewhilewalks past the last element (whereSLIST_NEXT(curelm, field) == NULL), assignscurelm = NULL, and re-evaluatesSLIST_NEXT(NULL, field)β NULL dereference.
If the list is empty (SLIST_FIRST == NULL), the else branch starts
with curelm = NULL and the first SLIST_NEXT(NULL, ...) deref panics
immediately.
- Harness verification.
findings/poc/DF-0663/df0663_slist_sim.creproduces the exact SLIST_REMOVE logic in userspace with an empty list and an uninserted element. Run:[ng_device newhook SLIST_REMOVE on never-inserted conn] Case 1: empty list SLIST_FIRST(&sc_head_empty) = 0x0 new_conn = 0x8004902c0 invoking SLIST_REMOVE(&sc_head_empty, new_conn, ...) Segmentation fault (core dumped) EXIT=139The segfault proves the macro dereferences NULL on this path.
Why the live kernel cannot trigger this
$ grep "ng_device" /home/maxx/dfbsd/dfbsd/sys/conf/files netgraph7/ng_device.c optional netgraph7_device
Only netgraph7/ng_device.c is registered in sys/conf/files. The OLD
netgraph/ng_device.c (which the finding cites) is not registered β
it is not built into the kernel and not built as a module by any
Makefile. The active netgraph7 implementation
(sys/netgraph7/ng_device.c:264-312 ng_device_newhook) is a complete
rewrite that uses priv_p and IF_ queues, has no SLIST, and no
equivalent bug. The OLD file is dead code retained for historical
reference.
Verified on the running guest: ls /boot/kernel/ | grep ng_device
returns nothing; nm /boot/kernel/kernel | grep ng_device_newhook
returns nothing.
Privilege / threat model
- The vulnerable file is not built on any standard kernel. To make
it live, an admin would have to manually add it to
conf/files(e.g.netgraph/ng_device.c optional netgraph_device) and rebuild β no DragonFly release has shipped it in decades (netgraph7 superseded it). - Impact if it WERE built: kernel panic from a privilege that can
create netgraph hooks (root, via
AF_NETGRAPHsocket; or kernel-only paths). The trigger requiresmake_dev()orM_NOWAITkmalloc()to fail β i.e. memory pressure / device-number exhaustion. With INVARIANTS ON, KASSERT failures in the slab allocator may catch some of these first. - Bottom line: latent dead-code bug, not exploitable on stock DragonFly. Recommend deleting the dead file or porting the netgraph7 implementation in place.
Recommended fix
The finding's correct code-shape fix is to remove the bogus
SLIST_REMOVE calls at :290 and :300 (since new_connection was
never inserted). However, since the file is dead code, the better
recommendation is to delete sys/netgraph/ng_device.c (and its
header) to prevent confusion. findings/poc/DF-0663/fix.diff carries
both options: a minimal correctness fix (remove SLIST_REMOVE), and a
note recommending deletion of the dead file.
Fix verification
not_testablecompile+harness validated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source+harness. ng_device SLIST_REMOVE on never-inserted newhook error path. Dead code: sys/netgraph/ng_device.c not in conf/files.
No comments yet.