DF-4471 — wrapper-less __drm_helper_disable_unused_functions NULLs crtc->primary->fb without dropping its reference =================================================================================================================== File: sys/dev/drm/drm_crtc_helper.c (pass 2; upstream v4.15-identical). No runnable PoC — pure reference-accounting defect, hardware-gated the same way as DF-4469/DF-4470 (needs radeon/amdgpu + a mode configuration with a temporarily-unused CRTC), and the observable effect (fb/GEM object pinned past destroy) needs kernel introspection to see. Verified by code trace: Reference protocol ------------------ "crtc->primary->fb owns exactly one framebuffer reference" — established by __drm_mode_set_config_internal (drm_crtc.c:474-498): before set_config every plane->old_fb is snapshotted; after it, get(new plane->fb) / put(old_fb). Any pointer the helper leaves behind is balanced there. The defect ---------- sys/dev/drm/drm_crtc_helper.c:181-191 __drm_helper_disable_unused_functions(): if (!crtc->enabled) { ... crtc->primary->fb = NULL; /* line 189: NO put() */ } Inside set_config the wrapper's put(old_fb) balances the lost pointer (get is skipped for the NULL). But two in-tree callers invoke the function WITHOUT the wrapper: * drm_helper_resume_force_mode() — drm_crtc_helper.c:983 * drm_helper_disable_unused_functions() (exported) — drm_crtc_helper.c:219 (used by drm_fb_helper.c, radeon_device.c, radeon_fb.c, amdgpu_device.c, amdgpu_fb.c — resume + fbdev-restore paths) Each such call drops the plane's fb pointer and leaks its reference: the drm_framebuffer and its GEM bo can never be freed (memory leak; DRM_IOCTL_MODE_RMFB of that fb id will also keep reporting it alive). Repeated suspend/resume cycles (laptop lid events) repeat the leak. Severity: Low (leak only, safe direction, needs the unused-CRTC state and driver-initiated paths; an unprivileged console user can at most influence vt/fbcon restore frequency). Fix --- Track and drop the reference at the NULL assignment site: ```diff --- a/sys/dev/drm/drm_crtc_helper.c +++ b/sys/dev/drm/drm_crtc_helper.c @@ -184,9 +184,11 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev) if (crtc_funcs->disable) (*crtc_funcs->disable)(crtc); else (*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF); + if (crtc->primary->fb) + drm_framebuffer_put(crtc->primary->fb); crtc->primary->fb = NULL; } } } ``` …and remove the matching put from the set_config wrapper accounting, or (simpler policy fix) make the wrapper tolerate the changed refs by snapshotting counts rather than pointers.