# DF-5616 — VERDICT

**Finding**: `icu_ioapic_extint()` (sys/platform/pc64/icu/icu.c:158-183) —
signed lower-bound validation gap (`if (irq >= 8 || irq == 2)` at icu.c:167
does not reject `irq < 0`), leading to `mask = ~(1 << irq)` at icu.c:170 —
a C-undefined shift — feeding `outb(IO_ICU1 + 1, mask)` at icu.c:180.
Secondary hygiene: the IMR/ICW writes bypass `imen_spinlock` and never sync
the `icu_imen` shadow.

## Reproduced? YES (defect manifests live; impact demonstrably none)

On the audit QEMU guest (DragonFly 6.5-DEVELOPMENT #0 Thu Jul 2 06:02:54 2026,
X86_64_GENERIC, 6 vCPUs, i440fx), a KLD harness (icuintro.c, root-loaded)
called the real kernel routine with `irq = -1, vec = 0x20`:

* return value `0` — the function ACCEPTS the negative irq; per its own
  comment ("Only first 8 interrupt is supported", icu.c:163-166) it must
  reject out-of-range input with EOPNOTSUPP.
* IMR1 after the call = `0xff` — the UB shift `1 << -1` materialized as
  `1 << 31` on this compiler/target, so `mask = (uint8_t)~0x80000000 = 0xff`
  (all-masked). P3 of the harness computed the exact icu.c:170 expression
  live: `0xff`. **No wild mask, no OOB, no corruption.**
* `icu_imen` shadow untouched by the routine (shadow_untouched=YES) —
  confirming the missing shadow sync (cosmetic here; the ICU ABI is retired
  on IOAPIC systems).
* Full state restore (manual ICW sequence mirroring icu_init(), icu.c:62-102)
  returned IMR to {0xfb,0xff} exactly; ELCR stable 0x0c00 throughout;
  guest alive, interrupts flowing, module unloaded cleanly.

Severity is **Info**: the only in-tree caller pins `irq == 0` with an
always-on KKASSERT (ioapic_abi.c:1064 `KKASSERT(irq == 0)`), there is no
userspace/ioctl/sysctl reachability — kernel-code-only (root/KLD) — and the
sink is provably benign on x86 (mask truncates to 0xff = "mask everything",
the same value icu_init writes at icu.c:83 anyway). No uid0, no panic, no
leak, no DoS: impact = none.

## Why this is still worth fixing

It is the same *validation-hygiene class* the orchestrator flagged for this
PIC family this pass (icu_abi.c DF-5464/DF-5465, ioapic.c DF-5503), but it is
the only remaining instance where the guard itself exists yet is incomplete
(a signed lower bound), rather than merely KASSERT-gated. Two tokens fix it.

## Pass-2 checklist results (the rest of the file: clean)

* **(a) IMEN mask arithmetic / SMP RMW** — CORRECT. `ICU_INTRDIS`/`ICU_INTREN`
  (icu_ipl.s:108-144) do the shadow RMW + port write under `imen_spinlock`,
  and the locore `SPIN_LOCK` macro (lock.h:61-78) does `pushfq/cli/cmpxchg`
  (unlock `popfq`) — so the lock is local-IRQ-safe as well. `MASK_IRQ`/
  `UNMASK_IRQ` (icu_vector.s:100-116) both hold `ICU_IMASK_LOCK`; the EOI is
  issued *inside* the lock (icu_vector.s:137-140), preserving the
  mask+EOI atomicity needed for level-triggered shared IRQs. Byte-split
  writes always rewrite the full byte from the shadow under the lock — no
  lost-update window. LIVE: `icu_imen` shadow == hardware IMR (`fffb`)
  before and after mask cycles (run.log D1/P2/P6).
* **(b) ELCR trigger persistence** — icu.c never touches ports 0x4d0/0x4d1.
  LIVE: ELCR (0x0c00 — irqs 10,11 level, PCI-style) unchanged across both
  INTRDIS/INTREN mask cycles (P2) and a full ICW1..ICW4+IMR reprogram of
  ICU2 (P5). `elcr_write_trigger`'s unlocked shadow RMW (elcr.c:100-118) has
  only boot-time callers — no runtime race (and elcr.c is not this file).
* **(c) reprogram state machine / pending vectors** — there is no
  `icu_reprogram` in DragonFly; the closest machinery is `gd_ipending` +
  doreti replay (icu_vector.s:141-166) — outside this file. The
  icu.c-adjacent lost-update window (`MachIntrABI.cleanup()` bzeroing
  `gd_ipending` after IF is restored — icu.c:202-206, init_main.c:306-307)
  is unreachable: at every point cleanup runs, all device IRQs are masked
  in hardware (boot-order proof: IF first enabled at SI_BOOT2_LEAVE_CRIT
  0x1900000 with IMR=0xfffb from icu_abi_stabilize; the only post-IF call
  site, ioapic.c:623, runs at 0x1a70000 with zero registered ICU sources —
  bus enumeration is later — and slave IMR 0xff so not even the cascade can
  assert). No production lost-update.
* **(d) DF-5464 KASSERT-class twin check** — icu.c contains NO INVARIANTS-only
  KASSERT at all; its three assertions are always-on KKASSERTs of internal
  boot invariants (icu.c:109, 190, 191), and each provably holds at every
  call site: `icu_definit` runs at machdep.c:2775 after `MachIntrABI =
  MachIntrABI_ICU` (machdep.c:2682); for `icu_reinit_noioapic`,
  ioapic_config() can only return an error BEFORE `MachIntrABI =
  MachIntrABI_IOAPIC` (ioapic.c:103, :113 vs :125; post-switch failures are
  inline panics and the tail returns 0 unconditionally at ioapic.c:211), and
  lapic.c:1287 runs at 0x1a50000 before any switch; `ioapic_enable` is
  forced to 0 at both call sites before the call (ioapic.c:622,
  lapic.c:1286). No production-bypassable assertion in this file.
* **DF-1102 subsumption** — icu.c never calls msi_setup/msi_map; the ICU-side
  consumer of those unvalidated sinks is icu_abi.c:499 (covered by DF-5466's
  negative `hw.icu.msi_start` chain). Nothing to add.
* **Dead code** — `icu_reinit()` (icu.c:127-141) and `elcr_resume()`
  (elcr.c:120-126) have zero callers in the tree; the ACPI resume path they
  were written for does not exist (`acpi_sleep_machdep` is a stub returning
  0, acpi_wakeup.c:50-54; `acpi_install_wakeup_handler` empty at :56-59).
  The `#ifdef foo` branch (never defined) shows the re-enable-of-registered-
  IRQs logic was abandoned. Consequence: `icu_init()` provably never runs
  after boot — no runtime SMP race is reachable on any current path.

## Fix

```diff
--- a/sys/platform/pc64/icu/icu.c
+++ b/sys/platform/pc64/icu/icu.c
@@ -163,7 +163,7 @@ icu_ioapic_extint(int irq, int vec)
 	/*
 	 * Only first 8 interrupt is supported.
 	 * Don't allow setup for the slave link.
 	 */
-	if (irq >= 8 || irq == 2)
+	if (irq < 0 || irq >= 8 || irq == 2)
 		return EOPNOTSUPP;
```

Fix validation status: `not_applicable` (Info finding; single-fix kernel
rebuild not warranted — the change is a two-token guard whose behavioral
effect is exactly "return EOPNOTSUPP for irq<0", verified by inspection; no
memory-corruption baseline exists to regression-test).

## Harness incident (documented for honesty)

Harness v1 used `lwkt_setcpu_self(globaldata_find(0))` to pin to CPU0 and
panicked the guest afterwards in `dfly_acquire_curproc`
(usched_dfly.c:453 `lp->lwp_qcpu == dd->cpuid`) — raw LWKT thread migration
from a process thread is out-of-contract for usched_dfly. This is a harness
bug, not an audit finding (requires root KLD; no unprivileged trigger).
See panic.txt. v2 removed the pinning; run.log (v2) is the decisive run on
a fresh `vm.sh reset with-src` guest.

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