[PATCH 2/2] dma-buf/dma-fence: Order setting of the signaled-bit properly
From: Philipp Stanner
Date: Wed Oct 21 2026 - 05:57:19 EST
dma_fence_is_signaled() can return true *while* the fence is still
signaling. This is fragile and has been involved in at least two bugs:
commit c8a5d5ea3ba6 ("nouveau: fix client work fence deletion race")
commit 7c703a7d3f2b ("drm/amdgpu: Fix one use-after-free of VM")
The signaled-bit is set under lock-protection but read without it.
Adding the lock has been rejected on the grounds of the lock-acquisition
shooting down cache lines of parties that merely want to check whether
the fence is already signaled.
Make sure the bit can only become visible as 1 once the callbacks have
ran. To achieve this, read the bit with test_bit_acquire() and set it
with set_bit_release().
Signed-off-by: Philipp Stanner <phasta@xxxxxxxxxx>
---
drivers/dma-buf/dma-fence.c | 11 +++++++++--
include/linux/dma-fence.h | 6 +++++-
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
index bd58688b81a7..07d499fa35ac 100644
--- a/drivers/dma-buf/dma-fence.c
+++ b/drivers/dma-buf/dma-fence.c
@@ -359,8 +359,7 @@ void dma_fence_signal_timestamp_locked(struct dma_fence *fence,
dma_fence_assert_held(fence);
- if (unlikely(test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
- &fence->flags)))
+ if (unlikely(dma_fence_test_signaled_flag(fence)))
return;
trace_dma_fence_signaled(fence);
@@ -384,6 +383,14 @@ void dma_fence_signal_timestamp_locked(struct dma_fence *fence,
INIT_LIST_HEAD(&cur->node);
cur->func(fence, cur);
}
+
+ /*
+ * This orders the signaled-state with the test_bit_acquire() in
+ * dma_fence_test_signaled_flag(). This ensures that functions like
+ * dma_fence_is_signaled() can only ever return true once signaling is
+ * completed (i.e., after the callbacks above ran).
+ */
+ set_bit_release(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags);
}
EXPORT_SYMBOL(dma_fence_signal_timestamp_locked);
diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h
index dd07d128adc3..61522027af41 100644
--- a/include/linux/dma-fence.h
+++ b/include/linux/dma-fence.h
@@ -496,7 +496,11 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence);
static inline bool
dma_fence_test_signaled_flag(struct dma_fence *fence)
{
- return test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags);
+ /*
+ * The counterpart of this ordered operation can be found in
+ * dma_fence_signal_timestamp_locked().
+ */
+ return test_bit_acquire(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags);
}
/**
--
2.56.0