# DF-2208 — VERDICT

**File:** `sys/dev/drm/drm_sysfs.c:39-42` (drm_sysfs_connector_add)
**Claim:** kzalloc/kasprintf results used without NULL checks; OOM during
connector registration → NULL-deref write at line 40 → panic.

## Bottom line

**false_positive for the filed threat model (OOM-triggered NULL deref),
with the code-shape claim mechanically PROVEN including an actual kernel
panic when the NULL is injected.**

## Line-accurate confirmation

- `drm_sysfs.c:39` — `connector->kdev = kzalloc(sizeof(struct device), GFP_KERNEL);`
  no NULL check.
- `drm_sysfs.c:40-42` — `connector->kdev->kobj.name = kasprintf(...)` —
  immediate dereference of the unchecked kzalloc result; the kasprintf
  result is stored unchecked as well.
- `drm_sysfs.c:45` — unconditional `return 0`: callers
  (`drm_connector.c:444-446`) can never see an allocation failure.
- Contrast: `drm_sysfs_minor_alloc()` at `drm_sysfs.c:93-95` does check
  kzalloc and returns `ERR_PTR(-ENOMEM)`.

## Mechanical reproduction (verbatim-code KLD rig, DF-2214 pattern)

The UNMODIFIED production function (tree lines 31-46, sha256-pinned,
extracted on the guest from a tree byte-identical to the audited tree)
was compiled kernel-resident with production-faithful allocator shims
(`kzalloc`→`kmalloc(size, M, flags|M_ZERO)` per slab.h:35; `kasprintf`
per drm_dragonfly.c:65-94) whose ONLY delta is optional fault injection
at exactly the two cited call sites.

- **CASE B (kasprintf NULL injected, run.log):** `ret=0`,
  `kdev != NULL`, `kobj.name == NULL` — the function reports SUCCESS
  with a NULL name planted; the first consumer deref
  (`intel_dp.c:5251` `connector->kdev->kobj.name`) would fault.
- **CASE C (kzalloc NULL injected, panic.txt):** `sysctl
  kern.df2208_fire=1` → verbatim add writes at NULL+8:
  ```
  Fatal trap 12: page fault while in kernel mode
  fault virtual address = 0x8
  fault code = supervisor write data, page not present
  Stopped at drm_sysfs_connector_add+0x58: movq %rax,0x8(%r12)
  ```
  `offsetof(struct device, kobj.name) == 8` in both production layout
  (linux/device.h:44-47, linux/kobject.h:50-51) and the rig layout.

## Why false_positive on DragonFly: the OOM precondition cannot occur

The filed trigger is "unprivileged local user induces kernel memory
pressure → kzalloc/kasprintf return NULL". On DragonFly that NULL can
never come back from these calls:

1. `GFP_KERNEL` is `(M_WAITOK | M_CACHEALIGN)` —
   `sys/dev/drm/include/linux/gfp.h:16`. No `M_NULLOK`.
2. `kzalloc(size, flags)` → `kmalloc(size, M_DRM, flags | M_ZERO)` —
   `sys/dev/drm/include/linux/slab.h:35`.
3. `_kmalloc()` (sys/kern/kern_slaballoc.c):
   - zone-limit path (:869-878): returns NULL **only** with
     `M_NULLOK`; otherwise `panic("%s: malloc limit exceeded")`.
   - new-slab path `kmem_slab_alloc()` (:1722-1724): kernel_map
     exhaustion returns NULL **only** with `M_NULLOK`; otherwise
     `panic("kmem_slab_alloc(): kernel_map ran out of space!")`.
   - page-starvation path (:1786-1800): with `M_WAITOK` the allocator
     `vm_wait()`s and retries forever — it never returns NULL.
4. `kvasprintf()` (sys/dev/drm/drm_dragonfly.c:65-82) allocates with
   the caller flags (`GFP_KERNEL`) and its `if (str == NULL) return
   NULL;` at :78-79 is dead code for GFP_KERNEL callers on DFly.

Under the claimed OOM conditions the kernel therefore panics INSIDE the
allocator (or blocks) before `drm_sysfs.c:40` can ever execute with a
NULL `kdev`. The cited deref is unreachable through the cited trigger
on this platform. (On Linux, `kzalloc(GFP_KERNEL)` CAN return NULL, so
the upstream-pattern bug is real there; the DFly port simply cannot
reach it.)

## Privesc assessment (standing directive)

Not a memory-corruption primitive on DragonFly: the unreachable write
is to a fixed near-NULL offset (`NULL+8`), value a kasprintf string
pointer — no control surface even if it were reachable. No uid=0
route; not applicable beyond the panic DoS shape that itself cannot be
triggered as filed.

## Fix validation (A/B)

`fix.diff` adds the two NULL checks with `-ENOMEM` returns (and, in
the remove half shared with DF-2209, NULLs `connector->kdev` and frees
the name string).

- **In-rig A/B (same KLD):** with identical injections the FIXED
  variant returns `ret=-12 (-ENOMEM)`, `kdev=NULL`, no fault, no silent
  NULL planted (run.log CASE E). Callers fail loudly via
  `drm_connector.c:445-446`.
- **Real-module compile validation:** fix.diff applied to the guest
  `/usr/src` tree; the production `drm` KLD module rebuilt clean with
  `-Werror` (`fix_module_build.log`, drm.ko relinked, zero errors —
  the only "error" grep hit is the `-Werror` flag string itself).
  Full-kernel runtime validation is not meaningful on this guest: it
  has no DRM device (`/dev/dri` absent, drm.ko not attached) and
  runtime kldload of the stock drm module GPFs on this guest, so the
  production path cannot execute there — same justification as the
  DF-2214 precedent (verbatim kernel-resident code is the executed
  artifact).

## Side observation (filed into the fix)

`drm_sysfs_connector_remove()` frees only `connector->kdev`; the
kasprintf'd `kobj.name` string leaks per connector add/remove cycle
(run.log CASE A: alloc=2 free=1). fix.diff frees it.

## Artifacts

- `run.log`, `run.2.log` — decisive + repeat matrix runs (fresh guest)
- `run_dmesg.log` — full dmesg of the decisive run
- `build.log` — full untrimmed build+run transcript (provenance chain,
  hashes, cc 8.3 kernel-context build)
- `panic.txt`, `fire_serial_full.log` — the CASE C fatal trap from the
  serial console (guest died by design; reset afterwards)
- `fix_module_build.log` — patched real drm module build
- `df22_rig.c` — assembled source as compiled (baseline + fixed)
- `env.txt` — guest environment

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