drm_mode_config_init() ignores drm_mode_create_standard_properties() failure: NULL standard properties are attached to every universal plane and atomic CRTC, turning any later unprivileged MODE_OBJ_GETPROPERTIES/GETCONNECTOR ioctl into a kernel NULL-deref panic (upstream-inherited, ENOMEM-gated)
Summary
~20 sequential allocations, each can ENOMEM mid-way leaving later prop_* NULL; all three drivers treat init as infallible and attach the NULLs to every plane/CRTC (attach has no NULL check) - the device registers normally and the first unauthenticated MODE_OBJ_GETPROPERTIES (DRM_RENDER_ALLOW) derefs prop->flags -> panic. Read-only derefs (DoS ceiling); upstream closed the class with drmm_mode_config_init. ENOMEM-at-load precondition; no /dev/dri on guest. Propagate-the-failure fix across the 3 call sites.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4879 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.9 KB | β raw | |
| VERDICT.md | β | 5.2 KB | β raw | |
| trigger.c | β | 1.6 KB | view raw | |
| drm.h | β | 874 B | view raw | |
| build.sh | β | 150 B | view raw | |
| fix.diff | β | 3.2 KB | view raw | |
| verdict.json | β | 4.0 KB | view raw |
DF-4879 β unchecked drm_mode_create_standard_properties() in drm_mode_config_init()
File: sys/dev/drm/drm_mode_config.c (pass 2)
Severity: Low (kernel NULL-deref panic, setup-gated)
Status: source-proven, NOT PoC-verified on this guest (see VERDICT.md)
Summary
drm_mode_config_init() (sys/dev/drm/drm_mode_config.c:370) calls
drm_mode_create_standard_properties(dev) at line 392 and discards its
return value. That helper performs ~20 sequential allocations
(drm_mode_config.c:217-355) and returns -ENOMEM on the first failure,
leaving dev->mode_config.prop_fb_id / prop_crtc_id / prop_crtc_x..h /
prop_active / prop_mode_id / prop_src_x..h / ... NULL (only the earlier
ones are set).
Driver load then continues with half-initialized mode_config:
drm_universal_plane_init() attaches those (possibly NULL) property
pointers to every plane (drm_plane.c:262-275:
drm_object_attach_property(&plane->base, config->prop_fb_id, 0) β¦ β no
NULL check), and drm_crtc_init_with_planes() attaches
config->prop_active / config->prop_mode_id (drm_crtc.c:339-341).
Any later unprivileged ioctl that enumerates object properties β
DRM_IOCTL_MODE_OBJ_GETPROPERTIES (drm_ioctl.c:678, DRM_UNLOCKED |
DRM_RENDER_ALLOW, no auth) or DRM_IOCTL_MODE_GETCONNECTOR
(drm_connector.c:1820) β walks the property array and dereferences
prop->flags with no NULL check (drm_mode_object.c:341-346) β kernel
NULL-deref β panic.
Preconditions / honesty
- The NULL-prop state requires
-ENOMEMduring driver load (root-triggered setup: kldload/boot-time memory pressure). i915, radeon and amdgpu all ignore the (void) init result today (intel_display.c:15342, radeon_display.c:1579, amdgpu_device.c:2604). - Once the device is in that state, the panic trigger is a plain unprivileged ioctl on /dev/dri/card0 or /dev/dri/renderD128.
- Inherited from Linux (~4.19
drm_mode_config_init); upstream later fixed the class by moving todrmm_mode_config_init()which fails the whole device init. DragonFly port kept the void variant. - Derefs are reads only (
prop->flags,prop->base.id) β no write primitive, no kernel info leak (page-0 mapping would only read back attacker-crafted bytes). Impact ceiling: local DoS/panic.
Why untested here
The audit QEMU guest has no DRM device (/dev/dri absent β QEMU VGA,
no i915/radeon/amdgpu binding), so DRM_IOCTL_MODE_OBJ_GETPROPERTIES
cannot even be issued; and the ENOMEM-at-load precondition cannot be
injected without kernel fault-injection knobs this guest does not have.
The chain is verified line-by-line in source instead (VERDICT.md).
Contents
VERDICT.mdβ full narrative with path:line evidencetrigger.cβ reference prober (would exercise the deref if a NULL-prop device existed; NOT runnable on this guest)build.shβ cc command for trigger.cfix.diffβ propagate the failure (git-apply-able, never applied to sys/)verdict.json/manifest.json
DF-4879 VERDICT β drm_mode_config_init() ignores property-creation failure
Status: untested (source-proven; not runnable on this guest β no DRM device, no fault injection). Impact if manifested: local kernel panic (DoS), Low. Not memory corruption (read-only NULL derefs).
The defect (certain, source)
sys/dev/drm/drm_mode_config.c:392
void drm_mode_config_init(struct drm_device *dev)
{
...
drm_mode_create_standard_properties(dev); /* ret discarded */
drm_mode_create_standard_properties() (same file, 212-356) creates
~20 properties through drm_connector_create_standard_properties()
(line 217) and then drm_property_create_{range,signed_range,object,bool}
/ drm_property_create calls (lines 221-353). Each returns NULL on
allocation failure; the helper bails with -ENOMEM at the first NULL.
On a mid-way failure the earlier dev->mode_config.*_property pointers
are set and the rest stay NULL (struct comes from kzalloc'd drm_device).
The failure is invisible to all three in-tree callers, which treat init as infallible:
sys/dev/drm/i915/intel_display.c:15342βdrm_mode_config_init(dev);sys/dev/drm/radeon/radeon_display.c:1579βdrm_mode_config_init(rdev->ddev);sys/dev/drm/amd/amdgpu/amdgpu_device.c:2604βdrm_mode_config_init(adev->ddev);
Propagation to NULL pointers stored in objects (certain)
drm_universal_plane_init() attaches the standard properties to every
plane without NULL checks β sys/dev/drm/drm_plane.c:262-275:
drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
drm_object_attach_property(&plane->base, config->prop_in_fence_fd, -1);
drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
...
drm_object_attach_property() (drm_mode_object.c) performs no NULL
validation. Atomic CRTC init attaches config->prop_active /
config->prop_mode_id the same way (sys/dev/drm/drm_crtc.c:339-341).
radeon marks mode_config_initialized = true unconditionally after init
(radeon_display.c:1580), i.e. even a half-failed init proceeds to
full registration (drm_modeset_register_all()).
Unprivileged sink (certain reachability once state exists)
drm_mode_object_get_properties() β sys/dev/drm/drm_mode_object.c:341-346:
for (i = 0, count = 0; i < obj->properties->count; i++) {
struct drm_property *prop = obj->properties->properties[i];
...
if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic) /* NULL deref */
Reached from:
* DRM_IOCTL_MODE_OBJ_GETPROPERTIES β drm_mode_obj_get_properties_ioctl()
(drm_mode_object.c:382-407), registered DRM_UNLOCKED|DRM_RENDER_ALLOW
β no authentication, render-node allowed (drm_ioctl.c:678).
* DRM_IOCTL_MODE_GETCONNECTOR β drm_connector.c:1820.
Trigger: open /dev/dri/card0 (or renderD128), GETPLANERESOURCES /
GETRESOURCES to learn any plane/crtc/connector id, then
MODE_OBJ_GETPROPERTIES on it with count_props>0 β prop->flags read at
NULL+offset β kernel page fault β panic. All subsequent uses
(__drm_object_property_get_value at 296-304 compares pointers, then
prop->base.id at 353) are reads too β no write primitive, no kernel
memory disclosure (if the attacker mapped page zero, the reads return
their own bytes). DoS only.
Why not reproduced on the guest
- Guest has no DRM device:
/dev/dridoes not exist (QEMU std VGA; no i915/radeon/amdgpu attaches), so the trigger ioctl has no target. - The setup precondition (
-ENOMEMinsidedrm_mode_create_standard_propertiesduring driver load) cannot be forced: the guest has no memory-allocation fault-injection knob, and the DRM drivers cannot be meaningfully loaded without their GPUs. - Verified live on guest
dfbsd 6.5-DEVELOPMENT #0 Thu Jul 2 06:02:54 UTC 2026:ls -l /dev/dri/βNo such file or directory.
Classification: real defect, upstream-inherited, Low severity ("narrow trigger, requires already-privileged setup" per rubric). Fix is cheap and local (see fix.diff): propagate the error.
Fix validation
Not possible on this guest (same reasons as above) β
fix_status: not_testable. The diff is compile-logic-equivalent to
upstream's drmm_mode_config_init() handling (which fails device init
when standard property creation fails).
Guest state notes (unrelated findings live-checked this run)
/dev/nvmmdoes not exist on this guest:kldload nvmmloads nvmm.ko but MOD_LOAD fails withnvmm: cpu not supported(nvmm_dragonfly.c:nvmm_ident() == NULLβ QEMU CPU exposes no SVM/VMX),module_register_init: MOD_LOAD (nvmm) error 45. When the backend does attach, the node is createdroot:nvmm 0640(nvmm_dragonfly.c:392) β NVMM is not an unprivileged surface by default (only root or members of groupnvmmgid 90 can open it).
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_testableCannot build a DRM-bearing kernel config on this guest (no GPU to bind; also cannot force the ENOMEM precondition), so baseline-vs-patched comparison is not testable here. The fix is the exact semantic upstream adopted (drmm_mode_config_init fails device init when standard property creation fails).
fix.diff (git-apply-able, never applied to the read-only sys/ tree): signature change + ret propagation in drm_mode_config.c:370-401, prototype in include/drm/drm_mode_config.h:870, abort-load at intel_display.c:15342, radeon_display.c:1579, amdgpu_device.c:2604
Confirmed kernel references
- sys/dev/drm/drm_mode_config.c:392
- sys/dev/drm/drm_mode_config.c:217
- sys/dev/drm/drm_mode_config.c:221
- sys/dev/drm/drm_plane.c:262
- sys/dev/drm/drm_plane.c:267
- sys/dev/drm/drm_crtc.c:339
- sys/dev/drm/drm_mode_object.c:341
- sys/dev/drm/drm_mode_object.c:345
- sys/dev/drm/drm_ioctl.c:678
- sys/dev/drm/i915/intel_display.c:15342
- sys/dev/drm/radeon/radeon_display.c:1579
- sys/dev/drm/amd/amdgpu/amdgpu_device.c:2604
Detail
Evidence (decisive lines)
VERDICT.md carries the full path:line chain (init:392 -> attaches: drm_plane.c:262-275/drm_crtc.c:339-341 -> sink: drm_mode_object.c:345 via drm_ioctl.c:678). trigger.c is the reference prober (count_props=64 forces the prop->flags read). Guest live-check: /dev/dri absent (documented in VERDICT.md).
PoC changes
no seed PoC existed; trigger.c written from scratch as a reference prober, never executed (no DRM device on guest)
Verified recommended fix
Make drm_mode_config_init() return int, propagate drm_mode_create_standard_properties() failure, and abort driver load at the three call sites (fix.diff mirrors upstream's drmm_mode_config_init semantics)
Verdict
Source-proven defect, not runnable on the audit guest: drm_mode_config_init() (sys/dev/drm/drm_mode_config.c:392) ignores the -ENOMEM return of drm_mode_create_standard_properties() (lines 217-353), leaving later standard properties NULL; drm_universal_plane_init()/drm_crtc_init_with_planes() attach those NULL pointers to every plane/CRTC (drm_plane.c:262-275, drm_crtc.c:339-341), and the unauthenticated DRM_IOCTL_MODE_OBJ_GETPROPERTIES (drm_ioctl.c:678, DRM_UNLOCKED|DRM_RENDER_ALLOW) dereferences prop->flags (drm_mode_object.c:345) -> kernel NULL-deref panic. Precondition is allocation failure during privileged driver load (i915/radeon/amdgpu all treat init as infallible: intel_display.c:15342, radeon_display.c:1579, amdgpu_device.c:2604); the trigger itself is an unprivileged ioctl. Guest has no /dev/dri (no GPU) and no memory-fault-injection knob, so no live PoC was possible. Derefs are read-only: no write primitive, no kernel disclosure; impact ceiling is local DoS (panic). Upstream-inherited (Linux ~4.19 void drm_mode_config_init); upstream later fixed the class via drmm_mode_config_init() which fails device init.
No comments yet.