# DF-3656 — VERDICT

**Status: inconclusive (runtime hardware+OOM-gated on the virtio-only guest);
ownership defect is source-certain.**

## Root cause (ownership trace)

### Site 1 — `amdgpu_dm_connector_init()` (amdgpu_dm.c:4058-4137)

```c
4073   i2c = create_i2c(link->ddc, link->link_index, &res);
...
4080   res = i2c_add_adapter(&i2c->base);       /* ADAPTER REGISTERED */
4082   if (res) { ...; goto out_free; }
4089   res = drm_connector_init(dm->ddev, &aconnector->base, ...);
                                                  /* CONNECTOR REGISTERED */
4095   if (res) { ...; goto out_free; }
...
4119 #if defined(CONFIG_DEBUG_FS)
4120   res = connector_debugfs_init(aconnector);
4121   if (res) { ...; goto out_free; }          /* connector ALREADY registered */
...
4131 out_free:
4132   if (res) {
4133           kfree(i2c);                        /* freed while registered  */
4134           aconnector->i2c = NULL;
4135   }
4136   return res;
```

* On the debugfs-failure path the **registered** i2c adapter is kfree'd
  without `i2c_del_adapter()` — it remains in the i2c core's adapter list
  with `adapdata` pointing at freed memory; the next DDC transaction on that
  bus (EDID probe, CEC) is a UAF.
* The caller (`amdgpu_dm_initialize_drm_device`, 1875-1878) then `goto fail`
  and additionally kfrees the **registered** `aconnector` (see site 2), which
  is still linked in `dev->mode_config.connector_list`, still `link->priv`
  (4069), and still owns `dm_dp_aux`/`mst_mgr`.

### Site 2 — `amdgpu_dm_initialize_drm_device()` fail path (1937-1943)

```c
1937 fail:
1938   kfree(aencoder);
1939   kfree(aconnector);
1940   for (i = 0; i < dm->dc->caps.max_planes; i++)
1941           kfree(mode_info->planes[i]);
1942   return -EINVAL;
```

Reached after full registration when e.g. `dce110_register_irq_handlers()`
fails (1915-1918/1922-1925 — `amdgpu_irq_add_id()` failure) or any
connector-loop init step fails for the last pair:

* `aencoder` was registered by `amdgpu_dm_encoder_init()` →
  `drm_encoder_init()` (4164) → linked in `mode_config.encoder_list`;
  kfree'd without `drm_encoder_cleanup()` → dangling.
* `aconnector` registered via `drm_connector_init` (plus `link->priv`,
  `link->ddc` i2c, CEC) → dangling.
* `mode_info->planes[i]` registered via `drm_universal_plane_init()`
  (3647/3657/3667) → linked in `mode_config.plane_list`; kfree'd without
  `drm_plane_cleanup()` → dangling.

The error unwinds via `amdgpu_dm_init()` error → `amdgpu_dm_fini()` (487) →
`amdgpu_dm_destroy_drm_device()` → `drm_mode_config_cleanup(dm->ddev)`
(1947), which walks `connector_list`/`encoder_list`/`plane_list` and calls
the objects' `->destroy` hooks **on freed memory** → use-after-free
(deterministic once the error path is taken). Additionally, any HPD IRQ
delivered before the lists are pruned uses the freed `aconnector` cookie
(`handle_hpd_irq(void *param)` → `aconnector->hpd_lock` at 1083).

Distinct from DF-1153 (reset-function kzalloc-failure UAF): different
functions, different lifetime owners (DRM core lists vs crtc/plane state).

## Why Low

Triggering requires an allocation or debugfs failure during DM init — an
OOM/fault-injection condition, not attacker-timing-controllable. Impact once
triggered: UAF in `drm_mode_config_cleanup` / i2c core — memory corruption
class, but the practical exposure is boot/module-load flakiness.

## Verification (Phase V)

Guest is virtio-only (env.txt) — amdgpu never attaches; additionally the
trigger needs an induced allocation failure at exactly this window. No
meaningful userspace model exists for a kernel-object ownership bug; verdict
recorded as inconclusive with the source trace as evidence. Fix:
`fix.diff` (de-register before free / let mode_config own registered
objects); `git apply --check` clean. `fix_status: not_testable`.

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