# DF-4840 — VERDICT

**Finding:** use-after-free race on non-refcounted mode objects in
`drm_mode_obj_set_property_ioctl()` (sys/dev/drm/drm_mode_object.c:504-533),
with a fully attacker-controlled pointer chase ending in a controlled
indirect call through the freed+reclaimed object.

## Reproduced? YES — with precise scoping of what was and was not demonstrated

All DRM functions exercised are the stock `/boot/kernel/drm.ko` symbols
(`nm`: `drm_mode_obj_set_property_ioctl`, `drm_mode_object_find`,
`drm_mode_create`, `drm_mode_destroy`, `drm_mode_plane_set_obj_prop`).
The kld only fabricates the drm_device/driver the guest lacks (no
/dev/dri on the audit guest; DF-4786 precedent) and drives the race.

### 1. The unprotected window is real and is hit spontaneously (mode 4)

Probe = stock `drm_mode_object_find()` + the ioctl's exact
`obj->properties` read, with one added atomic-counter store between them
(so the window is observable; it is ~10-20x wider than the compiled
ioctl's). Destroyer = stock `drm_mode_destroy()` under
`mode_config.mutex` (as `fill_modes` runs it) + groom
(kzalloc same-size, plant, kfree — stand-in for the userspace heap
spray; on this no-SMAP guest the planted pointers could be userland
addresses).

```
hw.modeuaf.calls: 1138728638  finds: 112481917  destroys: 59734211  hits: 295   (60 s)
hw.modeuaf.calls:  42494140360(=40.5e9 mode1 + 30 s probe)            hits: 110 (30 s probe)
```
`dmesg`: `modeuaf: UAF WINDOW HIT (spontaneous): find() returned freed+groomed
block; obj->properties attacker-controlled` — 110-295 times per run.
That is: the stock find handed out a live mode pointer, the mode was
destroyed (unregister+kfree) before the read, the block was reclaimed with
attacker data, and the read returned the attacker value.

### 2. The stock compiled ioctl window did NOT hit spontaneously (honest negative)

`objdump -d /boot/kernel/drm.ko` shows the window is 3 instructions
(`callq find; test rax,rax; mov 0x8(%rax),%rdx` —
`drm_mode_obj_set_property_ioctl+0x3b..+0x47`). Across ~7.3e10 stock ioctl
calls (runs A-D in run.3.log; incl. B-thread pinned to the IRQ cpu under a
100k-packet host ping flood) — **0 spontaneous stock-window hits** on this
QEMU/KVM guest. The window exists (proven structurally and by the probe);
its 5 ns compiled form simply was never preempted in-window here. On real
hardware with real interrupt loads the rate is of the class that hits —
but this pack does NOT claim a spontaneous stock-ioctl hit.

### 3. Full consequence chain under forced interleave (mode 5)

`lwkt_yield()` placed exactly where a preemption of the ioctl thread
between drm_mode_object.c:515 and :519 would occur; then the verbatim
:519-532 sequence: `obj->properties` → fabricated
`drm_object_properties` (count=1, properties[0] → fabricated
`drm_property` with matching id) → `property->dev` → stock
`drm_mode_plane_set_obj_prop()` (obj->type planted as
DRM_MODE_OBJECT_PLANE) → `plane->funcs->set_property()` where `funcs`
was planted at freed-block+112:

```
modeuaf: CONTROLLED INDIRECT CALL plane=0xfffff8008f8af4a8 property=0xfffff80089996a48 value=0x0
modeuaf: verbatim dispatch ret=0 (canary fired above if 0)
```
14-15 chains per 6-8 s run. The stock dispatch returns *success*
through 100% attacker-fabricated objects. (The destroyer is quiesced
during the chained dispatch — modeling the attacker-owned heap where the
planted reallocation persists; the find→free→groomed-read race itself is
genuine either way.)

### 4. Panic at attacker-chosen data (modes 8 and 7)

- mode 8: `plane->funcs->set_property = 0x4141414141414140` →
  `Fatal trap 9: general protection fault`, **`Stopped at
  drm_mode_plane_set_obj_prop+0x36: ret`** — the controlled indirect
  call faulted inside the stock drm.ko function (panic.txt).
- mode 7: `property->dev = 0x5151515151515100` → GPF at the
  attacker-chosen dereference of `property->dev` (panic.2.txt,
  `probe_window+0x176: movq 0x20(%rdx),%rax`).

### 5. Fix validation — PASSED

`fix.diff` holds `mode_config.mutex` (the lock all runtime mode-destroy
paths run under) across find→dispatch in the real ioctl.
- Patched drm.ko rebuilds clean (drm-build-patched.log).
- objdump: lockmgr acquisition (`lea 0x2c8(%rdi); mov $0x2,%esi; callq`)
  precedes the inlined find in the patched ioctl.
- A/B on the patched load: old sequence (mode 5) still hits — 36 hits /
  14 controlled calls (harness control); **fixed sequence (mode 9):
  0 hits in 4.2e8 forced iterations**; real patched ioctl (mode 1):
  2e9 calls, 60.8e6 destroys, no deadlock, no regression.

## Exploit ceiling (uid0 direction, documented — not completed)

The chain gives: controlled kernel deref chain + controlled indirect
call target. On this no-KASLR/no-SMAP guest a full uid0 chain would
place the fabricated `drm_object_properties`/`drm_property`/`drm_driver`
structures in userland pages and pivot through the stock dispatch into a
write primitive; the realistic blocker is winning the stock 5 ns window
reliably plus obtaining stable kernel addresses for `dev`. This run
characterized the primitive (controlled call + controlled deref → panic)
and did not complete the privilege-escalation endgame; the in-tree
userspace trigger additionally requires a DRM card (mode objects must
exist), which this guest lacks — same "latent on the audit guest, live
on any real GPU system" posture as DF-4786.

## Bottom line

Race-condition UAF with demonstrated: spontaneous window hits (probe),
full attacker-controlled chain + controlled indirect call (forced
interleave), and kernel panics at attacker-chosen addresses through stock
drm.ko code. Severity Medium-High (master-gated ioctl; race-window
dependent; requires a DRM device for the userspace trigger).

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