# DF-1473 — mpr_sas_lsi.c u16 truncation in EventDataLength*4

## Verdict
**REPRODUCED (source-level harness).** The bug is real; impact ceiling is a
kernel page-fault panic (EventDataLength=0x4000 → kmalloc(0) → ZERO_LENGTH_PTR
deref) and/or OOB heap read of the M_MPR slab bucket (EventDataLength=0x4001
→ kmalloc(4) → under-sized buffer parsed by the event-handler thread). The
path is exercised only by an LSI SAS3 HBA (PCI 1000:0097) sending a crafted
event-reply DMA frame; QEMU does not emulate that HBA so the kernel code path
cannot be triggered on this guest. fix.diff applies cleanly and
`nativekernel` succeeds (rc=0).

## Mechanism (`sys/dev/raid/mpr/mpr_sas_lsi.c`)
1. Line 136: `u16 sz;`
2. Line 148: `sz = le16toh(event->EventDataLength) * 4;` — `event->EventDataLength`
   is `U16` (`sys/dev/raid/mpr/mpi/mpi2_ioc.h:563`); the multiplication by 4
   promotes to int (32-bit) but the assignment back to `u16 sz` truncates the
   high 16 bits.
3. `EventDataLength = 0x4000` (16384) → 0x4000 * 4 = 0x10000 → truncated to **0**.
4. Line 149: `fw_event->event_data = kmalloc(0, M_MPR, M_ZERO|M_NOWAIT);`
   In DragonFly `kmalloc(0)` returns `ZERO_LENGTH_PTR` (`-8`), which is
   non-NULL, so the NULL check on line 150 passes.
5. Line 156: `bcopy(event->EventData, fw_event->event_data, 0)` is a no-op.
6. Later (taskqueue thread, `mprsas_fw_work`), `fw_event->event_data` (== -8)
   is dereferenced to read `data->ReasonCode` → page fault → panic.
7. `EventDataLength = 0x4001` → sz truncated to **4** → kmalloc(4) succeeds →
   bcopy copies 4 bytes → event-struct parsing reads fields past byte 4 →
   OOB heap read of the M_MPR slab bucket.

## Harness proof (`harness.c`)
Compares the buggy (u16-truncated) and correct (u32) sizes for six EDL values:

```
case                                        EDL     buggy_sz   correct_sz
normal EventDataLength=8                      8           32           32
EventDataLength=24 (reply sz)                24           96           96
EventDataLength=0x4000 -> sz=0            16384            0        65536
EventDataLength=0x4001 -> sz=4            16385            4        65540
EventDataLength=0x4002 -> sz=8            16386            8        65544
EventDataLength=0xFFFF -> sz=0xFFFC       65535        65532       262140
Buggy: 4/6 cases produce a truncated (wrong) size.
```

## Exploit-chain note
Trigger requires a malicious or compromised SAS3 HBA in a PCIe slot (or a
passthrough VFIO of one into a VM — a realistic cloud-tenant-attack scenario).
On the audit guest (no SAS3 HW) the path cannot be exercised. The primitive
characterization is: a single malicious event-reply DMA frame triggers a
panic (DoS) or a controlled OOB heap read; with a sustained stream of crafted
events the OOB reads can be used for heap disclosure / grooming. Realistic
ceiling is DoS + info-leak.

## PoC changes
- Original PoC was README-only.
- Added harness.c, build/run scripts, env, logs, fix.diff, VERDICT.md,
  manifest.json.

## Fix
`fix.diff` declares `sz` as `u32` so the multiplication result is not
truncated. Matches the finding markdown proposal ("declare sz uint32_t").

## Fix-validation
`patch -p1 --forward` succeeds (hunk #1 at line 133). `nativekernel`
completes with rc=0 (saved as `fix_build.log`). No run-time exercise is
possible because no SAS3 HBA exists on the guest → `fix_status:
"not_testable"`. Diff applies and compiles; changed logic closes the cited
truncation.
