Re: [PATCH 1/3] drm/nouveau: destroy the fence event before cancelling its work

[email protected]
Newsgroups org.freedesktop.lists.dri-devel
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] Moving `nvif_event_dtor()` before `nouveau_fence_context_kill()` introduces an unprotected Time-of-Check to Time-of-Use (TOCTOU) race condition that results in a NULL pointer dereference in `nvif_object_ioctl`.
--

--- Patch [1]: [PATCH 1/3] drm/nouveau: destroy the fence event before cancelling its work ---
commit 248f9b5f2479dc65d3572babf739eb3883d7b9f7
Author: Marek Czernohous <[email protected]>

drm/nouveau: destroy the fence event before cancelling its work

This commit reorders the teardown sequence in nouveau_fence_context_del() by
moving nvif_event_dtor() before cancel_work_sync() and
nouveau_fence_context_kill(). It aims to prevent a newly arriving interrupt
from re-arming the work that was just cancelled.

Link: https://sashiko.dev/#/patchset/[email protected]?part=1

> diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
> index edbe9e08ba0fd..4a3698dc2cd14 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_fence.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
> @@ -96,9 +96,9 @@ nouveau_fence_context_kill(struct nouveau_fence_chan *fctx, int error)
>  void
>  nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
>  {
> +	nvif_event_dtor(&fctx->event);
>  	cancel_work_sync(&fctx->uevent_work);
>  	nouveau_fence_context_kill(fctx, 0);

[Severity: Critical]
Does moving nvif_event_dtor() before nouveau_fence_context_kill() introduce a
Time-of-Check to Time-of-Use (TOCTOU) race condition?

Since nouveau_fence_context_kill() hasn't run yet, the fences are still
active and unsignaled. If Thread A is polling a fence and calls
nouveau_fence_enable_signaling() which reaches nvif_event_allow(), it might
evaluate nvif_event_constructed() right before Thread B destroys the event:

Thread A:
drivers/gpu/drm/nouveau/nvif/event.c:nvif_event_allow() {
	if (nvif_event_constructed(event)) {

Thread B concurrently cleans up the channel:
drivers/gpu/drm/nouveau/nouveau_fence.c:nouveau_fence_context_del() {
	nvif_event_dtor(&fctx->event);
	...
drivers/gpu/drm/nouveau/nvif/object.c:nvif_object_dtor() {
	nvif_object_ioctl(object, &args, sizeof(args), NULL);
	object->client = NULL;

Thread A then continues and calls into nvif_mthd():
drivers/gpu/drm/nouveau/nvif/event.c:nvif_event_allow() {
		int ret = nvif_mthd(&event->object, NVIF_EVENT_V0_ALLOW, ...);
		...
drivers/gpu/drm/nouveau/nvif/object.c:nvif_object_ioctl() {
	struct nvif_client *client = object->client;
	/* ... */
	return client->driver->ioctl(client->object.priv, data, size, hack);

Could Thread A read the stale object->client as NULL and fatally dereference
it here, since both polling on an exported sync_file fd and channel cleanup
are accessible to unprivileged userspace?

> -	nvif_event_dtor(&fctx->event);
>  	fctx->dead = 1;
>  
>  	/*

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
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.