# DF-4878 VERDICT — unsynchronized i2caux engine release NULL-wipe → dal_ddc_close(NULL)

## Status: untested in situ (no AMD GPU on guest); race established by line trace

## Root cause

`dal_i2caux_release_engine()` (sys/dev/drm/amd/display/dc/i2caux/i2caux.c:420-429)
performs, with no synchronization whatsoever:

1. `engine->funcs->release_engine(engine)` — for aux engines just a register
   update (aux_engine_dce110.c:73-78), for SW i2c engines a **no-op**
   (i2c_sw_engine.c:559-561);
2. `dal_ddc_close(engine->ddc)` — closes both DDC pins;
3. `engine->ddc = NULL`.

The acquire path (`dal_aux_engine_acquire` aux_engine.c:48-77;
`dal_i2caux_i2c_sw_engine_acquire_engine` i2c_sw_engine.c:462-476) opens
the pins and stores `engine->ddc = ddc` (aux_engine.c:75,
i2c_sw_engine.c:473). The pin double-open guard lives in
`dal_gpio_open_ex` (`gpio->pin != NULL` → GPIO_RESULT_ALREADY_OPENED,
gpio_base.c:59-70) — which *re-opens* the instant T1's `dal_ddc_close`
completes, i.e. between steps 2 and 3 of a concurrent release. T1's step 3
then NULLs the ddc pointer T2 just installed:

* SW i2c path: T2's `dal_i2c_sw_engine_submit_channel_request()` uses
  `struct ddc *ddc = engine->base.ddc` (i2c_sw_engine.c:491) → NULL deref
  in `start_sync`.
* AUX path: T2 completes the transaction and its own release calls
  `dal_ddc_close(NULL)` (i2caux.c:426) → `ddc->pin_clock` on NULL
  (gpio_service.c:587-590 — `dal_ddc_close` has no NULL guard, unlike
  `dal_gpio_close` at gpio_base.c:220-227) → fatal trap.

The port itself flags the missing lock: i2caux.h:91
`/* [anaumov] in DAL2, there is a Mutex */` — DAL2 serialized
acquire→submit→release; this tree does not. Related known family:
DF-4787/DF-4788 (gpio_service open path), DF-4808 (hw-engine accounting) —
all different files; this file's defect is the release-side wipe.

Converse interleaving (T2 acquires before T1's close): T1 closes T2's pins
mid-transaction — wrong-owner close corrupting the gpio state machine.

## Why untested

The guest's only display device is QEMU std VGA (env.txt: vgapci0
8086:1111); `amdgpu.ko` never attaches, the DC `i2caux` object is never
constructed, and its datapath (dc_link_ddc.c:598 → i2caux.c:109/196)
cannot be entered. Hardware blocker, not a refutation — every store/load
in the interleaving was verified at the cited lines.

## Impact

Narrow-window kernel NULL-deref panic (or gpio state corruption) inside
the display DDC/AUX path, triggered by concurrent hotplug-detect vs
mode-set transactions on one connector. DoS-class; not a corruption
primitive (the wiped value is NULL, not attacker data).

## Fix validation

fix_status: not_testable (hardware absent). fix.diff guards the close —
`if (engine->ddc) dal_ddc_close(engine->ddc);` — removing the panic sink
(the wrong-owner close and full serialization need the DAL2 mutex;
recommended in the finding text).

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