# DF-4240 — VERDICT

**REPRODUCED** (kernel memory corruption, crash; fix validated on a rebuilt kernel).

## What was proven, and how

`sys/dev/raid/dpt/dpt_scsi.c` is compiled into the guest's X86_64_GENERIC kernel
(`nm /boot/kernel/kernel` → `dpt_attach` @ 0xffffffff80439500, `dpt_init` @ …89f0+,
`dptallocccbs` @ …75e0, `dpt_action` @ …8010). There is no DPT HBA on the guest, so the
rig (`dfdpt_rig.c`) stands in for the hardware only:

1. **Baseline, controlled** (`chan8 4`, `run.log` + `run_chan4_controlled.log`):
   fabricate a post-`dpt_init` softc (real bus_dma, real DMA-mapped dccb array, free list
   built by production `dptallocccbs`), set `channels = 4` — exactly what production
   `dpt_init` computes from a device-fed `MAX_CHAN = 3` (dpt_scsi.c:1313, no clamp) — and
   call the **production `dpt_attach`**. Observed:
   - `attach_ret=4`: the loop ran iteration i=3 out of bounds;
   - `&sims[3] == &paths[0]` (rig prints 0 bytes apart): iteration 3 stored a `cam_sim*`
     into `paths[0]`, clobbering the path pointer installed by iteration 0 —
     `paths[0] == sims[3]` value confirmed ("type confusion: YES");
   - canary `commands_processed` 0x41414141 → 0x8d481e20 and `lost_interrupts`
     0x42424242 → 0xfffff800: together the kernel pointer 0xfffff8008d481e20 that
     `xpt_create_path(&dpt->paths[3])` wrote across both counters (8-byte store at
     `&paths[3] == &commands_processed`).
2. **Baseline, catastrophic** (`chan8 8`, `run_channels8_fault.log`): with channels=8 all
   ten OOB stores execute; the store at `&sims[7]` (== `&channels`+`max_id`) corrupts the
   loop-bound field `channels` with the low 32 bits of a kernel pointer, the loop runs
   away, and production `xpt_create_path` faults writing a `cam_path*` to an unmapped
   page 77,824 bytes past the softc:
   `Fatal trap 12: page fault … supervisor write data, page not present …
   xpt_create_path+0x6b: movq %rbx,(%r12)` — system down in DDB.
3. **Fix validation** (`fix_validation.log`): all three dpt fixes applied to the guest's
   /usr/src, kernel rebuilt (nativekernel, gcc 8.3) and booted. The rig's `fullinit`
   path emulates the EATA config handshake (virtual HBA kthread: watches HA_WCOMMAND,
   DMA-feeds the 0xc1 config page incl. `MAX_CHAN=7`, raises HA_AIRQ; the handshake
   emulation is race-free — see rig comments) so the **production `dpt_init`** parses the
   device-fed config itself:
   - `dpt7: controller reported 8 channels; clamping to 3` (the fix)
   - `dpt7: FW Rev. , 3 channels, 16 CCBs` (production init banner — full success)
   - production `dpt_attach` returned 3; canaries `0x41414141→0x41414141`,
     `0x42424242→0x42424242` **intact**; slot at +176 still reads 0x4242424241414141
     (the canary pair) and +184 reads 0xf00000003 (`channels=3`, `max_id=15` — the
     softc's own fields, no pointer store); `runaway=0`.

   Baseline behavior gone on the patched kernel; normal 3-channel attach unaffected.

## Threat model

All inputs are device-fed: `conf.MAX_CHAN` arrives over the EATA PIO/DMA config protocol
from the PCI card (a hostile or glitching controller — firmware bug, power blip, PCI
reset — or a passed-through/virtualized device). Attach runs once per boot/hotplug, so
the corruption is single-shot but destructive: with MAX_CHAN=3..4 it type-confuses
`paths[0]` (CAM later treats a `cam_sim*` as a `cam_path*`) and turns two counters into a
pointer; with MAX_CHAN≥5 it corrupts `channels`/`max_id`, the TAILQ linkage of
`dpt_softcs`, and — via the loop-bound feedback — writes an unbounded stream of kernel
pointers past the softc (demonstrated page fault). Local unpriv→root is not the threat
model here; this is a hostile/failed-device kernel-corruption bug.

## Notes / caveats

- The rig's `path0_holds_sim` detector compares the value at `&sims[3]` with the value at
  `&paths[0]` — the *same address* — so it prints "YES" even on the patched kernel where
  no OOB store happened (`&paths[0]` legitimately holds its own path). The canaries and
  `attach_ret`/`channels` are the real discriminators; VERDICT relies on those.
- The chan8 baseline bypasses `dpt_init` (fabricates its output), because the full
  handshake emulation was built for the fix-validation run; the `fullinit` path closes
  that gap end-to-end on the patched kernel (device → MAX_CHAN → clamp).
- Earlier seed evidence (`dpt_df4240_layout.c`, old `run.log`) was a userland ABI layout
  proof only; superseded by this run.

Bottom line: the unclamped, controller-fed channel count drives production `dpt_attach`
into demonstrated out-of-bounds pointer stores (type confusion over live softc members
and an uncontrolled runaway write), and the one-line clamp in `fix.diff` removes it.

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