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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5364 Β· 11 files
FileTypeDescriptionSize
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).

VERDICT.md
↓ download raw

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_testable
baseline no→ patch + rebuild →patched clean

fix.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
↓ fix.diffper-fix-DF-5364

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