β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4195

drm_connector_register() error path leaves dangling connector->kdev β€” latent double-free on re-register; unchecked kzalloc in the sysfs stub

Summary

A late_register failure unwinds through drm_sysfs_connector_remove, which kfrees connector->kdev without NULLing it; any re-register early-returns on the stale non-NULL kdev yet marks the connector REGISTERED; the eventual unregister kfrees the dangling pointer (double free). Separately, drm_sysfs_connector_add dereferences an unchecked kzalloc result - NULL-deref panic under OOM at registration. No in-tree driver retries register after failure, so the double-free is a latent API hazard. NULL-after-kfree + check-alloc fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4195 Β· 5 files
FileTypeDescriptionSize
VERDICT.md β€” 2.7 KB ↓ raw
README.md β€” 572 B ↓ raw
fix.diff β€” 871 B view raw
manifest.json β€” 814 B view raw
verdict.json β€” 2.5 KB view raw

DF-4195 evidence pack β€” drm_connector_register/unregister kdev lifecycle hazard

Latent double-free (re-register after failed register) + unchecked kzalloc NULL-deref in the DFBSD sysfs stub. Low severity, source-certain, reachability speculative (no in-tree retry path; OOM-gated).

Contents: VERDICT.md full narrative with path:line citations fix.diff NULL the kdev on remove; check kzalloc in add manifest.json / verdict.json

No runtime PoC: guest has no DRM device (see ../DF-4194/env.txt) and neither branch is reachable in-tree today. Status: untested.

VERDICT.md
↓ download raw

DF-4195 β€” drm_connector_register/unregister kdev lifecycle hazard (dangling pointer, latent double-free)

sys/dev/drm/drm_connector.c β€” pass 2, Low, code-certain / reachability speculative.

The issue

On the error path of drm_connector_register() (sys/dev/drm/drm_connector.c:453-467), a late_register (or, where compiled in, debugfs) failure unwinds through drm_sysfs_connector_remove() (sys/dev/drm/drm_sysfs.c:48-55), which does kfree(connector->kdev) without clearing connector->kdev, and leaves registration_state == DRM_CONNECTOR_INITIALIZING (line 441 gates progress; the REGISTERED assignment at line 461 is never reached).

Two consequences in the combined drm_connector.c + drm_sysfs.c lifecycle:

  1. Re-register leaves REGISTERED with a dangling kdev. If any driver ever calls drm_connector_register() again on such a connector, state is still INITIALIZING, so the function proceeds β€” but drm_sysfs_connector_add() (drm_sysfs.c:37) early-returns on the stale non-NULL connector->kdev ("already added"), the connector is marked REGISTERED with a freed kdev, and the eventual drm_connector_unregister() β†’ drm_sysfs_connector_remove() performs kfree() on the already-freed pointer β†’ double free. In-tree today no driver retries register on a failed connector (grep of all drm_connector_register( call sites: drm_connector_register_all at device registration once; MST hotplug paths register each connector once after creation), so this is a latent API hazard, not a demonstrated in-tree trigger β€” hence Low / speculative reachability.
  2. Unchecked kzalloc NULL-deref in the stub drm_sysfs_connector_add() (drm_sysfs.c:41-44): connector->kdev = kzalloc(...); connector->kdev->kobj.name = kasprintf(...) dereferences the unchecked allocation β†’ NULL-deref panic under kernel memory pressure (device registration / MST hotplug).

Fix

fix.diff: clear connector->kdev = NULL after kfree in drm_sysfs_connector_remove(), and check the kzalloc result in drm_sysfs_connector_add() (return -ENOMEM).

Verification

Not runtime-verified: the audit guest has no DRM device (env.txt in the DF-4194 pack), the re-register path has no in-tree caller, and the NULL-deref needs an OOM at registration. Recorded as Low with source-level certainty.

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.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff (NULL kdev after kfree + check both allocations in add) removes both failure modes at the source; not kernel-build validated because the guest has no DRM hardware.

findings/poc/DF-4195/fix.diff
↓ fix.diffper-fix-DF-4195

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (path:line walkthrough); fix.diff (kdev=NULL on remove + kzalloc/kasprintf checks); caller grep summarized in VERDICT.md; ../DF-4194/env.txt (no DRM on guest).

PoC changes

No seed PoC; no runtime PoC possible (no DRM device on guest; no in-tree re-register caller).

Verified recommended fix

In drm_sysfs_connector_remove() set connector->kdev = NULL after kfree; in drm_sysfs_connector_add() check the kzalloc/kasprintf results and return -ENOMEM.

Verdict

Code-certain lifecycle defect, reachability speculative. drm_connector_register()'s error unwind (sys/dev/drm/drm_connector.c:453-467) calls drm_sysfs_connector_remove(), which kfrees connector->kdev without NULLing it (sys/dev/drm/drm_sysfs.c:48-55) and leaves registration_state == INITIALIZING. A re-register would early-return on the stale non-NULL kdev (drm_sysfs.c:37) yet mark the connector REGISTERED, so the eventual unregister kfrees the dangling pointer (double free); separately drm_sysfs_connector_add() dereferences an unchecked kzalloc result (drm_sysfs.c:41-44) β€” NULL-deref panic under OOM at device registration/MST hotplug. Grep of every drm_connector_register() caller shows no in-tree retry after failure (register_all runs once; MST hotplug registers each connector once after creation), and the guest has no DRM device, so neither branch was exercised at runtime.