# DF-5653 — VERDICT

**Finding:** `elcr_write_trigger()` performs an unlocked
read-modify-write of the global `elcr_status` shadow and the paired
port byte — `sys/platform/pc64/icu/elcr.c:103-117` — and the caller
`icu_abi_legacy_intr_config()` (icu_abi.c:366-397) serializes nothing
(it does not even take `icu_irqmap_tok`, which the MSI paths in the
same file hold; cf. icu_abi.c:463, 529, 625).

**Verdict: REPRODUCED (live, measured): 1,827 + 2,158 lost
trigger-mode updates out of 300,000 writes per thread in a 2-second
run. Severity Low (root/boot-gated reach; real functional consequence
on hit).**

## Mechanism

```
CPU0 (irq5 -> LEVEL)                CPU1 (irq7 -> LEVEL)
new = elcr_status | 0x20   (stale base S0)
                                    new = elcr_status | 0x80  (same stale S0)
elcr_status = new          (S0|0x20)
                                    elcr_status = new         (S0|0x80 — bit5 LOST)
outb(0x4d0, shadow&0xff)           outb(0x4d0, shadow&0xff)   (bit5 lost in HW too)
```

Both the shadow and the hardware byte converge to the loser's word: the
winner's `LEVEL` request for irq5 is silently reverted to `EDGE`. On
real hardware a level-triggered device (e.g. an ISA/EISA card or ACPI
SCI configured level) whose request is dropped runs edge — interrupt
storms or lost interrupts for that line.

## Live proof (run.log phase R, X86_64_GENERIC, 6 vCPU)

Two kernel threads pinned to cpu0/cpu1 (`kthread_create_cpu`),
300,000 iterations each, each iteration = one real
`elcr_write_trigger()` + a shadow read-back check on the same
interrupt-mode it just wrote:

```
R1: done=2 waited=2s lost_irq5=1827 lost_irq7=2158
```

≈0.6% of all writes were clobbered by the peer's stale-word store —
the race is not theoretical; it hits thousands of times per second of
contention. After the run both irqs were restored to their baseline
EDGE modes and the ELCR verified back at 0x0c00 (`R2 ... ok=YES`,
`E9 ... coherent=YES guest HEALTHY`).

## Reachability (why Low, not higher)

* No in-tree path lets two CPUs call `legacy_intr_config` concurrently
  today: PCI probing is serialized at boot, `acpi_sci_config()` runs on
  cpu0 only (`KKASSERT(mycpuid == 0)`, acpi_fadt.c:222), and the
  post-boot route (kldload of a driver calling `bus_config_intr`) is
  root-only. The unprivileged-user reach is nil.
* The 0x4d0/0x4d1 byte writes do not interleave with the ICU mask
  writes at 0x21/0xa1 (different registers), so there is no mask vs
  trigger register-tearing hazard — the defect is purely the
  shadow-word RMW, plus the same-thread variant (interrupt handler
  calling `elcr_write_trigger` between elcr.c:113 and elcr.c:115-117),
  which converges and is benign.
* Root-gated with a concrete, measured corruption primitive of a
  hardware config register: Low per the rubric ("requires
  already-privileged user"), certain mechanism.

## Fix

Serialize the shadow update + port write with a spinlock (fix.diff) —
DragonFly idiom `static struct spinlock x = SPINLOCK_INITIALIZER(...)`
(cf. subr_prf.c:107). The early-out (`new_status == elcr_status`) is
moved inside the lock so the check can't race either. `elcr_resume()`
may take the same lock for symmetry when it gains callers (DF-5654).

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