# DF-5745 VERDICT — REPRODUCED (fatal page fault, production code, on-guest)

**Status: reproduced · impact: panic (kernel NULL-deref DoS) · confidence:
certain**

## Root cause (static, certain)

* `apple-gmux.c:130` — single static `apple_gmux_softc_data`.
* `apple-gmux.c:867` — unconditional `apple_gmux_softc_data = sc;` on every
  attach that reaches the registration stage.
* `apple-gmux.c:880-888` — second instance's
  `vga_switcheroo_register_handler()` returns `-EINVAL`
  (vga_switcheroo.c:242-245: one handler per system).
* `apple-gmux.c:892-894` — `err_register_handler:` path sets
  `apple_gmux_softc_data = NULL` **unconditionally** — destroying the live
  global owned by the first, still-attached instance.
* Dispatches dereference the global with no NULL guard:
  `gmux_switchto` apple-gmux.c:511-513, `gmux_switch_ddc` :524/530/533,
  `gmux_set_power_state` :581.
* Natural trigger: i915 boot path i915_drv.c:738-746 (`handler_flags() &
  CAN_SWITCH_DDC` → `vga_switcheroo_force_migd()` → `handler->switchto()`
  without checking the handler's softc); also root's /dev/vga_switcheroo
  (0600) writes and DRM EDID paths via `vga_switcheroo_lock_ddc`.

## Reproduction (guest: DragonFly 6.5-DEVELOPMENT X86_64_GENERIC, QEMU)

Planted SSDT with two `APP000B` devices. Two independent rigs:

1. **4-port `_CRS` rig** (`ssdt_b.aml`, AAA0@0x8e, AAA2@0x4cc): both attach
   to the registration stage (version reads at base+4 hit readable QEMU
   ports 0x92 and ELCR 0x4d0/0x4d1 → "classic"); AAA0 registers the handler
   fully; AAA2 fails at `-22` after overwriting + NULLing the global.
2. **0x78-length `_CRS` rig** (`ssdt_b2.aml`) — same state machine with
   valid-length regions, proving independence from DF-5744.

Both rigs, unmodified shipped `/boot/kernel/apple_gmux.ko`, trigger KLD
invoking the exact production entry point i915 uses:

```
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x60        <- offsetof(switch_state_ddc)
fault code = supervisor write data, page not present
Stopped at gmux_switchto+0x10: movl %edi,0x60(%rbx)   <- rbx = NULL global
```

(panic.txt / panic.b2.txt; ssh session died + guest in DDB, `vm.sh status` ⇒
down — reproduced/panic per the status table.)

`handler_flags = 0x1 (CAN_SWITCH_DDC=1)` confirms i915_drv.c:742 would take
the same branch on this state, i.e. on an affected machine the panic occurs
spontaneously at DRM load, no root action needed beyond module load order.

## Threat model

Requires two `APP000B` ACPI devices — crafted/malicious DSDT/SSDT (root /
firmware compromise / firmware bug). Not reachable by an unprivileged local
user on properly-firmware'd hardware. Kernel NULL-deref → panic (DoS), not
memory corruption with controlled data (write value is an enum at a fixed
small offset; NULL page not mappable on x86-64 without... the write is to
fixed VA 0x60, kernel-mode, non-present → always a trap). Low severity
consistent with the crafted-firmware precondition; impact ceiling is
reboot-panic of a machine at DRM load.

## Fix validation (module-only rebuild, same 0x78 rig)

* Step 1 (guards + conditional clear only): trigger → `force_migd returned
  -19, NO PANIC` — crash eliminated, but line 867's unconditional set still
  let the second instance claim-then-clear the global (handler dead).
* Step 2 (complete fix.diff, adds ownership: set global only if NULL):
  trigger → `force_migd returned 0, NO PANIC`, guest stays up — the
  surviving instance keeps working.

Baseline reproduced; patched behavior: no panic and functional dispatch ⇒
fix_status: fixed. (Intermediate -19 result retained in fix_validation.log
as evidence the guard alone is insufficient for correctness.)

## Notes

* Same-family lifetime issue as DF-2228 (ACPI notify-handler UAF) exists on
  the attach-error path (`err_enable_gpe:` → `AcpiRemoveNotifyHandler` after
  `AcpiEnableGpe` failure, apple-gmux.c:897-900) — same root cause class
  (no-op `AcpiOsWaitEventsComplete`), not re-reported here.
* `vga_switcheroo.c` has its own adjacent issues (e.g. `force_migd` lacks a
  handler NULL check; negative errno returned from d_write) — different
  file, noted for a future pass, not part of this finding.

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