# DF-4378 — drm_drv.c minor teardown leaks the make_dev'd `/dev/dri/cardN` cdev and `minor->kdev`; stale node keeps `si_drv1` dangling at the freed drm_device

- **File:** `sys/dev/drm/drm_drv.c` (root cause site: `drm_minor_free()` :176-196; companion `drm_sysfs.c:97`)
- **Class:** CWE-401/CWE-772 (missing resource release / dangling pointer after free) — Low
- **Pass:** 2 (GLM 5.3 re-audit); no known findings existed for this file
- **Verdict:** `not_reproduced` — hardware/module-gated: the audit QEMU guest has no DRM GPU; `/dev/dri` absent and no drm kld loaded (see `run.log`). Code defect certain from source (`destroy_dev` appears nowhere in `sys/dev/drm`).

## What a maintainer needs to know

On DragonFly, each `drm_minor` allocation creates real kernel resources:

- `drm_dev_init()` → `drm_minor_alloc()` (drm_drv.c:130) →
  `drm_sysfs_minor_alloc()` (drm_sysfs.c:79) which **`kzalloc`s a `struct device`**
  (:93) and **`make_dev()`s the character device `/dev/dri/cardN`** (:97-98), then
  drops the returned `cdev` on the floor (`devnode` local is never stored).

Teardown never releases either:

- `drm_minor_free()` (drm_drv.c:176-196) does `idr_remove` + `kfree(minor)` —
  `put_device(minor->kdev)` is `#if 0` (:186-188), the kzalloc'd `struct device`
  leaks, and the cdev is never `destroy_dev`'d (`rg destroy_dev sys/dev/drm` → 0 hits).
- `drm_dev_unregister()` → `drm_minor_unregister()` (:237-260) only NULLs the idr
  slot and `dev_set_drvdata(minor->kdev, NULL)`; the devfs node persists.
- After the final `drm_dev_put()` → `drm_dev_fini()` (:666-693) → `drm_minor_free()`,
  the surviving `/dev/dri/cardN` node's `si_drv1` (set to the `drm_device*` at every
  open, drm_file.c:497) **dangles at freed memory**. New opens fail safely
  (`drm_minor_acquire` → `idr_find` → NULL → ENODEV), so the dangling pointer is not
  directly dereferenceable by users; the practical cost is:
  1. a per-attach/detach-cycle leak of one cdev + one `struct device` per minor
     (driver reload, GPU hotplug), and
  2. a stale `/dev/dri/cardN` entry that collides with the `make_dev` of the next
     attachment of the same minor index.

Trigger requires root (kldunload/devctl detach) or physical GPU hotplug — hence Low.

## Build / Run / Expected

```
build.sh   # on the DragonFly guest: cc -o chk_drm chk_drm.c
run.sh     # ./chk_drm
```

Expected on hardware with a DRM driver: card nodes listed; the leak is observed by
`kldunload`/`kldload` cycles (`vmstat -m` M_DRM growth, `/dev/dri` entry surviving
unload).

Observed on this guest (run.log): `DF-4378: /dev/dri absent` → EXIT=2; `kldstat`
shows only kernel/ehci/xhci; `hw.drm` sysctl unknown → drm core not even loaded.

## Fix

`fix.diff` — store the cdev in `struct drm_minor`, `destroy_dev()` it and
`kfree(minor->kdev)` in `drm_minor_free()` (with `<sys/conf.h>` included before the
function).
