# DF-5134 — VERDICT: reproduced (permanent kernel memory + fd-table leak), fix validated

## Mechanism (source)

* Linux `dma_buf_fd()` = `get_unused_fd_flags()` + `fd_install(fd, file)`;
  `fd_install` moves the CALLER's reference into the fd table.
* DragonFly `fsetfd()` does NOT consume: `fsetfd_locked()` (kern_descrip.c:2190-2194)
  does `fhold(fp)` then installs — the table takes a NEW reference.
* `sys/dev/drm/linux_dma-buf.c:139-166` (`dma_buf_fd`) never issues the
  balancing `fdrop()`, and its only caller that transfers a reference
  (`drm_gem_prime_handle_to_fd`, drm_prime.c:668-685) deliberately does NOT
  `dma_buf_put()` on success — correct under Linux fd_install semantics,
  fatal under this port's fsetfd semantics.

Per successful PRIME export the falloc reference from `dma_buf_export()`
(linux_dma-buf.c:123) is orphaned. Because it never reaches 0,
`dmabuf_close()` (linux_dma-buf.c:54-68) never runs, so
`drm_gem_dmabuf_release()` never drops the GEM-object reference taken by
`drm_gem_dmabuf_export()` (drm_prime.c:368-372), and the `drm_dev_get()`
leaks too. The GEM object being immortal also means its backing storage
(i915: shmem pages of attacker-chosen size) is pinned forever, and each
iteration permanently consumes one `struct file` from the global table
(`nfiles` vs `kern.maxfiles` = 130112 on the guest) — an unprivileged
render-node loop exhausts kernel memory and then the system-wide fd table.

## Result (baseline, stock modules)

```
$ su -m nobody -c /tmp/leak_trigger
[leak_trigger] uid=65534 pid=1296 got dmabuf fd=3 (refs: 1 from dma_buf_export/falloc + 1 from fd table)
[leak_trigger] closing fd=3; after this NO ONE holds the buffer in userland or kernel except the orphaned export ref
[leak_trigger] check: orphan f_count=1 => LEAK: struct file + dma_buf never freed (repro)
LEAK_RC=2

(dmesg)
dmabug: mkfd: uid=65534 dmabuf=0xfffff8008d981f98 file=0xfffff8011738c900 fd=3 f_count=2
dmabug: check: dmabuf=0xfffff8008d981f98 file=0xfffff8011738c900 f_count=1 (fd closed, no other owner exists)
```

The file is alive at f_count=1 with zero owners — the orphan, proven live by
an unprivileged (uid 65534) run using only the stock `dma_buf_export()` /
`dma_buf_fd()` code. In the real drm_prime path this orphan additionally
pins the GEM object, its pages, and the drm_device (see README).

## Fix validation

`fix.diff` = `fdrop(dmabuf->file)` after `fsetfd()` in `dma_buf_fd()`
(mirrors Linux's fd_install consumption; also carries DF-5133's
fo_shutdown line, which does not affect this test). drm.ko rebuilt,
installed, rebooted, identical run as uid 65534:

```
dmabug: mkfd: ... fd=3 f_count=1 (expect 2: falloc + fd-table)
dmabug: ops->release(dmabuf=0xfffff8008d602060) ran -> dmabuf_close() frees it
dmabug: check: ops->release ran + file freed -> NO leak (fixed kernel)
LEAK_RC=0
```

fix_baseline_reproduced=1 (leak on stock), fix_patched_reproduced=0
(no leak after fix). fix_status=fixed.

## Caveat

No GPU on the audit guest: the kmod performs the two stock calls the prime
ioctl would; the refcount accounting under test (falloc/fsetfd/close/
dma_buf_put ledger) is entirely the stock kernel's. The drm_prime-level
amplification (GEM object + pages + drm_device also immortal, because
dmabuf_close never runs) is source-traced, not guest-demonstrated.

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