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

sili_pm: sili_pm_read/sili_pm_write/sili_pm_set_feature dereference unchecked sili_ata_get_xfer() NULL β€” kernel panic when any PM register access runs against an exhausted ccb pool

Summary

All three PM register accessors take an ata_xfer from the shared ccb pool with no NULL check - the very next statement writes xa->fis->type, faulting the NULL page. CAM I/O coexists with port-thread PM maintenance (every PM hot-plug pass makes 1-3 PM accesses), so a port saturated with NCQ plus one hot-plug event yields panic. Same class as DF-4237 - three further unchecked instances. Hardware-gated on guest (no sili HBA); full code-trace + trigger. NULL-check + fail-soft fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4692 Β· 11 files
FileTypeDescriptionSize
README.md β€” 1.7 KB ↓ raw
VERDICT.md β€” 3.0 KB ↓ raw
trigger_sili_pm.c β€” 3.1 KB view raw
build.sh β€” 120 B view raw
run.sh β€” 148 B view raw
build.log β€” 106 B view raw
run.log β€” 1.1 KB view raw
env.txt β€” 664 B view raw
fix.diff β€” 1.2 KB view raw
manifest.json β€” 1.3 KB view raw
verdict.json β€” 2.7 KB view raw

DF-4692 β€” sili_pm: sili_pm_read / sili_pm_write / sili_pm_set_feature dereference unchecked sili_ata_get_xfer() NULL β€” kernel panic on ccb-pool exhaustion

Build & run

sh build.sh          # cc -O2 -Wall -o trigger_sili_pm trigger_sili_pm.c
sh run.sh            # ./trigger_sili_pm 15  (camcontrol reset/rescan storm)

Expected output

  • On hardware with a SiI 3124/3132 HBA + SiL3726 PM, port saturated with I/O: any hot-plug async-notify event (or the periodic PM state machine pass) makes the port thread call sili_pm_read(ap, 15, SATA_PMREG_EINFO, …) (sili.c:530) / sili_pm_check_good() (sili.c:580,654 β†’ sili_pm.c:584,589,610). With every command slot busy, sili_ata_get_xfer() returns NULL (sili.c:2322-2327) and xa->fis->type = … (sili_pm.c:645) faults the NULL direct-map page β†’ kernel panic.
  • On the audit QEMU guest: trigger builds and runs to completion against ata0/ata1 (no sili HBA exists; see env.txt) β€” the defective code is never reached. This is a hardware gate, not a false positive.

Root cause chain (path:line)

  1. sili_get_ccb() returns NULL when ap_ccb_free is empty β€” sili.c:2008-2014.
  2. sili_ata_get_xfer() propagates the NULL β€” sili.c:2322-2327.
  3. sili_pm_set_feature() dereferences without check β€” sili_pm.c:549-551.
  4. sili_pm_read() dereferences without check β€” sili_pm.c:643-645.
  5. sili_pm_write() dereferences without check β€” sili_pm.c:680-682.
  6. Live entries: PM hot-plug state machine EINFO poll (sili.c:530), sili_pm_check_good() (sili_pm.c:584,589), sili_pm_identify() (sili_pm.c:170-217), sili_pm_hardreset() (sili_pm.c:283-340).

Same defect class as DF-4237 (sili_cam.c, pass 1) β€” three further unchecked instances in this file.

VERDICT.md
↓ download raw

VERDICT β€” DF-4692

Status: inconclusive (hardware-gated) Β· code-path proven Β· expected impact: kernel panic (local DoS)

Reproduced?

No β€” and provably not runnable on this guest: the audit QEMU guest has no SiI 3124/3132 HBA (PCI inventory in env.txt: PIIX3 IDE + virtio only, 7 devices) and QEMU does not emulate the SiL3726 port multiplier, so sili(4) never attaches (dmesg has zero "sili" lines) and sili_pm_* is unreachable at runtime. The trigger builds (build.log) and runs (run.log): 24+ XPT_RESET_DEV / rescan operations were driven against the only buses present (ata0/ata1, nata driver) β€” benign, as expected when the sink is absent. This is the same evidence standard as the accepted DF-4502/DF-4503 sibling packs.

Why the defect is real (trace)

  1. sili_get_ccb() β€” sili.c:2003-2018 β€” returns TAILQ_FIRST(&ap->ap_ccb_free) unchanged when the free list is empty, i.e. NULL is a designed return.
  2. sili_ata_get_xfer() β€” sili.c:2317-2337 β€” propagates it: if (ccb == NULL) { DPRINTF("NULL ccb"); return (NULL); } (:2322-2327).
  3. sili_pm_set_feature() β€” sili_pm.c:549-551: xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]); xa->fis->type = … β€” no check.
  4. sili_pm_read() β€” sili_pm.c:643-645 β€” same unchecked deref.
  5. sili_pm_write() β€” sili_pm.c:680-682 β€” same unchecked deref. On NULL xa, xa->fis loads from ~offset 0x10 of the direct map β†’ supervisor fault on the NULL page β†’ kernel panic.

Reachability of pool exhaustion on a PM port

The port-thread PM state machine issues sili_pm_read(ap, 15, EINFO) on every hot-plug notification pass (sili.c:529-534) and sili_pm_check_good() performs up to three further PM accesses per event (sili_pm.c:584, 589, 610). These run under the port lock, but commands issued before the event still occupy slots (ap_active). CAM I/O and PM maintenance are not mutually exclusive (sili_cam_action() issues under ap_sim_lock, sili_cam.c:122/202), so a saturated port (all SILI_MAXCMDS slots busy β€” e.g. NCQ depth against a PM with several disks) plus one async-notify interrupt yields NULL at step 3/4/5. ata_xfer has no reserved slot for PM maintenance (the err_ccb reservation is only for error recovery, sili.c:2033-2050).

This is the same class as DF-4237 (sili_cam.c probe NULL deref, pass 1, Medium) β€” three additional unchecked instances live in this file, including the periodic EINFO poll that re-fires on every hot-plug event.

Realistic impact ceiling

Kernel panic (unprivileged trigger: any user keeping the disks busy while a hot-plug event / camcontrol rescan lands). No write primitive β€” NULL-page read/write fault. Severity Medium, consistent with DF-4237.

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 validation not testable: the guest has no SiI 3124/3132 HBA and QEMU does not emulate the SiL3726 port multiplier, so neither baseline nor patched kernel can reach sili_pm_* at runtime. fix.diff is a line-accurate source-level fix (all referenced helpers declared in sili.h); code-reviewed only.

env.txt (PCI inventory, 0 sili dmesg lines); fix.diff
↓ fix.diffper-fix-DF-4692

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md 'Why the defect is real' steps 1-5; env.txt proves the hardware gate; run.log shows the trigger driving 24+ XPT reset/rescan ops against the only buses present (ata0/ata1, nata).

PoC changes

No seed existed; trigger written fresh for this pack (stdarg.h fix applied during guest build).

Verified recommended fix

NULL-check sili_ata_get_xfer() result in sili_pm_set_feature/sili_pm_read/sili_pm_write and return EBUSY.

Verdict

Code-trace proven; not runtime-triggerable on the audit guest because no sili(4) HBA exists there (hardware-gated, not a false positive): sili_ata_get_xfer() returns NULL when the ccb free list is empty (sili.c:2322-2327 -> 2003-2018) and sili_pm_read (sili_pm.c:643-645), sili_pm_write (:680-682) and sili_pm_set_feature (:549-551) dereference xa->fis unconditionally. Live entry: every PM hot-plug async-notify pass runs sili_pm_read(ap,15,EINFO) from the port thread (sili.c:530) and sili_pm_check_good performs up to three more PM accesses per event (sili_pm.c:584,589,610); CAM I/O (issued under ap_sim_lock, sili_cam.c:122/202) coexists with port-thread PM maintenance, so a saturated port + one hot-plug event = NULL deref -> panic. Same class as DF-4237 (sili_cam.c), three further instances.