dma-fence: Fix potential tracepoint null pointer dereferences

Trace_dma_fence_signaled, trace_dma_fence_wait_end and
trace_dma_fence_destroy can all currently dereference a null fence->ops
pointer after it has been reset on fence signalling.

Lets use the safe string getters for most tracepoints to avoid this class
of a problem, while for the signal tracepoint we move it to before ops are
cleared to avoid losing the driver and timeline name information. Apart
from moving it we also need to add a new tracepoint class to bypass the
safe name getters since the signaled bit is already set.

For dma_fence_init we also need to use the new tracepoint class since the
rcu read lock is not held there, and we can do the same for the enable
signaling since there we are certain the fence cannot be signaled while
we are holding the lock and have even validated the fence->ops.

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
Fixes: 541c8f2468 ("dma-buf: detach fence ops on signal v3")
Cc: Christian König <christian.koenig@amd.com>
Cc: Philipp Stanner <phasta@kernel.org>
Cc: Boris Brezillon <boris.brezillon@collabora.com>
Cc: linux-media@vger.kernel.org
Cc: linaro-mm-sig@lists.linaro.org
Reviewed-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
Link: https://lore.kernel.org/r/20260415083207.40513-2-tvrtko.ursulin@igalia.com
This commit is contained in:
Tvrtko Ursulin 2026-04-15 09:32:07 +01:00 committed by Tvrtko Ursulin
parent c83ad8ea6b
commit e94b9f0154
2 changed files with 36 additions and 7 deletions

View File

@ -363,6 +363,8 @@ void dma_fence_signal_timestamp_locked(struct dma_fence *fence,
&fence->flags)))
return;
trace_dma_fence_signaled(fence);
/*
* When neither a release nor a wait operation is specified set the ops
* pointer to NULL to allow the fence structure to become independent
@ -377,7 +379,6 @@ void dma_fence_signal_timestamp_locked(struct dma_fence *fence,
fence->timestamp = timestamp;
set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags);
trace_dma_fence_signaled(fence);
list_for_each_entry_safe(cur, tmp, &cb_list, node) {
INIT_LIST_HEAD(&cur->node);

View File

@ -9,12 +9,40 @@
struct dma_fence;
DECLARE_EVENT_CLASS(dma_fence,
TP_PROTO(struct dma_fence *fence),
TP_ARGS(fence),
TP_STRUCT__entry(
__string(driver, dma_fence_driver_name(fence))
__string(timeline, dma_fence_timeline_name(fence))
__field(unsigned int, context)
__field(unsigned int, seqno)
),
TP_fast_assign(
__assign_str(driver);
__assign_str(timeline);
__entry->context = fence->context;
__entry->seqno = fence->seqno;
),
TP_printk("driver=%s timeline=%s context=%u seqno=%u",
__get_str(driver), __get_str(timeline), __entry->context,
__entry->seqno)
);
/*
* Safe only for call sites which are guaranteed to not race with fence
* signaling,holding the fence->lock and having checked for not signaled, or the
* signaling path itself.
* signaling, holding the fence->lock and having checked for not signaled, or
* the signaling path itself.
*
* TODO: Remove the need for this event class when drivers switch to independent
* fences.
*/
DECLARE_EVENT_CLASS(dma_fence,
DECLARE_EVENT_CLASS(dma_fence_ops,
TP_PROTO(struct dma_fence *fence),
@ -46,7 +74,7 @@ DEFINE_EVENT(dma_fence, dma_fence_emit,
TP_ARGS(fence)
);
DEFINE_EVENT(dma_fence, dma_fence_init,
DEFINE_EVENT(dma_fence_ops, dma_fence_init,
TP_PROTO(struct dma_fence *fence),
@ -60,14 +88,14 @@ DEFINE_EVENT(dma_fence, dma_fence_destroy,
TP_ARGS(fence)
);
DEFINE_EVENT(dma_fence, dma_fence_enable_signal,
DEFINE_EVENT(dma_fence_ops, dma_fence_enable_signal,
TP_PROTO(struct dma_fence *fence),
TP_ARGS(fence)
);
DEFINE_EVENT(dma_fence, dma_fence_signaled,
DEFINE_EVENT(dma_fence_ops, dma_fence_signaled,
TP_PROTO(struct dma_fence *fence),