# DF-1606 — acpi_hp `cmi_order[128]` heap-overflow PoC

## TL;DR

`sys/dev/acpica/acpi_hp/acpi_hp.c:1180-1202` populates a fixed-size array
`sc->cmi_order[128]` (declared at softc line 147) by iterating WMI CMI
instances reported by the BIOS. The shift loop at line 1190 starts at
`i = sc->cmi_order_size` and writes `cmi_order[i]` with **no upper bound
check**. On the 129th successful CMI read, `cmi_order_size == 128`, and the
shift loop (or the final write at line 1198) writes `cmi_order[128]` — 8
bytes past the array end, into the slab slot adjacent to
`struct acpi_hp_softc`. Each additional CMI instance adds 8 more bytes of
OOB write.

`/dev/hpcmi` is created with mode 0644 on HP systems that expose the CMI WMI
GUID (`acpi_hp_attach` line 523: `make_dev(..., 0644, "hpcmi")`). Any
unprivileged user can `cat /dev/hpcmi` to trigger the loop on first read.

The bug is **real** but the live trigger requires HP ACPI/WMI hardware that
exposes >128 CMI instances (the finding cites EliteBook 840 G5/G8). This
DragonFly audit guest has no HP WMI hardware at all — `acpi_hp.ko` does not
attach, no softc is allocated, `/dev/hpcmi` does not exist, and `has_cmi`
would be 0 even if the driver did attach. We prove the primitive at the
harness level instead.

## How to reproduce

```sh
./build.sh
./run.sh
```

Expected output (the trigger case):

```
maxInstance           = 140
cmi_order array bound = 128 entries (1024 bytes)
cmi_order_size after  = 140 entries (overflow by 12 entries)
adjacent-slab canary corrupted bytes = 60 (first at +0)
[OK] heap OOB write past cmi_order[127] confirmed: 60 bytes
     kernel path: acpi_hp_hpcmi_read lines 1190-1202
     in-kernel effect: corruption of adjacent kmalloc-2048 slab
     slot -> INVARIANTS panic on default GENERIC; silent heap
     corruption on noinv.
```

## Why a harness instead of an in-guest trigger

The kernel path runs only when `/dev/hpcmi` exists and is read.
`/dev/hpcmi` is created by `acpi_hp_attach()` only when the CMI WMI GUID is
detected by the underlying `acpi_wmi` driver — which in turn requires HP
ACPI/WMI tables provided by HP firmware. This QEMU/KVM guest has no such
hardware, so the device never exists. Without HP hardware the loop body is
genuinely dead code at runtime; we prove the primitive by reproducing the
exact loop logic in userspace with a heap-allocated fake softc whose layout
matches the kernel's `kmalloc`'d softc.

## Why no in-guest escalation chain

Phase 6 requires pushing memory-corruption primitives to `uid=0`. For
DF-1606 we hit a valid hard blocker: the live trigger conditions (HP
EliteBook + >128 CMI instances) cannot be produced in this QEMU guest. The
primitive is real (the harness confirms a 60-byte OOB write into an adjacent
slab slot), but exercising it through the actual kernel path requires
hardware this guest does not have. Even `kldload acpi_hp` (which is itself a
root-only action that would invalidate any chain per the Phase 6 bright-line
rule) would not help — the driver would not attach without underlying WMI
hardware, and `has_cmi` would remain 0 so the loop body never executes.

## Fixed-logic variant

`df1606_fixed.c` incorporates the same bound check the `fix.diff` introduces
at line 1180 (top of the `else` branch). Build + run:

```sh
cc -O2 -o df1606_fixed df1606_fixed.c
./df1606_fixed
```

Expected output:

```
maxInstance           = 140
cmi_order_size after  = 128 entries (capped at array bound 128)
adjacent-slab canary corrupted bytes = 0
[OK] fix confirmed: bound check stopped the insert at 128; no OOB write past cmi_order[127].
```

## Fix

See `fix.diff` (validated: applies cleanly, compiles with rc=0, see
`fix_build.log`). The patched kernel boots cleanly.
