Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] Fatal NULL pointer dereference in msm_kms_fini_fb_unpin() when handling initialization errors if msm_kms_init_fb_unpin() was not completed. - [High] Deferred framebuffer unpin logic creates a TOCTOU race for shared framebuffers, leading to premature synchronous unpin and IOMMU faults. --
commit ce24ef75bb4418725e7dde4f8dcb994be7c35638 Author: Dmitry Baryshkov <[email protected]> drm/msm: release scanout framebuffers only after a vblank This commit defers the release of scanout framebuffers until after a vblank has occurred by queuing the unpin work via a vblank worker. It prevents hardware from fetching unmapped memory by ensuring the new configuration is fully latched before detaching the VMA. > diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c > index dfe040d5260bc..2e317bec9fbbb 100644 > --- a/drivers/gpu/drm/msm/msm_fb.c > +++ b/drivers/gpu/drm/msm/msm_fb.c [ ... ] > @@ -144,6 +145,9 @@ void msm_framebuffer_cleanup(struct drm_framebuffer *fb, > bool needed_dirtyfb) > > memset(msm_fb->iova, 0, sizeof(msm_fb->iova)); > > + if (crtc && msm_crtc_queue_fb_unpin(crtc, fb)) > + goto out; > + > for (i = 0; i < n; i++) { > msm_gem_unpin_iova(fb->obj[i], vm); > msm_gem_vma_put(fb->obj[i]); [Severity: High] Does this deferred unpin logic handle shared framebuffers safely? If a framebuffer is shared across multiple CRTCs and an atomic commit removes it from all of them simultaneously, msm_framebuffer_cleanup() is called sequentially for each plane. The last plane processed drops prepare_count to zero and funnels the unpin to a single CRTC. If that final CRTC happens to be disabled (where crtc->state->active is false in msm_crtc_queue_fb_unpin()), it returns false, causing an immediate synchronous unpin here. This could fault any other active CRTCs that haven't reached their vblank and are still actively scanning out the old frame. Even if active, could the last processed CRTC reach its vblank and unpin the buffer before slower CRTCs finish their frames? [ ... ] > diff --git a/drivers/gpu/drm/msm/msm_kms.c b/drivers/gpu/drm/msm/msm_kms.c > index e5d0ea6294484..6a857ad3464c4 100644 > --- a/drivers/gpu/drm/msm/msm_kms.c > +++ b/drivers/gpu/drm/msm/msm_kms.c [ ... ] > +void msm_kms_fini_fb_unpin(struct drm_device *dev) > +{ > + struct msm_drm_private *priv = dev->dev_private; > + struct msm_kms *kms = priv->kms; > + struct drm_crtc *crtc; > + > + drm_for_each_crtc(crtc, dev) { > + unsigned int idx = drm_crtc_index(crtc); > + > + if (idx >= ARRAY_SIZE(kms->fb_unpin)) > + continue; > + > + drm_vblank_work_cancel_sync(&kms->fb_unpin[idx].vblank_work); [Severity: High] Can this cause a NULL pointer dereference on initialization error paths? If msm_drm_kms_init() fails after CRTCs are created but before msm_kms_init_fb_unpin() succeeds (for example, if drm_vblank_init() fails), the error path calls msm_drm_kms_uninit(), which unconditionally calls msm_kms_fini_fb_unpin(). Because the vblank_work structs were zero-allocated and never initialized, drm_vblank_work_cancel_sync() will dereference a NULL work->vblank: drivers/gpu/drm/drm_vblank_work.c:drm_vblank_work_cancel_sync() { struct drm_vblank_crtc *vblank = work->vblank; struct drm_device *dev = vblank->dev; ... } -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=3
