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)
PoC verification
Evidence pack
findings/poc/DF-4436 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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
:193drm_mode_object_add(dev, &plane->base, DRM_MODE_OBJECT_PLANE)registers the plane as a mode object (allocates an IDR id).:202-208plane->format_types = kmalloc_array(format_count, ...).:214-215if (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).
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_testableNo 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)
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.
No comments yet.