# DF-4314 VERDICT — not_reproduced on the audit guest (device absent); race proven structurally in source

## Reproduced?

**No — on this guest it is impossible by construction.** The audit QEMU guest
has no Intel GPU: `pciconf -l` shows only `vgapci0 chip=0x11111234` (Bochs/VBE
virtio VGA), `/dev/dri` and `/dev/drm` do not exist (`run.log`, `env.txt`), and
no drm/i915 module is loaded. `HAS_OVERLAY()` is never true, `intel_setup_overlay()`
never runs, and the ioctl entry points return `-ENODEV` — in fact the drm2
device never attaches at all. The trigger therefore fails at `open()` with
ENOENT (exit 2). Per the audit status vocabulary this is a device/feature-absent
environment; recorded as `not_reproduced / reproduced=0 / impact=none` for what
was *observable on this guest*, with the code-level analysis below.

## Why the bug is real (source-level proof)

### The two colliding paths

**Path A — overlay off (DRM master, e.g. X stopping Xv video):**

1. `intel_overlay_put_image_ioctl` with `!I915_OVERLAY_ENABLE`
   (`intel_overlay.c:1075-1085`), or a CRTC dpms disable
   (`intel_display.c:5221-5229`), calls `intel_overlay_switch_off`
   (`intel_overlay.c:856`).
2. `intel_overlay_off` (`:382-421`) calls
   `intel_overlay_flip_prepare(overlay, NULL)` (`:417`) — moves the live vma
   into `overlay->old_vma` — then submits the `MI_OVERLAY_OFF` request with
   retire callback `intel_overlay_off_tail` and **sleeps** in
   `intel_overlay_do_wait_request` → `i915_gem_active_retire`
   (`:419-420`, `i915_request.h:701-722`).

**Path B — GPU reset:**

3. The wait uses `I915_WAIT_LOCKED`; a waiter that observes reset handoff
   **executes `i915_reset()` itself from inside its wait loop**
   (`i915_request.c:1320-1322` → `__i915_wait_request_check_and_reset`
   `:1250-1260`). Independently, any other WAIT_LOCKED waiter or the error
   handler can perform the reset.
4. `i915_reset` (`i915_drv.c:2291`, `lockdep_assert_held(struct_mutex)` holds
   the mutex Path A dropped around its sleep) calls `i915_gem_reset()` —
   completing Path A's still-in-flight off request — and then
   **`intel_overlay_reset(i915)`** (`i915_drv.c:2356`).
5. `intel_overlay_reset` (`intel_overlay.c:477-490`) runs
   `intel_overlay_release_old_vid(overlay)`:
   - if the `DOVSTA` flip-pending bit is clear (`:449`), it frees `old_vma`
     via the **direct** tail call (`:471-472`) — `last_flip` is *not*
     detached, the pending `off_tail` retire remains registered;
   - if the bit is set, it takes the slowpath, which calls
     `intel_overlay_submit_request` (`:216-226`) whose
     `GEM_BUG_ON(i915_gem_active_peek(&overlay->last_flip, …))` fires because
     `last_flip` still tracks the completed-but-unretired OFF request →
     **`BUG()` → panic** (`GEM_BUG_ON` is unconditionally fatal:
     `i915_gem.h:42-49`).
   Then it sets `overlay->crtc = NULL; overlay->active = false` (`:488-489`).

### The crash

6. Path A's `i915_gem_active_retire` resumes: the request is now complete, so
   it detaches `last_flip` and invokes the retire callback —
   `intel_overlay_off_tail` (`i915_request.h:719`: `active->retire(active,
   request)`; `intel_overlay.c:364-379`).
7. `off_tail` runs `intel_overlay_release_old_vma` (WARN_ON(!vma) — old_vma
   was already freed in step 5, WARN only, `:340-353`) and then executes

   ```c
   overlay->crtc->overlay = NULL;   /* intel_overlay.c:373 */
   ```

   with `overlay->crtc == NULL` (cleared in step 5) → kernel write to
   `0 + offsetof(struct intel_crtc, overlay)` (an absolute low address,
   unmapped) → page fault in kernel mode → **panic**.

There is no NULL guard anywhere on this path, and the alternative retire-worker
ordering (Path A aborted earlier by a signal, the OFF request retired by the
periodic `i915_retire_requests` worker after the reset) reaches the identical
`off_tail` NULL dereference. Both interleavings are architecturally enabled by
design: WAIT_LOCKED waits exist precisely so the waiter runs the reset inline.

### Reachability / attacker model

- The overlay-off half is DRM-master territory (X stopping Xv, or a CRTC
  dpms disable — kernel-internal, `intel_display.c:5228`).
- The **reset half is triggerable by an unprivileged local user**: GPU hangs
  from unprivileged GL/GEM clients are the canonical i915 reset trigger, and
  `i915_handle_error`/hangcheck then schedules exactly the
  `i915_reset → intel_overlay_reset` path raced above. When the GPU hangs,
  the master's in-flight overlay-off wait blocks *until* the reset — i.e. the
  "overlay-off pending during reset" condition is the *common* case whenever
  video playback/modeset coincides with a hang, not a narrow window.
- Requires Gen2–Gen5 Intel graphics with the overlay unit (HAS_OVERLAY);
  Ironlake+ (no overlay) is unaffected.

Impact: local kernel panic (DoS) — no controlled data is written (fixed NULL
base + fixed small offset), no info leak. Race-condition reliability: high on
hang coincidence, but not 100% deterministic → severity Medium,
CWE-362/CWE-476.

## Fix validation

`fix.diff` (two hunks) — `git apply --check` clean against this tree:

1. `intel_overlay_off_tail`: NULL-guard `overlay->crtc` (kills crash shape 6-7).
2. `intel_overlay_reset`: retire the pending `last_flip` *first*, while
   `overlay->crtc` is still valid (kills both the deferred-NULL-deref and the
   `GEM_BUG_ON` shape — `release_old_vid` afterwards finds `old_vma` already
   released by the properly-ordered retire).

`fix_status: not_testable` — validating on this guest is impossible: the
driver cannot attach (no Intel GPU), so neither baseline nor patched kernel
can execute the path. The fix was instead validated by re-deriving every
interleaving (reset-inside-wait, reset-inside-error-handler, deferred retire
worker) against the patched source: each ordering now retires `off_tail` with
`crtc` valid before any NULL assignment.

## Artifacts

`trigger.c` (code_hash a6499d4a5144fd6e4c159a43f8c8db24cdc3a4afac57f53c36da096aed46c7b4),
`build.log` (clean guest compile), `run.log` (device-absent result),
`env.txt` (guest PCI/DRM inventory), `fix.diff`, `manifest.json`, `verdict.json`.

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