# DF-4568 — VERDICT

**Status: untested · impact: none · severity Info (hardening) · defect confidence: certain**

## What was verified, and how

Static verdict; the teardown ordering was traced line by line across
`intel_uc.c`, `intel_guc_ct.c`, and `i915_irq.c`.

### The race window is real (defect confirmed)

Shutdown path (driver unload / GT reset contexts):

```
guc_disable_communication()            intel_uc.c:226
  ├─ intel_guc_ct_disable(&guc->ct)    intel_uc.c:231   ← FIRST
  │    ├─ ctch_close(guc, ctch)        intel_guc_ct.c:898
  │    │    ├─ guc_action_deregister_ct_buffer ×2      :284-289
  │    │    └─ ctch_fini → i915_vma_unpin_and_release(&ctch->vma,
  │    │                I915_VMA_RELEASE_MAP)           :214-218  ← page freed/unmapped
  │    └─ guc->send/handler = nop      :901-902        ← AFTER the free
  └─ gen9_disable_guc_interrupts(i915) intel_uc.c:233   ← masks + synchronize_irq
                                                        (i915_irq.c:555-567)
```

Between the free (`intel_guc_ct.c:217`) and the IRQ mask+synchronize
(`intel_uc.c:233` / `i915_irq.c:559-561`), an asserted GuC event dispatches
`guc->handler`, which is still `intel_guc_to_host_event_handler_ct`
(intel_guc_ct.c:846-851; only replaced at :902, after the free). The handler
runs `ct_process_host_channel()` (:815-840) against the released mapping:
`ctb_read()` reads `ctb->desc->head/tail` (:582-583) and writes
`desc->head` (:628); the `-EPROTO` path writes `ctb->desc->is_in_error`
(:838). UAF read+write in interrupt context. The `ctch_is_open()` guard
(:143-146, used at :822) is an unlocked check-then-use on `ctch->vma`, which
`i915_vma_unpin_and_release` clears non-atomically — it cannot close the
window.

### Reachability on this tree: none (verified)

- `intel_guc_ct_disable` is only called under `HAS_GUC_CT(i915)`
  (intel_uc.c:230-231) = `info.has_guc_ct` (i915_drv.h:2665); **no platform
  sets it** (declared intel_device_info.h:94; i915_pci.c sets only
  `.has_guc` at :469/:520; no runtime assignment anywhere in sys/dev/drm).
- Independently, `enable_guc` defaults to 0 (i915_params.h:49), so the GuC
  is not even loaded; the callers of `guc_disable_communication`
  (intel_uc.c:334, :423, :450 — sanitize/fini/suspend) are privileged
  driver-lifecycle paths.

### Guest PoC feasibility: none

Guest video is QEMU bochs std-VGA (`vgapci0@pci0:0:2.0 chip=0x11111234`);
no i915 device exists, and the racing path additionally requires the
interrupt-vs-teardown timing plus a loaded GuC — not constructible on this
guest, and not attacker-timed even on real hardware (driver unload/reset
contexts are privileged).

## Bottom line

Real lifetime bug in the shutdown ordering (fixed upstream in later i915 by
quiescing interrupts and switching the handler before freeing CT buffers),
but unreachable on this tree and only triggerable from privileged
driver-lifecycle timing even where reachable. Filed as Info hardening.
No PoC run: `untested`, `impact: none`, `fix_status: not_testable`.

## fix.diff

Included (authored against read-only sys/, never applied): reorder
`guc_disable_communication()` to disable+synchronize GuC interrupts and
switch send/handler to nop *before* `intel_guc_ct_disable()`, and move the
send/handler switch inside `intel_guc_ct_disable()` ahead of `ctch_close()`
as defense in depth.

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