# VERDICT DF-4579 — failed radeon attach leaks drm_device (DF drm_pci.c error unwind stubbed out)

**Status: not_reproduced on this guest (no ATI hardware — radeon cannot attach at all).**
Severity Low, confidence certain (source-verified DF divergence from upstream).

## Reproduced?

No — and provably not for lack of trying: the trigger requires an ATI Radeon PCI
device (attach-time load failure). The guest's only VGA is virtio-gpu
(`vgapci0@pci0:0:2:0 card=0x11001af4 chip=0x11111234`, env.txt), `/dev/dri` is
absent, and no radeon KLD loads. QEMU cannot present radeon-compatible hardware.
Following the DF-4564 precedent (radeon HW-absent verdicts), this is recorded as
`not_reproduced` with the source trace as the verification deliverable.

## The defect is certain from source

- `drm_get_pci_dev` (drm_pci.c:309-357):
  - `:318` `dev = drm_dev_alloc(driver, &pdev->dev);`
  - `:338-340` `ret = drm_dev_register(dev, ent->driver_data); if (ret) goto err_agp;`
  - `:343-357` the whole `err_agp` block — including upstream's
    `err_free: drm_dev_unref(dev);` — is `#if 0`'d out. On any register failure the
    function returns with `dev` allocated and never freed. Upstream Linux drm_pci.c
    has a live `drm_dev_unref(dev)` on this path; DragonFly's port stubbed it.
- Reachability: `radeon_driver_attach` (radeon_drv.c:812-839) →
  `radeon_pci_probe` (radeon_drv.c:433, the **only live `drm_get_pci_dev` caller**
  in the tree — drm_pci.c:404 sits in the fully-`#if 0` `drm_legacy_pci_init`,
  which also WARNs+returns `-EINVAL` for non-DRIVER_LEGACY drivers).
- Failure frequency: `drm_dev_register` (drm_drv.c:877-924) fails whenever
  `radeon_minor_register` fails, or `driver->load` =
  `radeon_driver_load_kms` (radeon_kms.c:95-173) returns the fatal-init error
  ("Fatal error during GPU init", radeon_kms.c:137). Load failures are routine with
  marginal/unsupported ATI hardware, bad firmware, or failed TTM init.
- Leak boundary: `radeon_driver_load_kms` self-cleans via
  `radeon_driver_unload_kms(dev)` (radeon_kms.c:170); `drm_dev_register`'s
  `err_minors` (drm_drv.c:915-918) unregisters minors only. So exactly one
  `struct drm_device` (drm_dev_alloc: drm_drv.c:717-735) leaks per failed attach.
- The radeon-side caller does its part correctly: `drm_fini_pdev(&pdev)`
  (radeon_drv.c:836) frees the `pci_dev` mirror on every error return of
  `radeon_pci_probe`, whether DF-positive (`ENXIO`, :391/:428/:804) or
  Linux-negative (from drm_get_pci_dev) — both are non-zero and handled.

## Impact ceiling

Kernel memory exhaustion over repeated failed attaches (boot-time on broken ATI
hardware, or root re-running `kldload radeon` after each failure — the probe can be
re-attempted indefinitely). No unprivileged trigger; no info leak; no corruption.
Severity Low (CWE-401).

## Exploit chain

None (leak only; requires root or already-broken hardware at boot).

## Fix validation

`fix.diff` restores the upstream unwind (`drm_dev_unref(dev)` — the function exists
at drm_drv.c:789 — moved out of the `#if 0` block). Cannot be validated on this
guest (no ATI hardware → neither baseline nor patched behavior is observable);
`fix_status: not_testable`. Cross-check on real hardware would be:
baseline — repeated kldload with forced load failure leaks (vmstat -m / kldunload
without memory returning); patched — no leak.

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