# DF-4964 VERDICT — sili(4) undersized-BAR(2) OOB MMIO at attach

## Bottom line

**REPRODUCED — panic (demonstrated), plus silent adjacent-KVA corruption
variant.  Fix validated on the guest (both manifestations gone).**

`sili_pci_attach()` maps BAR(2) as the port-register/LRAM window
(sys/dev/disk/sili/sili_attach.c:170-179) and allocates `ad_nports` (2 or 4)
ports (:253-268).  `sili_port_alloc()` then carves each port's subregion at
`SILI_PORT_REGION(port) = 8192 * port` (sys/dev/disk/sili/sili.c:156-163)
with **no check that the mapped window actually covers the subregion**.
`bus_space_subregion()` on x86_64 is unchecked pointer arithmetic
(sys/cpu/x86_64/include/bus_dma.h:78-84), and `bus_alloc_resource_any()`
maps exactly the BAR's decoded size.  A SiI 3124/3132 whose BAR(2) decodes
fewer than `nports * 8KB` bytes (malfunctioning silicon, re-flashed card, or
crafted PCIe/Thunderbolt device) therefore makes the first per-port MMIO in
`sili_port_alloc()` — `sili_pwrite(ap, SILI_PREG_INT_DISABLE, ...)` at
sili.c:173, and the CTL_SET/STATUS accesses at sili.c:185-186 — land
entirely outside the pmap-mapped window.

## How it was reproduced (guest, DragonFly 6.5-DEVELOPMENT #0/#1,
## X86_64_GENERIC, sili compiled in)

The PoC kld module `df4964.ko` fabricates the *hardware state only* and then
calls the driver's own `sili_port_alloc(sc, 3)` (SiI3124 → 4 ports → port 3
subregion at +24KB):

- **canary mode** (default): a real 64KB device mapping (phys 0x60000000 via
  `pmap_mapdev_uncacheable`, exactly what `bus_alloc_resource_any` produces
  for a BAR); the driver is handed a 4KB "BAR(2)" (sc_piot/sc_pioh +
  fake `struct resource` r_start/r_end).  Canaries planted at +0x7000 and
  +0x7014 (outside the window, inside the real map).
  Result (run.log):
  `canary@+0x7014 after = 00000fff` (SILI_PREG_INT_MASK) and
  `canary@+0x7000 after = 00000001` (SILI_PREG_CTL_RESET) —
  **the driver's writes landed 28KB past its 4KB window, silently**.
  (The v1 run additionally showed the OOB *read* at sili.c:186 returning
  garbage with READY set → "Port 3 will not go into reset" — an OOB read
  flipping driver control flow.)
- **panic mode** (`kenv df4964.panic=1`): the fake 4KB BAR is parked inside a
  freshly `kmem_alloc`+`kmem_free`d (PTE-torn-down, pmap_extract-verified)
  KVA hole — i.e. the BAR window is mapped but everything past it is not,
  which is the real-hardware undersized-BAR situation.  The driver's first
  OOB write faults (panic.txt):
  ```
  Fatal trap 12: page fault while in kernel mode
  fault virtual address = 0xfffff80118c67014     (= fake_bar + 0x7014, as predicted)
  fault code           = supervisor write data, page not present
  Stopped at sili_port_alloc+0x360: movl $0xfff,0x1014(%rdx)
  ```
  Guest died (`vm.sh status` → down) — panic classification met.

## Exploitability assessment

- Trigger surface: requires a SiI 3124/3132 (or compatible-ID) PCIe device
  presenting an undersized BAR(2) — crafted/malfunctioning hardware, or a
  QEMU-emulated device.  Not triggerable by unprivileged local users on
  normal hardware; boots/kldloads are when the path runs.
- Primitive: OOB MMIO write at device-adjacent KVA, offset
  `8192*port + reg` (port ≤ 3 from the ID table; reg small), value limited
  to fixed register constants (0xfff INT_MASK, 0x1 CTL_RESET, ...).  OOB MMIO
  read feeding driver branches.
- Ceiling: kernel page fault → panic at attach/boot (DoS), or silent
  corruption of whatever mapping sits past the BAR (on this guest's layout,
  adjacent kld/mapdev mappings).  uid=0 escalation is not realistic: the
  write offsets/values are device-register-shaped, not attacker-arbitrary,
  and targets are device-mapping KVA, not kernel heap objects.
  Severity therefore Low (crafted-hardware precondition).

## Fix validation (same guest, fix.diff applied to /usr/src, quickkernel,
## installkernel, reboot into #1 Sep 25 12:08 build)

- canary mode: `pci0: port regs window too small for port 3`,
  `sili_port_alloc returned 22 (EINVAL)`, **canaries untouched**
  (deadbeef/cafebabe intact) — run.2.log.
- panic mode against the identical unmapped-hole construction that killed
  the unpatched kernel: `TRIGGER: survived?? rc=22`, guest stayed up
  (run.2.log, `uptime` right after).
- Baseline (unpatched): canaries clobbered + Fatal trap 12 + guest down
  (run.log, panic.txt).

## Notes

- The vulnerable decision surface (mapping BAR(2) and choosing nports) is in
  sili_attach.c; the unchecked subregion arithmetic is in sili.c
  (`sili_port_alloc`), so the check belongs there (fix.diff) — it then
  covers any future caller.
- Incidental observation while building the PoC (NOT this finding, pmap.c
  is out of scope for this file): `pmap_extract()` on a kernel VA whose PTE
  was cleared but whose PT page is still present returns `va & PAGE_MASK`
  (no frame bits) because pmap.c:1739 tests `*pt` instead of `*ptep` for
  PG_V — callers that treat any non-zero return as "mapped with this phys
  address" get a bogus physical address for unmapped kernel VAs.

## 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.
