# DF-1042 PoC — lapic_set_cpuid OOB write via crafted MADT

## Trigger

Crafted ACPI MADT table containing x2APIC entries that exercise the missing
bounds check in `lapic_set_cpuid()`. Two variants:

### Variant A — large LocalApicId

Generate a MADT with one BSP entry that has `LocalApicId=0` (passes probe
phase) and one entry with `LocalApicId=256` (triggers OOB write at
`apic_id_to_cpu_id[256]`).

### Variant B — many enabled entries

Generate a MADT with 257+ enabled `LOCAL_APIC` entries; the `cpu` counter
reaches 256 and `cpu_id_to_apic_id[256]` is written OOB.

## Generate the MADT

`gen_madt.py` emits a minimal MADT table with the header, one LAPIC address
override, the BSP x2APIC entry, and one malicious x2APIC entry with
`LocalApicId=256`. Output is ready for QEMU's `-acpitable file=...`.

The MADT subtable layout (`ACPI_MADT_LOCAL_X2APIC`):
- Type (u8) = 9 (LOCAL_X2APIC)
- Length (u8) = 16
- Reserved (u16)
- LocalApicId (u32)  ← attacker-controlled
- Uid (u32)
- LapicFlags (u32)   bit 0 = ENABLED

Run:
```sh
python3 gen_madt.py > malicious_madt.aml
```

## Run under QEMU

```sh
qemu-system-x86_64 -enable-kvm -m 512 -smp 2 \
    -acpitable file=malicious_madt.aml \
    -kernel /path/to/dfbsd-kernel \
    -initrd /path/to/initrd \
    -serial stdio -display none
```

## Expected output (Variant A)

```
[early boot]
MADT: cpu id 0, acpi uid 0, apic id 0
MADT: cpu id 1, acpi uid 1, apic id 256
Fatal trap 12: page fault while in kernel mode
fault virtual address   = 0x<address past apic_id_to_cpu_id>
lapic_set_cpuid(...) at lapic.c:1216
madt_x2apic_pass2_callback(...) at acpi_madt.c:342
madt_lapic_pass2(...) at acpi_madt.c:...
lapic_config(...) at lapic.c:1290
```

## Notes

- This is a boot-time firmware-data trigger, not a userspace exploit. The
  "attacker" must control the firmware/ACPI tables (malicious VM host, malicious
  UEFI, or a SeaBIOS patch).
- An alternate proof without QEMU is a unit test that constructs a fake
  `ACPI_MADT_LOCAL_X2APIC` and calls `madt_x2apic_pass2_callback` directly with
  `LocalApicId >= 256`, asserting that `apic_id_to_cpu_id[256]` was written.

## Kernel references

- `sys/platform/pc64/apic/lapic.c:1212-1217` — `lapic_set_cpuid` (no bounds check)
- `sys/platform/pc64/apic/lapic.c:122-123` — 256-element mapping arrays
- `sys/platform/pc64/acpica/acpi_madt.c:330-342` — pass2 callback, no bounds check
- `sys/platform/pc64/acpica/acpi_madt.c:413` — probe filter the enumeration phase
  fails to mirror
- `sys/platform/pc64/x86_64/mptable.c:571` — safe pattern (`else if (cpu < MAXCPU)`)
