On Wed, 2026-08-19 at 14:33 +0200, Philipp Stanner wrote:

[…]

> 
> Regardless, looking at the code again, I would say that this might be a
> race, but I don't know enough about QXL to say for sure.
> 
> dma_fence_init() is (of course) not ordered:
> 
> 
> static void
> __dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops,
>                spinlock_t *lock, u64 context, u64 seqno, unsigned long flags)
> {
>       BUG_ON(!ops || !ops->get_driver_name || !ops->get_timeline_name);
> 
>       kref_init(&fence->refcount);
>       /*
>        * While it is counter intuitive to protect a constant function pointer
>        * table by RCU it allows modules to wait for an RCU grace period
>        * before they unload, to make sure that nobody is executing their
>        * functions any more.
>        */
>       RCU_INIT_POINTER(fence->ops, ops);
>       INIT_LIST_HEAD(&fence->cb_list);
>       fence->context = context;
>       fence->seqno = seqno;
>       fence->flags = flags | BIT(DMA_FENCE_FLAG_INITIALIZED_BIT);
> 
> (Should this maybe be set_bit() btw?)
> 
> 
> The fact that QXL could run into qxl_release_free() with an
> uninitialized fence hints at the fact that this might race, so
> DMA_FENCE_FLAG_INITIALIZED_BIT could be set / read before kref_init()
> ran.
> 
> 
> Maybe one way to verify / debug that would be to move
> spin_unlock(&qdev->release_idr_lock) downwards so it also guards
> dma_fence_was_initialized(), and also lock the initialization of the
> fence (in qxl_release_fence_buffer_objects() ?) with said lock.
> 
> If that's possible. Just brainstorming a bit for ways how to debug.
> 
> QXL does a few tricky things with the release->base.ops pointer.
> qxl_release_alloc() sets it to NULL, and only
> qxl_release_fence_buffer_objects() then actually sets it. So this could
> be the race? Setting of the ops pointer got replaced by setting of the
> fence-flag.
> 
> 
> P.

Could you test something like this? (not even compile-tested, just an idea)


diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
index 87797bea91cb..df1aa48b2809 100644
--- a/drivers/dma-buf/dma-fence.c
+++ b/drivers/dma-buf/dma-fence.c
@@ -1075,7 +1075,6 @@ __dma_fence_init(struct dma_fence *fence, const struct 
dma_fence_ops *ops,
        INIT_LIST_HEAD(&fence->cb_list);
        fence->context = context;
        fence->seqno = seqno;
-       fence->flags = flags | BIT(DMA_FENCE_FLAG_INITIALIZED_BIT);
        if (lock) {
                fence->extern_lock = lock;
        } else {
@@ -1084,6 +1083,8 @@ __dma_fence_init(struct dma_fence *fence, const struct 
dma_fence_ops *ops,
        }
        fence->error = 0;
 
+       smp_mb();
+       fence->flags = flags | BIT(DMA_FENCE_FLAG_INITIALIZED_BIT);
        trace_dma_fence_init(fence);
 }
 
diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h
index b52ab692b22e..40ffdcafaac1 100644
--- a/include/linux/dma-fence.h
+++ b/include/linux/dma-fence.h
@@ -292,7 +292,12 @@ void dma_fence_describe(struct dma_fence *fence, struct 
seq_file *seq);
  */
 static inline bool dma_fence_was_initialized(struct dma_fence *fence)
 {
-       return fence && test_bit(DMA_FENCE_FLAG_INITIALIZED_BIT, &fence->flags);
+       bool init;
+
+       init = fence && test_bit(DMA_FENCE_FLAG_INITIALIZED_BIT, &fence->flags);
+       smp_mb();
+
+       return init;
 }
 
 /**

Reply via email to