Unchecked kmalloc(M_NOWAIT) in CISTPL_FUNCID/CISTPL_CONFIG handlers: NULL-page write on allocation failure
Summary
Both pf allocations are M_NOWAIT yet immediately dereferenced (offsetof(number)==0 -> write to vaddr 0 on failure) - the CFTABLE handler in the same function checks correctly, so an oversight. Preconditions: kmalloc pressure at insertion (optionally manufactured via DF-5362+5361) + CIS leading with FUNCID/CONFIG. Model-verified (failure injection -> NULL-page write). NULL-check + DF-2319-hardening fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5364 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.1 KB | β raw | |
| VERDICT.md | β | 2.6 KB | β raw | |
| poc_pccard_cis2.c | β | 37.0 KB | view raw | |
| build.sh | β | 104 B | view raw | |
| run.sh | β | 110 B | view raw | |
| build.log | β | 495 B | view raw | |
| run.log | β | 258 B | view raw | |
| env.txt | β | 918 B | view raw | |
| fix.diff | β | 1.2 KB | view raw | |
| manifest.json | β | 999 B | view raw | |
| verdict.json | β | 3.8 KB | view raw |
DF-5364 β Unchecked kmalloc(M_NOWAIT) in FUNCID/CONFIG tuple handlers: NULL-page write on allocation failure
File: sys/bus/pccard/pccard_cis.c (pass 2, GLM 5.3). Full narrative: VERDICT.md.
Reproduce (deterministic userland model of the kernel parse/consume chains)
./build.sh # cc -O2 -Wall -o poc_pccard_cis2 poc_pccard_cis2.c ./run.sh # ./poc_pccard_cis2 kmallocfail
Expected decisive output: the RESULT: DF-5364 ... line (see run.log; exit 0).
Built and executed on the DragonFly guest toolchain (DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT),
cc 8.3. The QEMU guest has no PCMCIA bridge, so the kernel path itself is not
enterable; the model mirrors pccard_cis.c:100-444 (walk), :612-1260 (handlers),
decode_funce :1262-1294 and the pccard.c:429-474 consumer loops statement-by-
statement. Run ./poc_pccard_cis2 all for every scenario incl. negatives.
What it proves: Injected alloc failure -> (int)(NULL+0) write at :792/:839 (offsetof(number)==0). Recommended fix: NULL-check both allocations (error++ + break) and guard CFTABLE top-of-case (also hardens DF-2319) (fix.diff).
DF-5364 β Unchecked kmalloc(M_NOWAIT) in FUNCID/CONFIG handlers: NULL-page write
Verdict in one line
Statically certain (model-demonstrated address computation): both
kmalloc(sizeof(*state->pf), M_DEVBUF, M_NOWAIT | M_ZERO) calls in the
tuple parser are immediately dereferenced with no NULL check β on
allocation failure the very next statement writes through NULL.
Root cause (path:line)
CISTPL_FUNCID handler:
if ((state->pf == NULL) || (state->gotmfc == 2)) {
state->pf = kmalloc(sizeof(*state->pf), M_DEVBUF,
M_NOWAIT | M_ZERO); /* :790-791 unchecked */
state->pf->number = state->count++; /* :792 NULL+0 write */
...
CISTPL_CONFIG handler:
if (state->pf == NULL) {
state->pf = kmalloc(sizeof(*state->pf),
M_DEVBUF, M_NOWAIT | M_ZERO); /* :837-838 unchecked */
state->pf->number = state->count++; /* :839 NULL+0 write */
The CFTABLE handler in the same function does it correctly
(if (cfe == NULL) { DPRINTF(...); goto abort_cfe; } at :910-913), which
makes the omission plain.
offsetof(struct pccard_function, number) == 0, so the write is to virtual
address 0 β an immediate kernel page fault / panic on amd64 and i386.
Trigger / preconditions
pccard_parse_cis_tuple runs at every card insertion (pccard.c:195). The
allocation is small (~150 B), so failure requires kernel memory pressure
(kmalloc M_NOWAIT failing) β e.g. an already-memory-starved system at card
insert, or an attacker combining it with the DF-5362+DF-5361 unbounded
leak-loop to manufacture the exhaustion, then re-inserting a card whose
CIS leads with FUNCID or CONFIG. Low severity (DoS on top of memory
exhaustion), certain as a code defect.
Model evidence (run.log, scenario kmallocfail)
=== S6 kmallocfail === RESULT: DF-5364 FUNCID kmalloc(M_NOWAIT) returned NULL; next kernel statement writes *(int*)(NULL+0) = 0 -> NULL-page write, panic
(The model injects a single allocation failure at the FUNCID pf alloc and reports the address the kernel write would target; offsetof(number)=0.)
Fix
Check for NULL and bail out of the tuple (fix.diff, also adds the top-of-case guard in CFTABLE that hardens the DF-2319 default_cfe/pf NULLs so the new break-outs cannot re-expose them).
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_testablefix.diff authored post-verification; cannot be exercised against real PCMCIA hardware here. Reviewed: the break at FUNCID exits the switch with pf left NULL, and the CONFIG break likewise - the added CFTABLE top-of-case guard turns any subsequent CFTABLE into a clean error instead of the DF-2319 NULL deref; card->error++ still causes pccard_attach_card to reject the card at pccard.c:204.
findings/poc/DF-5364/fix.diff
Confirmed kernel references
Detail
Exploit chain
memory-exhausted kernel (optionally manufactured via DF-5362+DF-5361 unbounded leak) + card insertion whose CIS contains FUNCID or CONFIG -> kmalloc(M_NOWAIT) returns NULL at pccard_cis.c:790/:837 -> (int)NULL = count written at :792/:839 -> kernel NULL-page write panic
Evidence (decisive lines)
run.log (scenario 'kmallocfail': 'RESULT: DF-5364 FUNCID kmalloc(M_NOWAIT) returned NULL; next kernel statement writes *(int*)(NULL+0) = 0 -> NULL-page write, panic'); VERDICT.md (contrast with the correct :910-913 check); fix.diff
PoC changes
Wrote the full model (poc_pccard_cis2.c) with an allocation-failure injector; fixed the model's CIS-image encoding after the first guest run (attribute-space tuple bytes are 2 bus-bytes apart per pccardvar.h:255-259).
Verified recommended fix
NULL-check both kmalloc results (state->card->error++; break;), and add a top-of-case guard in CFTABLE_ENTRY (default_cfe==NULL || pf==NULL -> error++) so the new break-outs cannot re-expose the known DF-2319 NULLs.
Verdict
Statically certain code defect, model-demonstrated write target; kernel path not enterable in QEMU (no PCMCIA bridge). Both pccard_function allocations in pccard_parse_cis_tuple are M_NOWAIT yet unchecked: CISTPL_FUNCID at pccard_cis.c:790-791 is immediately followed by state->pf->number = state->count++ at :792, and CISTPL_CONFIG at :837-838 by the same at :839 - with offsetof(struct pccard_function, number)==0 an allocation failure becomes a write to virtual address 0 (immediate kernel panic). The CFTABLE handler in the same function checks its allocation correctly at :910-913, so the omission is an oversight, not policy. Preconditions: kmalloc failure at card insertion (small ~150B request, so kernel memory pressure is required - e.g. a system starved by the DF-5362+DF-5361 leak-loop, then re-inserted with a card whose CIS leads with FUNCID or CONFIG). Low severity panic-on-exhaustion DoS; reported for completeness/defense-in-depth. Model scenario 'kmallocfail' injects one allocation failure at the FUNCID pf alloc and prints the exact kernel write ('writes (int)(NULL+0) = 0 -> NULL-page write, panic').
No comments yet.