Thank you for your contribution! Sashiko AI review found 2 potential issue(s) 
to consider:
- [High] drm_user_fence_cancel() causes a Use-After-Free if the fence has 
signaled, because it requires the caller to pass a fence pointer without 
requiring the caller to hold a separate reference.
- [High] Contradictory documentation in drm_user_fence_cancel() instructs the 
caller to put references that the function already puts internally, leading to 
double-free bugs.
--

commit dfafc94b5c524854a64ed37e415adef39795ad80
Author: Srinivasan Shanmugam <[email protected]>

drm: Add common drm_user_fence helper

This commit introduces a common DRM user fence helper providing a pattern
to access userspace memory from a kthread context when a GPU fence signals.
It implements a kref-managed, dma-fence-callback-to-workqueue pattern that
borrows the process MM.

> diff --git a/drivers/gpu/drm/drm_user_fence.c 
> b/drivers/gpu/drm/drm_user_fence.c
> new file mode 100644
> index 0000000000000..a3b14245163ae
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_user_fence.c

[ ... ]

> +/**
> + * drm_user_fence_cancel - Cancel a pending user fence callback
> + * @ufence: user fence
> + * @fence: the same dma-fence passed to drm_user_fence_add_callback()
> + *
> + * Attempts to remove the pending callback before driver context teardown.
> + * Must be called before the driver tears down its workqueue or ops.
> + *
> + * If the callback has already fired this is a no-op and returns false.
> + * If removal succeeds the caller must call drm_user_fence_put() to
> + * release the reference taken by drm_user_fence_add_callback(), and
> + * must also call dma_fence_put() to release the fence reference that
> + * was transferred at add_callback time.

[Severity: High]
The kernel-doc instructions here state that the caller must call
drm_user_fence_put() and dma_fence_put() upon success.

However, looking at the implementation of drm_user_fence_cancel() below,
it internally executes dma_fence_put() and drm_user_fence_put() before
returning true.

If a driver strictly follows this documentation, will it lead to double-free
bugs when they try to drop the references again?

> + *
> + * Return: true if callback was removed, false if it had already fired.
> + */
> +bool drm_user_fence_cancel(struct drm_user_fence *ufence,
> +                        struct dma_fence *fence)
> +{
> +     if (dma_fence_remove_callback(fence, &ufence->cb)) {

[Severity: High]
Can this result in a use-after-free if the fence has already signaled?

According to the drm_user_fence_add_callback() design, the caller transfers
its fence reference to the callback. If the fence signals asynchronously, the
callback fires and internally drops that reference via dma_fence_put(). If
that was the last reference, the fence object is freed.

When the driver later initiates teardown and calls drm_user_fence_cancel(),
it passes this potentially dangling fence pointer to
dma_fence_remove_callback(), which dereferences it to acquire a spinlock.

Is there a way for the caller to safely cancel the callback without taking
and holding an additional reference to the fence?

> +             dma_fence_put(fence);
> +             drm_user_fence_put(ufence);
> +             return true;
> +     }
> +     return false;
> +}
> +EXPORT_SYMBOL_GPL(drm_user_fence_cancel);

-- 
Sashiko AI review ยท 
https://sashiko.dev/#/patchset/[email protected]?part=1

Reply via email to