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

drm_universal_plane_init: format_count>64 error path returns without kfree(format_types) or drm_mode_object_unregister - leaks the format array and the mode-object id

Summary

The plane is registered as a mode object and format_types allocated BEFORE the format_count>64 guard; that early return performs neither cleanup (unlike the sibling error paths) - leaks one kmalloc plus a mode-object id per failing init. Trigger is driver-static misconfiguration (no in-tree DFly driver does it; count not user-influenced). Upstream master later fixed the ordering. Hoist-the-guard fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4436 Β· 5 files
FileTypeDescriptionSize
README.md β€” 2.1 KB ↓ raw
VERDICT.md β€” 2.3 KB ↓ raw
verdict.json β€” 2.7 KB view raw
manifest.json β€” 809 B view raw
fix.diff β€” 645 B view raw

DF-4436 β€” drm_universal_plane_init: format_count>64 error path leaks format_types and the mode-object ID

Severity / class

Info (probe-time resource leak reachable only through driver misconfiguration; not attacker-controllable at runtime). Upstream-inherited: DF's drm_plane.c is Linux ~v5.0's file verbatim (verified by full diff, see below); modern upstream fixed the ordering by moving the check before drm_mode_object_add (today's master has the format_count > 64 check at drm_plane.c:386, before object add at :393).

Root cause

sys/dev/drm/drm_plane.c:

  • :193 drm_mode_object_add(dev, &plane->base, DRM_MODE_OBJECT_PLANE) registers the plane as a mode object (allocates an IDR id).
  • :202-208 plane->format_types = kmalloc_array(format_count, ...).
  • :214-215 if (WARN_ON(format_count > 64)) return -EINVAL;

That early return -EINVAL skips BOTH kfree(plane->format_types) and drm_mode_object_unregister(dev, &plane->base) β€” unlike the two neighboring error paths (:204-208 and :228-233) which clean up properly. Result per failed init: one leaked kmalloc_array allocation (≀ 64*4 = 256 B here since the check triggers only above 64) plus a leaked mode-object id in dev->mode_config (the plane was not yet on config->plane_list, so nothing else will ever release it).

Trigger

Static driver property: a driver passing format_count > 64 to drm_universal_plane_init(). No in-tree DragonFly driver does (i915/radeon/amdgpu all stay well under 64), and the count is not user-influenced. This is an error-path hygiene defect identical to upstream v5.0; it costs a WARN plus two small leaks per probe attempt of a hypothetical misconfigured driver.

Why not guest-tested

The guest has no DRM device (/dev/dri absent) and no in-tree driver trips the path; the trigger would require writing a new kernel module with a >64-format plane, which proves nothing beyond what static reading already proves. Info finding; QEMU verification not warranted.

Fix

fix.diff β€” move the format_count > 64 guard above drm_mode_object_add (exactly what upstream did in the __drm_universal_plane_init refactor that current master carries).

VERDICT.md
↓ download raw

DF-4436 VERDICT β€” static, upstream-inherited error-path leak

Reproduced?

No runtime verification performed (status: untested). Trigger is a driver-static misconfiguration (format_count > 64); no in-tree DragonFly driver trips it and the guest has no DRM device. Info severity; QEMU verification not mandated.

Why the defect is certain anyway (line-by-line proof)

sys/dev/drm/drm_plane.c: - :193 ret = drm_mode_object_add(dev, &plane->base, DRM_MODE_OBJECT_PLANE); β€” registers the object (IDR id allocated). - :197 drm_modeset_lock_init(&plane->mutex); - :202-203 plane->format_types = kmalloc_array(format_count, sizeof(uint32_t), GFP_KERNEL); - :214-215 if (WARN_ON(format_count > 64)) return -EINVAL; β€” returns WITHOUT kfree(plane->format_types) and WITHOUT drm_mode_object_unregister(dev, &plane->base) (and without drm_modeset_lock_fini), unlike the sibling error paths at :204-208 (alloc failure) and :228-233 (modifiers alloc failure), both of which do clean up.

Leak per call: the format array + a mode-object id (+ an initialized mutex left dangling). Reachable only at driver probe when a driver declares >64 plane formats; the count is compile-time driver data, not user input.

Upstream provenance

DF's drm_plane.c is Linux v5.0's file (full diff on record: only the drm_any_plane_has_format export removed, %jx format adaptation, and manual DRM_MODESET_LOCK_ALL_BEGIN/END expansion). v5.0 has the same leaky ordering. Current upstream master fixed it implicitly by moving the format_count > 64 guard before drm_mode_object_add (master drm_plane.c:386 precedes :393).

Security impact ceiling

None at runtime with in-tree drivers: bounded kernel-memory leak per probe attempt of a hypothetical misconfigured driver + WARN. Filed as Info (error-path hygiene / divergence from fixed upstream).

Fix validation

Not applicable (no runtime repro). fix.diff hoists the guard above drm_mode_object_add, mirroring current upstream.

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

No behavior validation possible (no triggering driver, no DRM device in guest); fix.diff reproduces modern upstream's ordering exactly and applies cleanly (patch -p1 --dry-run OK)

findings/poc/DF-4436/fix.diff (patch -p1 --dry-run: OK)
↓ fix.diffper-fix-DF-4436

Confirmed kernel references

Detail

Evidence (decisive lines)

findings/poc/DF-4436/VERDICT.md (path:line proof + upstream v5.0-vs-DF diff + master fix location); fix.diff (applies cleanly, patch --dry-run verified)

PoC changes

No PoC authored: trigger is driver-static misconfiguration; no in-tree driver trips it and guest has no DRM device

Verified recommended fix

Move the format_count>64 guard above drm_mode_object_add and the format_types allocation, as current upstream does

Verdict

Certain static error-path resource leak, not attacker-reachable at runtime. drm_universal_plane_init registers the plane as a mode object (drm_plane.c:193) and allocates plane->format_types (:202) BEFORE the 'if (WARN_ON(format_count > 64)) return -EINVAL;' at :214-215, which exits without kfree(plane->format_types) and without drm_mode_object_unregister - unlike the sibling error paths at :204-208 and :228-233 which clean up. Trigger is driver-static (a driver declaring >64 plane formats at probe); no in-tree DF driver does. DF's drm_plane.c is Linux v5.0's file verbatim (verified by full diff; only drm_any_plane_has_format removed, %jx adaptation, manual DRM_MODESET_LOCK_ALL expansion), and current upstream master fixed the ordering by moving the guard before drm_mode_object_add (master drm_plane.c:386 vs :393). Untested: trigger requires a hypothetical misconfigured out-of-tree driver and a DRM device; guest has no /dev/dri. Info severity.