# DF-3635 — `i915_gem_object_get_pages_phys()` error path leaks the GEM shmem vm_object hold+token

## What

`i915_gem_object_get_pages_phys()` takes the GEM backing-store vm_object
lock for the whole page-in copy loop:

```c
305	VM_OBJECT_LOCK(vm_obj);
306	for (i = 0; i < obj->base.size / PAGE_SIZE; i++) {
...
313		page = shmem_read_mapping_page(vm_obj, i);
315		if (IS_ERR(page)) {
316			err = PTR_ERR(page);
317			goto err_phys;          /* <-- lock still held */
318		}
...
328	VM_OBJECT_UNLOCK(vm_obj);          /* success path only */
...
357 err_phys:
358	drm_pci_free(obj->base.dev, phys);
359
360	return err;                          /* <-- returns with hold+token leaked */
```

(The `#if 0` Linux original has no VM object locking at all; the
lock/unlock pair is DragonFly-port glue, and the error jump bypasses the
unlock. The sibling function `i915_gem_object_get_pages_gtt()`
*does* unlock on its error paths — `VM_OBJECT_UNLOCK(mapping)` at
i915_gem.c:3032 — proving the omission here is accidental.)

On this tree `VM_OBJECT_LOCK` is `vm_object_hold()` (sys/vm/vm_object.h:267)
= `refcount_acquire(&obj->hold_count)` + `lwkt_gettoken(&obj->token)`
(sys/vm/vm_object.c:283-294). Returning without the matching
`VM_OBJECT_UNLOCK`/`vm_object_drop` therefore leaks both a hold
reference and a token reference on the object that backs the GEM bo:
the object can never be reclaimed (hold_count pinned), and the owning
thread carries an unbalanced token count (serialization accounting
corrupted for every later VM operation on that object by that thread).

## Reachability (why Low)

1. Only `i915_gem_object_attach_phys()` (i915_gem.c:6670) installs the
   phys ops whose `->get_pages` is this function, and on this tree
   `attach_phys` is called only from the display modesetting path
   (`intel_display.c:13183`) — DRM master territory, not direct
   unprivileged ioctl.
2. The error return requires `shmem_read_mapping_page()` to fail on the
   OBJT_DEFAULT backing object: fresh pages are zero-filled
   unconditionally (default pager `haspage` = FALSE,
   sys/vm/default_pager.c:138-141), so failure needs the page to have
   real swap backing (`vm_pager_get_page` → swap_pager I/O error) —
   i.e. swap-device failure while the bo is paged out.

Once triggered: permanent vm_object leak + token imbalance; worst case
a wedge of subsequent VM operations on that GEM object (local DoS, no
memory-safety impact).

## Fix

One-line unlock before the jump — see `fix.diff`.

## Verification

Not attempted at runtime (Low severity, hardware path): the guest has no
i915 device. Status: untested / source-certain. The identical error-path
discipline in `i915_gem_object_get_pages_gtt` (:3026-3033) confirms the
intended pattern.
