β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5654

elcr_resume() is dead code (zero callers) and no resync path exists β€” ELCR hardware state is never restored after a reset event nor re-read after probe; divergences are structurally invisible to the kernel

Summary

The only routine that writes the full shadow back has zero callers; the shadow is read from hardware exactly once and the write path early-outs on shadow match, making the kernel shadow-authoritative with no fallback. Live-verified: a raw outb flip was invisible to elcr_read_trigger (stale shadow served while the port read differently) and calling the dead elcr_resume() repaired the hardware byte-exactly - the dead function IS the missing fix. Doubly unreachable today (pc64 suspend stubbed, icu_reinit itself dead) but load-bearing the moment resume gains callers: every level-triggered ISA IRQ (incl. ACPI SCI) would silently run edge after resume. wire-into-icu_reinit fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5654 Β· 11 files
FileTypeDescriptionSize
elcrx.c β€” 9.6 KB view raw
Makefile β€” 78 B ↓ download
build.sh β€” 124 B view raw
run.sh β€” 167 B view raw
build.log β€” 6.7 KB view raw
run.log β€” 2.7 KB view raw
env.txt β€” 358 B view raw
fix.diff β€” 688 B view raw
VERDICT.md β€” 3.3 KB ↓ raw
manifest.json β€” 954 B view raw
verdict.json β€” 4.1 KB view raw
VERDICT.md
↓ download raw

DF-5654 β€” VERDICT

Finding: elcr_resume() (elcr.c:120-126) is dead code β€” zero callers in sys/ β€” and no other path re-reads or rewrites the ELCR after boot. The kernel's ELCR world model is: read hardware once in elcr_probe() (elcr.c:75), keep a shadow, write deltas. Nothing ever repairs the hardware from the shadow.

Verdict: REPRODUCED (live): the divergence is invisible to the kernel, and the dead function is exactly the missing repair. Severity Info (unreachable on this platform today β€” suspend itself is stubbed β€” but a real latent defect for the resume path).

Live proof (run.log phase D)

  1. No resync path: the harness flips irq5's bit in the hardware with one raw outb(0x4d0, b^0x20) (SMM/firmware/VM-migration surrogate). elcr_read_trigger(5) still returns E from the stale shadow while the port reads 0x20 β€” the kernel literally cannot observe the divergence; there is no re-read of the register anywhere (grep: elcr_status is assigned only at elcr.c:75 probe and elcr.c:113 write).

  2. The dead function is the exact fix: calling elcr_resume() directly from the KLD rewrote both bytes from the shadow and the hardware returned to the 0x0c00 baseline byte-exactly (D2 ... repaired=YES). One lwkt-safe call at the right resume hook is all that is missing.

Why this matters (and why only Info here)

  • Resume: on ACPI S3 the PICs are reprogrammed by the wakeup path; the ELCR (in the southbridge) loses nothing on this QEMU rig and β€” more decisively β€” DragonFly/pc64 suspend is currently stubbed (acpi_sleep_machdep no-op, acpi_wakeup.c:50-54, confirmed in the DF-5616 pass), and icu_reinit() (icu.c:127, "ICU reinitialize when ICU configuration has lost") β€” the natural caller β€” is itself dead code. So the missing wiring is doubly unreachable today. The moment either gains a caller, ELCR trigger modes silently stay whatever the firmware reset them to (all-edge), breaking every level-triggered ISA IRQ (SCI included β€” acpi_fadt.c:195 configures it level through this very API).
  • Early-out blind spot (same class): elcr_write_trigger() skips the port write when the shadow bit already matches (elcr.c:111-112), so a "set level" request for an irq whose shadow says level is a no-op even if the hardware bit was lost β€” the shadow-authoritative model has no fallback.
  • Not attacker-reachable: no unprivileged path; the divergence sources (SMM, migration, firmware) are environmental. Info.

Fix

Wire elcr_resume() into the ICU re-init path (icu_reinit(), icu.c:127 β€” its documented purpose is "ICU reinitialize when ICU configuration has lost"; the ELCR must be restored alongside the ICW reprogram icu_init() performs). fix.diff adds the call (plus the elcr_var.h include). When suspend/resume lands, the wakeup path should call icu_reinit() (or elcr_resume() directly) β€” the live D2 result proves the one-liner is sufficient.

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.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

Info/latent-defect finding β€” single-fix kernel rebuild not warranted (the affected path, suspend/resume + icu_reinit, is unreachable in the current tree). fix.diff adds the elcr_found-guarded elcr_resume() call to icu_reinit() ahead of icu_init(); its behavioral delta vs the live D2 result is precisely 'the repair that had to be induced by hand now runs at the re-init hook' β€” verified by inspection against run.log; nothing to regression-test on this rig.

fix.diff
↓ fix.diffper-fix-DF-5654

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log D1 line: 'raw outb flipped hw bit5: hw0x4d0=0x20 but elcr_read_trigger(5)=E -> kernel serves STALE shadow (no resync path exists; elcr_status read once at probe)' ; D2 line: 'after elcr_resume(): hw=0x0c00 baseline=0x0c00 repaired=YES (the dead function is exactly the missing fix)' ; E9: 'final hw=0x0c00 ... coherent=YES ... guest HEALTHY' (state left exactly as found)

PoC changes

Harness authored from scratch (no seed). v1 wedge documented under DF-5652 (wedge_v1.log); the D phase ran in the clean v2 pass. Divergence was induced and repaired entirely within the harness (raw outb flip -> elcr_resume() rewrite), leaving the ELCR byte-exact at its 0x0c00 baseline.

Verified recommended fix

Wire elcr_resume() into icu_reinit() (guard on elcr_found) so ELCR trigger modes are restored whenever the ICU configuration is re-established (elcr.c:120, icu.c:127)

Verdict

Live-verified on the QEMU/i440fx guest: elcr_resume() (elcr.c:120-126) has ZERO callers in sys/ β€” and no other path ever re-reads or rewrites the ELCR after boot (elcr_status is latched once in elcr_probe() at elcr.c:75 and only ever delta-written). The harness induced a hardware-side divergence with one raw outb(0x4d0, b^0x20) (surrogate for SMM/VM-migration/firmware/S3-reset): elcr_read_trigger(5) kept serving the stale shadow value (E) while the port read 0x20 β€” the divergence is structurally invisible to the kernel (no resync path exists). Calling the dead elcr_resume() directly rewrote both ELCR bytes from the shadow and restored the hardware to the 0x0c00 baseline byte-exactly ('D2 ... repaired=YES') β€” the missing wiring, proven. Consequence when a divergence source is real: every level-triggered ISA IRQ (including ACPI SCI, configured level through this API at acpi_fadt.c:195) silently runs edge β€” interrupt storms/lost interrupts. Severity Info because the defect is doubly unreachable today: pc64 suspend is stubbed (acpi_sleep_machdep no-op, acpi_wakeup.c:50-54) and the natural caller icu_reinit() (icu.c:127) is itself dead code; no unprivileged reach exists.