# DF-5352 VERDICT — reproduced (kernel panic from driver-facing legacy-config ABI), Low severity

## Bottom line

REPRODUCED on the guest both ways:

- production-behaviour kernel (X86_64_GENERIC minus `options INVARIANTS`;
  stock GENERIC ships without INVARIANTS): a KLD calling
  `MachIntrABI.legacy_intr_config(irq=24, LEVEL, LOW)` where irq 24 is an
  MSI-X map (im_gsi == -1) panics the kernel:
  `panic: ioapic_gsi_search: no I/O APIC` (ioapic.c:415-427, reached via
  ioapic_abi.c:1038 `ioapic_gsi_pin(map->im_gsi)`).
- INVARIANTS kernel: the same trigger panics at the debug assert
  `panic: assertion "irq >= 0 && irq < ioapic_abi_legacy_irq_max" failed in
  ioapic_abi_legacy_intr_config at ioapic_abi.c:999`.

Either way a single bad driver call — `bus_config_intr()` on an IRQ that
isn't a legacy map, e.g. the MSI/MSI-X vector the driver itself just
allocated — is promoted to a kernel panic. The function returns void; no
error path exists. On production kernels the guard KKASSERTs at
ioapic_abi.c:999/:1005 compile to nothing (sys/sys/systm.h:94-122), the
search loop at :1000-1004 falls through with cpuid==ncpus, and :1022/:1035
write flag/trigger fields into a wrong-type map before :1038 feeds
im_gsi=-1 into the gsi lookup. Fix validated: with runtime range/type
checks (early return) the identical kldload returns cleanly, no panic.

## Reproduction (baseline)

Production kernel (run_prodkernel.log):

    legacy_crash: msix_alloc rc=0 irq=24
    legacy_crash: calling legacy_intr_config(irq=24, LEVEL, LOW); production kernel should panic in ioapic_gsi_search (im_gsi == -1)
    panic: ioapic_gsi_search: no I/O APIC
    ioapic_gsi_search() at ioapic_gsi_search+0x3a 0xffffffff80b7350a
    Debugger("panic")

INVARIANTS kernel (panic_kkassert.txt — captured 07:58 UTC on the stock
X86_64_GENERIC):

    legacy_crash: ... irq=24
    panic: assertion "irq >= 0 && irq < ioapic_abi_legacy_irq_max" failed in ioapic_abi_legacy_intr_config at /usr/src/sys/platform/pc64/apic/ioapic_abi.c:999

Note on trigger selection: the first attempt used irq 19 and returned
*without* panic — on this QEMU guest irqs 16..23 are legacy level/low maps
(on some CPU), so ioapic_abi_legacy_intr_config() found a LEGACY map,
matched trig/pola, and took the early return at ioapic_abi.c:1024-1025.
The module therefore loops msix_alloc until it lands an irq >= 24. This
also documents the correct-behavior baseline: valid legacy requests are
unaffected.

## Impact

DoS/panic from privileged (driver) misuse; no memory corruption on the
demonstrated path (the -1 gsi reaches ioapic_gsi_search which panics by
design instead of indexing OOB). No unprivileged entry point. Severity
Low. Same pattern class as DF-5351: INVARIANTS-only guards on a
driver-facing ABI (also cited: ioapic_abi_legacy_intr_cpuid ioapic_abi.c:1121
silently returning cpu0, ioapic_abi_msi_map ioapic_abi.c:1413 passing
im_msi_base=-1 to msi_map for non-MSI intrs — benign values, no memory
unsafety).

## Fix validation (fix.diff)

Applied to guest /usr/src, incremental X86_64_NOINV rebuild (#1 08:38:15),
reboot, identical PoC (run_fixed.log):

    legacy_crash: msix_alloc rc=0 irq=24
    legacy_crash: calling legacy_intr_config(irq=24, LEVEL, LOW); ...
    legacy_crash: RETURNED WITHOUT PANIC (unexpected)
    LC_RC=0, guest up

No panic; correct legacy configurations (irq 19 path) still take the
normal route; MSI-X allocations/releases unaffected.

## poc_changes

- DECLARE_MODULE moduledata_t form (see DF-5351 VERDICT).
- Loop msix allocations until irq >= 24 (irqs 16..23 are legacy on this
  guest) — the naive "first msix irq" trigger hits a real legacy map and
  returns cleanly.
- Built with the in-tree module recipe (see build.sh).

## Kernel references

- sys/platform/pc64/apic/ioapic_abi.c:987-1053 (ioapic_abi_legacy_intr_config)
- sys/platform/pc64/apic/ioapic_abi.c:999, :1005 (KKASSERT-only guards)
- sys/platform/pc64/apic/ioapic_abi.c:1022, :1035-1036, :1038 (wrong-map writes + gsi_pin(-1))
- sys/platform/pc64/apic/ioapic_abi.c:763-764 (im_gsi/im_msi_base init to -1)
- sys/platform/pc64/apic/ioapic.c:409-427 (ioapic_gsi_pin → ioapic_gsi_search → panic)
- sys/sys/systm.h:94-122 (asserts compiled out without INVARIANTS)
- sys/sys/machintr.h:110 (machintr_legacy_intr_config macro — driver-facing)

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