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

pccard_driver_added NULL-deref panic (pf->dev) after a CIS-error card, via kldload of any pccard client driver

Summary

attach_card bails on card.error with pf_head populated but pf->dev NULL for every function (children created only later); a subsequent devclass_add_driver (kldload any pccard client) runs driver_added which does device_get_state(pf->dev) raw-deref NULL. The sibling walk in detach_card guards exactly this condition; driver_added forgot. Root-gated (kldload). One-line-guard fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5357 Β· 5 files
FileTypeDescriptionSize
README.md β€” 1.5 KB ↓ raw
VERDICT.md β€” 1.8 KB ↓ raw
fix.diff β€” 377 B view raw
verdict.json β€” 2.4 KB view raw
manifest.json β€” 677 B view raw

DF-5357 β€” pccard_driver_added NULL-deref panic on kldload after a CIS-error card

What

pccard_driver_added() (sys/bus/pccard/pccard.c:1074-1103):

1081  STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
1082      if (STAILQ_EMPTY(&pf->cfe_head))
1083          continue;
1084      child = pf->dev;
1085      if (device_get_state(child) != DS_NOTPRESENT)   /* child == NULL! */

pf->dev is NULL whenever pccard_attach_card() bailed out early β€” the sc->card.error return at pccard.c:205-208 (e.g. CFTABLE "too many io/mem spaces", memspace length 0/lengthsize 0 β€” pccard_cis.c:1047-1055,1167-1175, 1185-1203) or "Card has no functions" at pccard.c:209-212. Functions and their cfe lists were already parsed into pf_head, but children are only created further down (pccard.c:253-259).

device_get_state() is return(dev->state); (sys/kern/subr_bus.c:1824-1827) β€” NULL + offsetof(struct device, state) dereference β†’ kernel panic.

Trigger

  1. Insert a card whose CIS sets card->error (many single-tuple ways, e.g. a CISTPL_CFTABLE_ENTRY with 5 io ranges after a valid CISTPL_CONFIG).
  2. kldload any module registering a driver on the pccard bus (devclass_add_driver β†’ bus_generic_driver_added β†’ pccard_driver_added).

kldload requires root, hence Low severity (root-triggered local panic / defense-in-depth). All in-tree pccard clients are compiled into GENERIC, so the realistic trigger is an out-of-tree or custom-built client module.

Fix

if (pf->dev == NULL) continue; before the state check β€” fix.diff.

VERDICT.md
↓ download raw

DF-5357 VERDICT β€” pccard_driver_added NULL pf->dev dereference

Status: statically certain NULL-deref; trigger requires root (kldload), Low severity; not runnable in QEMU (no PCMCIA bridge)

Trace

  • Card inserted with erroring CIS β†’ pccard_read_cis populates sc->card.pf_head (functions + cfe chains) and increments sc->card.error (sys/bus/pccard/pccard_cis.c:1053/1173/1188/1202).
  • pccard_attach_card returns 1 at sys/bus/pccard/pccard.c:205-208 before the child-creation loop at pccard.c:233-259 β€” every pf->dev remains NULL (functions are M_ZERO-allocated, pccard_cis.c:790-791/837-838).
  • kldload of a module with a DRIVER_MODULE(x, pccard, ...) β†’ devclass_add_driver β†’ bus_generic_driver_added β†’ pccard_driver_added.
  • pccard.c:1084-1085: child = pf->dev (NULL) β†’ device_get_state(NULL) β†’ return(dev->state); (sys/kern/subr_bus.c:1824-1827) β†’ page-fault at offsetof(struct device, state) β†’ panic.

The sibling walk in pccard_detach_card guards this exact condition (if (pf->dev == NULL) continue;, pccard.c:304-305); pccard_driver_added simply forgot it.

Impact

Local kernel panic; the triggering action (kldload) needs root, so this is hardening/robustness (Low), same class as DF-1489.

Not reproduced on guest

No PCMCIA bridge in QEMU β†’ pf_head can never be non-empty on the guest; static certainty only. Model adds nothing (single missing NULL check).

Fix

One-line guard (fix.diff), mirroring pccard.c:304-305.

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

One-line NULL guard; cannot be boot-tested without PCMCIA hardware. Semantically identical to the existing guard in pccard_detach_card.

findings/poc/DF-5357/fix.diff
↓ fix.diffper-fix-DF-5357

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md trace; fix.diff one-line guard mirroring pccard.c:304-305

PoC changes

No seed PoC; static pack (hardware+root trigger).

Verified recommended fix

Skip functions without a child device in pccard_driver_added (if (pf->dev == NULL) continue;), mirroring the pccard_detach_card guard at pccard.c:304-305.

Verdict

Statically certain NULL-deref panic, root-triggered (Low). pccard_attach_card returns early on card->error (sys/bus/pccard/pccard.c:205-208) after read_cis already populated pf_head, leaving pf->dev NULL for every function (children are only created at pccard.c:253-259). A later devclass_add_driver of any pccard-bus module (kldload) runs pccard_driver_added, which does child = pf->dev at pccard.c:1084 and passes it to device_get_state (pccard.c:1085) β€” device_get_state is a raw dev->state load (sys/kern/subr_bus.c:1824-1827), so NULL dereferences and the kernel panics. The sibling loop pccard_detach_card guards exactly this ('if (pf->dev == NULL) continue;', pccard.c:304-305). Not runnable in QEMU (no PCMCIA bridge): pf_head can never be populated on the guest, static evidence only.