# DF-4532 VERDICT

**Classification:** REAL BUG, `inconclusive` at runtime (hardware-gated),
source-certain.

## Root cause chain

1. `sys/dev/drm/drm_file.c:351` — `device_busy(dev->dev->bsddev);` on every
   successful `drm_open()`. Unconditional; per-open.
2. The release side of the pair never runs on this port:
   - `drm_close()` (`drm_file.c:366-448`) contains no `device_unbusy()`.
   - `drm_release()` (`drm_file.c:595-604`) returns 0 immediately on
     `__DragonFly__` — dead.
   - `drm_cdevpriv_dtor()` (`drm_drv.c:1194-1236`) — the FreeBSD drm2 code this
     was ported from released the busy count in exactly this destructor — is
     never registered: zero `devfs_set_cdevpriv()` callers exist in
     `sys/dev/drm` (mechanically verified by `static_proof.sh`).
3. `sys/kern/subr_bus.c:1844-1853` — `device_busy()`: `dev->busy++`,
   `dev->state = DS_BUSY` (recursively busies `dev->parent`).
4. `sys/kern/subr_bus.c:2129-2134` — `device_detach()`: `if (dev->state ==
   DS_BUSY) return EBUSY;`

Net: one open ⇒ device + PCI parent stuck DS_BUSY with a never-decreasing
busy count ⇒ `kldunload`/detach of the drm driver fails forever, even after
all fds are closed. An unprivileged video-group user can wedge this state
with a single `open("/dev/dri/card0")`; only root notices (unload fails).

## Severity rationale: Low

The memory effect is 4 bytes of counter per open; the operational effect
(blocks driver detach/hot-replace, e.g. recovering a wedged GPU by module
reload) requires root to exercise. No memory-safety consequence found (the
counter is not used for any bounds decision; `device_unbusy()` being absent
means the state merely pins DS_BUSY).

## What was run

| Step | Result |
|---|---|
| `static_proof.sh` on the repo | **PASS** — busy/unbusy sites enumerated; dtor provably unregistered; no unbusy in drm_file.c |
| `trigger.sh` on guest | hardware-gated (`/dev/dri/card0` absent — QEMU std VGA, no drm driver) |
| `fix.diff` applies to pristine `sys/dev/drm/drm_file.c` | verified (`patch --dry-run` + apply on scratch copy) |
| patched `drm_file.c` compiled with exact kernel CFLAGS (X86_64_GENERIC) | **RC=0** (`fix_build.log`) |
| guest `/usr/src/sys/dev/drm/drm_file.c` restored | md5 matches repo |

## Fix

`fix.diff`: balance the `device_busy()` with `device_unbusy(dev->dev->bsddev)`
at the tail of `drm_close()`, after `drm_minor_release()`. Placement after
the last use of `dev` is deliberate; it runs exactly once per close
(= once per successful open). Note: DF-4533's fix.diff anchors at the same
line — merge by hand if both are applied together.
`fix_status: not_testable` (no DRM device on the guest; compile-validated only).

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