ng_xxx_newhook bookkeeping defects: free-slot search missing break makes every DLCI hook fail ENOBUFS; sizeof/strlen off-by-one rejects single-digit names; parsed dlci never stored; debughook stale after disconnect
Summary
Four independent template defects: (1) the free-channel loop only ever 'continue's, so chan always exits at XXX_NUM_DLCIS and every dlciNN hook creation returns ENOBUFS on a fresh node; (2) cp = name + sizeof(LEADIN) counts the NUL and skips the first digit ('dlci5' -> EINVAL, 'dlci55' parses as 5); (3) channel[chan].dlci is never assigned, killing dup detection and demux even with (1)+(2) fixed; (4) xxxp->debughook is never cleared in disconnect - latent stale pointer, never dereferenced. VERIFIED live: NGM_CONNECT returns ng_xxx_newhook's errno synchronously - dlci55 -> errno 55 (ENOBUFS), dlci5 -> errno 22 (EINVAL), dlci05 -> 55. Non-shipping template; no memory-safety impact.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3552 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.1 KB | β raw | |
| VERDICT.md | β | 3.2 KB | β raw | |
| v1demo.c | β | 8.2 KB | view raw | |
| run.sh | β | 190 B | view raw | |
| build.log | β | 3.1 KB | view raw | |
| run.log | β | 1.8 KB | view raw | |
| env.txt | β | 314 B | view raw | |
| fix.diff | β | 1.2 KB | view raw | |
| verdict.json | β | 3.1 KB | view raw |
DF-3552 evidence pack β v1 ng_sample newhook bookkeeping defects
sys/netgraph/ng_sample.c ng_xxx_newhook()/ng_xxx_disconnect():
(1) free-slot search loop has no break β every dlciNN hook fails
ENOBUFS (:221-227); (2) sizeof vs strlen off-by-one skips the first
digit of the hook name (:210) β dlci5 = EINVAL, dlci55 parses as 5;
(3) channel[chan].dlci never assigned (:228-232) β dup detection and the
demux search are dead; (4) debughook never cleared on disconnect
(:238-241, :460-466) β latent stale pointer, never dereferenced.
Build / Run
Same v1demo.c, build.sh and module setup as the DF-3550 pack (one
directory up). Decisive commands:
kldload ng_socket ; kldload /root/ngs1/ng_sample.ko /root/v1demo hooktest dlci55 # expect: ENOBUFS (55) from missing break /root/v1demo hooktest dlci5 # expect: EINVAL (22) from sizeof off-by-one
Expected
CONNECT dlci55: FAILED errno=55 (No buffer space available) CONNECT dlci5: FAILED errno=22 (Invalid argument)
See run.log (from the same decisive session as DF-3550's).
DF-3552 β VERDICT: REPRODUCED (v1 ng_sample newhook bookkeeping defects)
Impact: none (hook-name parsing / slot allocation logic in a dormant template) Confidence: certain
What was run
v1demo hooktest NAME on the stock v1 netgraph stack: creates a sample
node (NGM_MKPEER, socket(sd) <-> sample(downstream)), then issues
NGM_CONNECTs trying to attach further hooks named NAME and dlci05 on
the sample node. v1 generic commands return errors synchronously from
sendto(2), so the observed errno is ng_xxx_newhook()'s own return.
Observed (run.log)
=== hooktest dlci55: CONNECT dlci55: FAILED errno=55 (No buffer space available) CONNECT dlci05: FAILED errno=55 (No buffer space available) === hooktest dlci5: CONNECT dlci5: FAILED errno=22 (Invalid argument) CONNECT dlci05: FAILED errno=55 (No buffer space available)
Why (kernel path, line-by-line)
Four independent defects in ng_xxx_newhook() / ng_xxx_disconnect()
(sys/netgraph/ng_sample.c):
- Missing
breakin the free-slot search β every DLCI hook fails with ENOBUFS. (:221-227) After the "find existing dlci" loop misses, the free-slot loopfor (chanβ¦) if (xxxp->channel[chan].dlci != -2) continue;only evercontinues β it has nobreak, sochanalways exits the loop atXXX_NUM_DLCISandreturn (ENOBUFS)fires on a fresh node with all 16 channels free. Confirmed live:dlci55β 55. sizeofvsstrlenoff-by-one in the hook-name parse. (:210)cp = name + sizeof(NG_XXX_HOOK_DLCI_LEADIN)βsizeof("dlci")counts the NUL, socpskips the first digit character."dlci5"leavescpat the terminating NUL βisdigitfails β EINVAL (confirmed live: errno 22);"dlci55"parses as 5, not 55; single-digit DLCI hook names are impossible.channel[chan].dlciis never assigned. (:228-232) The parseddlciis used for the search but never stored, so even with (1) and (2) fixed, duplicate-DLCI detection (:218-220) never matches and the downstream demux search (:369-371) can never find a channel.- Stale
debughookpointer. (:238-241,:460-466)newhookstoresxxxp->debughook = hookfor the debug hook butng_xxx_disconnect()never clears it. Latent (never dereferenced anywhere in the file), so not separately exploitable β recorded for completeness since the field survives until the private struct is freed at rmnode.
Reachability: identical gating to DF-3550/DF-3551 β module builds from /usr/src but does not ship; node creation requires root.
Exploit chain
None β pure logic defects in demo/template code; no memory-safety impact.
Fix validation
fix.diff fixes all four: strlen instead of sizeof, a real break in
the free-slot search, the missing channel[chan].dlci = dlci assignment,
and clearing debughook on disconnect. Kernel-rebuild validation skipped
per contract (Info severity).
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_testableFour-part fix in fix.diff (strlen instead of sizeof; break-on-free-slot; store channel[chan].dlci; clear debughook on disconnect); kernel rebuild validation skipped per contract for Info-severity template defects.
fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (hooktest dlci55 -> errno=55 twice; hooktest dlci5 -> errno=22), v1demo.c (hooktest mode; v1 generic cmds return newhook's errno synchronously from sendto(2)), fix.diff (four-part template repair, git-apply --check clean)
PoC changes
Same v1demo.c driver as DF-3550 (hooktest mode); NGM_CONNECT initially waited for a response message, but v1 generic commands signal errors synchronously via sendto(2) and send no response - reworked to read the errno from sendto.
Verified recommended fix
Apply fix.diff: fix the slot-search break, the sizeof/strlen off-by-one, the missing channel dlci assignment, and clear debughook on disconnect.
Verdict
Reproduced live on the stock v1 netgraph stack: hooktest shows 'dlci5' -> EINVAL(22) and 'dlci55'/'dlci05' -> ENOBUFS(55) on a fresh sample node with all 16 channels free. Root causes in sys/netgraph/ng_sample.c: (1) the free-slot search loop (:221-227) has no break - it only ever 'continue's, so chan always exits at XXX_NUM_DLCIS and every DLCI hook creation fails ENOBUFS; (2) 'cp = name + sizeof(NG_XXX_HOOK_DLCI_LEADIN)' (:210) counts the NUL, skipping the first digit - single-digit names are impossible ('dlci5' -> isdigit('\0') -> EINVAL) and 'dlci55' parses as 5; (3) channel[chan].dlci is never assigned from the parsed value (:228-232), so duplicate-DLCI detection (:218-220) and the downstream demux search (:369-371) are dead even with (1)+(2) fixed; (4) xxxp->debughook is stored in newhook (:240) but never cleared in ng_xxx_disconnect (:460-466) - a latent stale pointer, never dereferenced in the file. Pure logic defects in demo/template code (module builds but does not ship; node creation root-gated) - no memory-safety impact.
No comments yet.