Re: [PATCH v3 5/8] drm/gm12u320: replace struct drm_simple_display_pipe with regular atomic helpers

"Ze Huang" <[email protected]>
Newsgroups dev.linux.lists.imx,dev.linux.lists.sashiko-reviews
Message-ID <[email protected]>
On Mon Jul 27, 2026 at 4:13 AM CST, sashiko-bot wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] The driver will crash in `gm12u320_stop_fb_update` due to a NULL pointer dereference when dropping a framebuffer reference. The asynchronous worker `gm12u320_fb_update_work` drops its reference to the framebuffer and sets `gm12u320->fb_update.fb = NULL` after copying the first frame. When the CRTC is later disabled (e.g., via device unplug or KMS disable), `gm12u320_stop_fb_update` reads this NULL pointer into `old_fb` and unconditionally calls `drm_framebuffer_put(old_fb)`. Because `drm_framebuffer_put` computes the address of `&fb->base` before passing it to `drm_mode_object_put()`, a NULL `fb` results in a non-NULL offset pointer, bypassing any potential NULL checks in the core and causing a fatal page fault when dereferenced.
> - [High] The driver calls sleeping functions (`mutex_lock` and `cancel_delayed_work_sync`) from atomic context paths, violating DRM subsystem architectural guidelines. The patch converts the driver to use standard CRTC and plane atomic helpers (`gm12u320_crtc_helper_atomic_enable`, `gm12u320_crtc_helper_atomic_disable`, and `gm12u320_plane_helper_atomic_update`). These callbacks are explicitly classified by DRM guidelines as atomic context where sleeping is forbidden, yet they call functions that block.
> --
>
> --- Patch [5]: [PATCH v3 5/8] drm/gm12u320: replace struct drm_simple_display_pipe with regular atomic helpers ---
> commit 0dd628ecb0a7395ec2d8faaa3a751862640afcb7
> Author: Ze Huang <[email protected]>
>
> drm/gm12u320: replace struct drm_simple_display_pipe with regular atomic helpers
>     
> Convert gm12u320 to direct primary plane, CRTC and encoder setup.
>     
> Keep shadow-plane helper state, framebuffer access helpers and
> no-scaling plane-state check from simple-KMS path.
>
>> diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c
>> index 4ad074337af07..754617722a0db 100644
>> --- a/drivers/gpu/drm/tiny/gm12u320.c
>> +++ b/drivers/gpu/drm/tiny/gm12u320.c
>
> [ ... ]
>
>> @@ -555,43 +560,106 @@ static int gm12u320_conn_init(struct gm12u320_device *gm12u320)
>>  }
>>  
>>  /* ------------------------------------------------------------------ */
>> -/* gm12u320 (simple) display pipe				      */
>> +/* gm12u320 display pipe					      */
>>  
>> -static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe,
>> -				 struct drm_crtc_state *crtc_state,
>> -				 struct drm_plane_state *plane_state)
>> +static void gm12u320_crtc_helper_atomic_enable(struct drm_crtc *crtc,
>> +					       struct drm_atomic_commit *commit)
>>  {
>>  	struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT };
>> -	struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
>> -	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
>> +	struct gm12u320_device *gm12u320 = to_gm12u320(crtc->dev);
>> +	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(commit, &gm12u320->plane);
>> +	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(pstate);
>>  
>>  	gm12u320->fb_update.draw_status_timeout = FIRST_FRAME_TIMEOUT;
>> -	gm12u320_fb_mark_dirty(plane_state->fb, &shadow_plane_state->data[0], &rect);
>> +	gm12u320_fb_mark_dirty(pstate->fb, &shadow_plane_state->data[0], &rect);
>>  }
>
> [Severity: High]
> This isn't a bug introduced by this patch, but does calling
> gm12u320_fb_mark_dirty() here violate DRM subsystem architectural guidelines by
> sleeping in an atomic context?
>
> gm12u320_crtc_helper_atomic_enable() runs in an atomic context during
> non-blocking commits where sleeping is forbidden. However,
> gm12u320_fb_mark_dirty() unconditionally acquires a mutex:
>
> 	mutex_lock(&gm12u320->fb_update.lock);
>
> Could this lead to kernel warnings like 'scheduling while atomic' or deadlocks
> during display modesets? (The same applies to
> gm12u320_plane_helper_atomic_update below).

False positive

>
>> -static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe)
>> +static void gm12u320_crtc_helper_atomic_disable(struct drm_crtc *crtc,
>> +						struct drm_atomic_commit *commit)
>>  {
>> -	struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
>> +	struct gm12u320_device *gm12u320 = to_gm12u320(crtc->dev);
>>  
>>  	gm12u320_stop_fb_update(gm12u320);
>>  }
>
> [Severity: High]
> This is also a pre-existing issue, but does gm12u320_stop_fb_update() sleep in
> an atomic context, and could it cause a NULL pointer dereference?
>
> First, like the enable path above, gm12u320_crtc_helper_atomic_disable() runs
> in an atomic context where sleeping is forbidden. But gm12u320_stop_fb_update()
> calls cancel_delayed_work_sync() and acquires a mutex.

False positive

>
> Second, can old_fb be NULL when passed to drm_framebuffer_put() inside
> gm12u320_stop_fb_update()?
>
> The asynchronous worker gm12u320_fb_update_work() drops its reference to the
> framebuffer and sets gm12u320->fb_update.fb = NULL after copying the first
> frame. If the CRTC is later disabled (e.g., via device unplug or KMS disable),
> gm12u320_stop_fb_update() reads this NULL pointer into old_fb and passes it to
> drm_framebuffer_put(old_fb):

Good catch. If the CRTC is disabled after gm12u320_fb_update_work()
has consumed the pending framebuffer, old_fb will be NULL. The current
disable path then calls drm_framebuffer_put(old_fb) with a NULL pointer.

We can guard it the same way as gm12u320_fb_mark_dirty() [1]:

	if (old_fb)
		drm_framebuffer_put(old_fb);

Maybe we can fix this in a following patch.

[1] https://elixir.bootlin.com/linux/v7.1.3/source/drivers/gpu/drm/tiny/gm12u320.c#L432

>
> static void gm12u320_stop_fb_update(struct gm12u320_device *gm12u320)
> {
> 	struct drm_framebuffer *old_fb;
> 	...
> 	old_fb = gm12u320->fb_update.fb;
> 	...
> 	drm_framebuffer_put(old_fb);
> }
>
> Because drm_framebuffer_put() computes the address of &fb->base before passing
> it to drm_mode_object_put(), passing a NULL fb results in a non-NULL offset
> pointer. This bypasses any NULL checks in the core and causes a fatal page
> fault when dereferenced.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.