synchronize_irq() is an empty lkpi stub: interrupt-drain barriers in i915_irq.c are void (MMIO after power-well off, work resurrected after cancel_work_sync)
Summary
DragonFly's LinuxKPI synchronize_irq() does nothing (hardirq.h:33-36 - confirmed verbatim on the audit guest's own /usr/src). i915_irq.c relies on it as a hard irq-drain barrier in four places, all copied verbatim from upstream where the barrier is real: (a) gen8_irq_power_well_pre_disable - after return the caller disables the pipe power domain while an in-flight gen8_de_irq_handler may still MMIO the now-unclocked display block -> machine check/hard hang on real hardware; (b) gen6_disable_rps_interrupts - an in-flight handler can run schedule_work(&rps->work) AFTER cancel_work_sync returned, voiding its 'callers may free associated data' contract (benign while loaded since the work re-validates under irq_lock, but widens DF-3699); (c) gen9_disable_guc_interrupts same pattern; (d) intel_runtime_pm_disable_interrupts - runtime suspend can gate device power under a preempted in-flight handler -> D3 MMIO -> hang/panic. SYSTEMIC lkpi gap affecting all lkpi DRM drivers; filed here at its most acute consumer. Fix: implement a real per-irq in-flight refcount + waitqueue synchronize_irq in the lkpi trampoline.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3700 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.4 KB | β raw | |
| VERDICT.md | β | 4.5 KB | β raw | |
| trigger.sh | β | 983 B | view raw | |
| build.sh | β | 90 B | view raw | |
| run.sh | β | 94 B | view raw | |
| build.log | β | 458 B | view raw | |
| run.log | β | 458 B | view raw | |
| env.txt | β | 373 B | view raw | |
| manifest.json | β | 803 B | view raw | |
| verdict.json | β | 3.0 KB | view raw |
DF-3700 β synchronize_irq() lkpi stub voids IRQ-drain barriers in i915_irq.c
WHAT sys/dev/drm/include/linux/hardirq.h:33-36
static inline void synchronize_irq(unsigned int irq) { }
DragonFly's LinuxKPI synchronize_irq() is an empty stub. i915_irq.c relies on it as a hard interrupt-drain barrier in four places; each barrier is void on DragonFly.
AFFECTED BARRIERS (sys/dev/drm/i915/i915_irq.c) 1. gen8_irq_power_well_pre_disable() i915_irq.c:3710 "make sure we're done processing display irqs" β after this returns the caller (intel_display.c power-well code) turns the pipe power domain OFF. An in-flight gen8_de_irq_handler (entered before the GEN8_IRQ_RESET_NDX masking posted) can then perform MMIO on the unclocked display block -> machine check / hard hang on real hw. 2. gen6_disable_rps_interrupts() i915_irq.c:522 disable -> synchronize_irq() -> cancel_work_sync(&rps->work) (i915_irq.c:529). An in-flight gen6_rps_irq_handler that already passed the interrupts_enabled check can still run schedule_work() AFTER the cancel -> work resurrected post-cancel. Benign while the driver is loaded (work re-checks under irq_lock), but it breaks the documented contract of cancel_work_sync and widens DF-3699's window. (At driver unload the ordering in intel_modeset_cleanup β intel_irq_uninstall (free_irq, drains) then flush_scheduled_work β happens to cover the lifetime.) 3. gen9_disable_guc_interrupts() i915_irq.c:569 Same pattern for GuC events. 4. intel_runtime_pm_disable_interrupts() i915_irq.c:4982 Calls only the driver's irq_uninstall hook (register reset, no free_irq), then a no-op sync. Runtime suspend can power the device down while a preempted in-flight handler still touches MMIO -> D3-device MMIO -> hang/panic. (Handler-entry !intel_irqs_enabled() early-return protects only handlers that have not started yet.)
GUEST STATUS No i915 hardware (vgapci = Bochs 0x1234:1111), no i915 module (/boot/kernel has drm.ko only). Runtime verdict: inconclusive. Source-level certainty: the stub is empty β verifiable by inspection.
BUILD ./build.sh (no-op; source-analysis finding β trigger.sh checks the stub and device presence)
RUN ./trigger.sh
DF-3700 VERDICT β synchronize_irq() stub breaks i915_irq.c drain barriers
STATUS: inconclusive (runtime) β the empty stub is a source-certain fact; hardware impact requires a real i915, which the audit guest lacks. reproduced=0.
1. THE FACT (certain)
sys/dev/drm/include/linux/hardirq.h:33-36:
static inline void
synchronize_irq(unsigned int irq)
{
}
Upstream Linux synchronize_irq() waits for all in-flight (and threaded) executions of the given irq's handler to complete. DragonFly's LinuxKPI makes it a no-op. Every call site in i915_irq.c that uses it as a drain barrier is therefore unprotected against a concurrently executing irq handler on another CPU (or a preempted handler thread).
2. CALL-SITE EXPOSURE ANALYSIS (sys/dev/drm/i915/i915_irq.c)
-
i915_irq.c:3710 gen8_irq_power_well_pre_disable(): GEN8_IRQ_RESET_NDX(DE_PIPE, pipe) under irq_lock, then synchronize_irq(drm.irq), return -> caller kills the pipe power domain. In-flight gen8_de_irq_handler() (i915_irq.c:2721-2888) does raw I915_READ/WRITE on DE_PIPE/DE_PORT/SDE registers. MMIO to an unclocked display block on Intel hw reliably hangs the machine (this is why upstream added the barrier β the comment is verbatim in the code). IMPACT: hard hang / machine check = hw DoS.
-
i915_irq.c:522 gen6_disable_rps_interrupts(): interrupts_enabled=false (under irq_lock) -> synchronize_irq() [NO-OP] -> cancel_work_sync(&rps->work) (i915_irq.c:529). A preempted gen6_rps_irq_handler() (i915_irq.c:1829) that read rps->interrupts_enabled == true before the disable can resume after the cancel and run schedule_work(&rps->work) (i915_irq.c:1838): work pending after cancel_work_sync returned "safe to free". While the driver is loaded the late work is functionally benign (gen6_pm_rps_work re-validates under irq_lock, i915_irq.c:1287-1292) β but it voids cancel_work_sync's contract and is the same window class as DF-3699. At unload, intel_modeset_cleanup's intel_irq_uninstall (free_irq = real drain) -> flush_scheduled_work ordering (intel_display.c:16197,16203) covers the object lifetime.
-
i915_irq.c:569 gen9_disable_guc_interrupts(): identical pattern to the above for pm_guc_events.
-
i915_irq.c:4982 intel_runtime_pm_disable_interrupts(): calls dev_priv->drm.driver->irq_uninstall(&dev_priv->drm) β the DRIVER hook (e.g. gen8_irq_reset), NOT drm_irq_uninstall β so no free_irq drain happens here; then a no-op synchronize_irq. The subsequent runtime suspend can gate device power while a preempted handler is mid-flight -> MMIO on a D3 device -> hang/panic.
3. SEVERITY REASONING
No memory corruption is demonstrated; the concrete hazard is MMIO to powered-off hardware (hard hang = local hw DoS, timing-dependent) and broken work-cancel contracts that widen DF-3699. Requires real Intel graphics (SNB+). Severity Medium, confidence: certain that the stub is empty and that all four barriers are void; likely that at least the power-well case produces hangs on affected hardware over time.
4. WHY NOT REPRODUCED
Guest has no Intel display device (vgapci0 = 0x1234:1111 Bochs) and no i915 module in /boot/kernel. trigger.sh documents the checks performed.
5. FIX
Implement a real drain in the lkpi: e.g. an in-flight per-irq refcount +waitqueue incremented around the handler trampoline that lkpi uses when registering irqs, on which synchronize_irq() waits. Minimal safe alternative for the i915 power-well path: keep the pipe power well's irq handlers from running by masking + posting GEN8_DE_PIPE_IMR before reset (already done) AND parking the irq via bus descriptor teardown for the power-well disable window.
RECOMMENDED_FIX: "Implement synchronize_irq() in the DragonFly LinuxKPI (in-flight handler refcount + wait) instead of the empty stub in sys/dev/drm/include/linux/hardirq.h:33."
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testableno fix.diff: proper fix belongs in the lkpi irq trampoline (new infrastructure), not a one-file change; runtime validation impossible without i915 hardware
findings/poc/DF-3700/VERDICT.md#5-fix
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (guest /usr/src shows the empty stub verbatim); VERDICT.md section 2 (per-call-site exposure trace); env.txt (no i915 hw/module)
PoC changes
n/a - source-analysis finding; trigger.sh reproduces the stub inspection on any DFBSD box with /usr/src
Verified recommended fix
Implement synchronize_irq() in the DragonFly LinuxKPI (per-irq in-flight handler refcount + waitqueue in the lkpi irq trampoline) instead of the empty stub in sys/dev/drm/include/linux/hardirq.h:33.
Verdict
The DragonFly LinuxKPI synchronize_irq() is an empty stub (sys/dev/drm/include/linux/hardirq.h:33-36 - verified verbatim on the guest's own /usr/src in run.log), voiding the interrupt-drain barriers i915_irq.c relies on: gen8_irq_power_well_pre_disable (i915_irq.c:3710) can return while gen8_de_irq_handler is still executing, after which the caller powers off the display block and the in-flight handler's MMIO hits an unclocked domain (hard hang on real hardware); gen6_disable_rps_interrupts (i915_irq.c:522 -> cancel_work_sync at :529) and gen9_disable_guc_interrupts (:569) can have schedule_work run after cancel (contract break; benign while loaded, widens DF-3699); intel_runtime_pm_disable_interrupts (:4982) only calls the driver irq_uninstall hook (register reset, no free_irq) so runtime suspend can gate power under a preempted in-flight handler. Not runtime-testable: guest has Bochs VGA and no i915 module. Impact ceiling on real hardware: MMIO-after-poweroff hard hang (hw DoS) and broken cancel_sync contracts; no memory corruption demonstrated.
No comments yet.