dma_fence_enable_sw_signaling acquires an extra reference on each chain
fence. The error unwind loop calls dma_fence_put only once per
chain/fence without first signaling the fence to trigger the callback
that releases the signaling reference. This prevents the chain fence kref
from reaching 0, permanently leaking the chain and its contained fence.
Cc: stable(a)vger.kernel.org
Fixes: dc2f7e67a28a ("dma-buf: Exercise dma-fence-chain under selftests")
Signed-off-by: WenTao Liang <vulab(a)iscas.ac.cn>
---
drivers/dma-buf/st-dma-fence-chain.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/dma-buf/st-dma-fence-chain.c b/drivers/dma-buf/st-dma-fence-chain.c
index 821023dd34df..7dc18e294387 100644
--- a/drivers/dma-buf/st-dma-fence-chain.c
+++ b/drivers/dma-buf/st-dma-fence-chain.c
@@ -152,7 +152,10 @@ static int fence_chains_init(struct fence_chains *fc, unsigned int count,
unwind:
for (i = 0; i < count; i++) {
- dma_fence_put(fc->fences[i]);
+ if (fc->fences[i]) {
+ dma_fence_signal(fc->fences[i]);
+ dma_fence_put(fc->fences[i]);
+ }
dma_fence_put(fc->chains[i]);
}
kvfree(fc->fences);
--
2.39.5 (Apple Git-154)
Buy real and fake passport online, Buy ID cards online, (WhatsApp : +49 1575 3756974) Buy driving license, Buy drivers license online, Buy green card, residence permit, IELT, work permit, citizenship, buy Canadian resident permits, apply for Canadian citizenship certificates, buy Canadian ID cards, buy novelty ID cards, buy authentic identity documents. https://buyrealcurrency.com/https://buyrealcurrency.com/https://buyrealcurrency.com/product/加拿大居留许可/https://rushmynewpassport.com/product/buy-canadian-resident-permits/
(WhatsApp : +49 1575 3756974)
WeChat ID : Scottbowers44
(Email: authenticnotes5(a)gmail.com)
https://counterfeitdocsforsale.com/ Buy fake US dollars (USD), buy fake Chinese yuan (CNY), buy fake RMB/RMB, buy fake Canadian dollars (CAD), buy fake Australian dollars (AUD) Buy fake British pounds (GBP), buy fake Euros (EUR), buy fake Hong Kong dollars ($HK). buy fake US dollars/Australian dollars/Canadian dollars/CNY/Euros/CNY, buy fake Euro banknotes, buy fake Australian dollars, buy fake Canadian dollars, buy fake US dollars, buy fake RMB online, buy fake RMB https://globaltraveldocs.com/https://rushmynewpassport.com/ We offer all types of visas, travel documents, and passports at the best prices and with exceptional quality. We handle passports for small countries, multinational passports, entry and exit assistance for Southeast Asia, passport activation, expedited naturalization, and second identity planning, giving you an alternative.
(WhatsApp : +49 1575 3756974)
Email: authenticnotes5(a)gmail.com
https://rushmynewpassport.com/ Buy passports online (Email: authenticnotes5(a)gmail.com) buy USA passports, buy Chinese passports, buy Hong Kong passports, buy Taiwan passports, buy diplomatic passports, Buy China travel documents People's Republic of China (PRC) https://rushmynewpassport.com/https://buyrealcurrency.com/product/buy-real-and-fake-passport/https://rushmynewpassport.com/buy-real-usa-passport-online/
(WhatsApp : +49 1575 3756974 )
On 6/26/26 14:28, WenTao Liang wrote:
> dma_fence_get_stub() acquires an extra reference on the global stub
> fence, but this reference is never released on any execution path. The
> stub fence is filtered out inside dma_fence_unwrap_merge (already
> signaled), so the extra reference is never consumed. Both success and
> error paths fail to call dma_fence_put on the stub.
>
> Cc: stable(a)vger.kernel.org
> Fixes: 245a4a7b531c ("dma-buf: generalize dma_fence unwrap & merging v3")
Just drop that, the stub fence is a global dummy and leaking reference to it is harmless.
But just in case somebody uses this code as blueprint for this own implementation we should probably clean it up.
> Signed-off-by: WenTao Liang <vulab(a)iscas.ac.cn>
Reviewed-by: Christian König <christian.koenig(a)amd.com>
> ---
> drivers/dma-buf/st-dma-fence-unwrap.c | 11 +++++++----
> 1 file changed, 7 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/dma-buf/st-dma-fence-unwrap.c b/drivers/dma-buf/st-dma-fence-unwrap.c
> index 72ca632e3981..b9ed85570211 100644
> --- a/drivers/dma-buf/st-dma-fence-unwrap.c
> +++ b/drivers/dma-buf/st-dma-fence-unwrap.c
> @@ -483,7 +483,7 @@ static int unwrap_merge_order(void *arg)
>
> static int unwrap_merge_complex(void *arg)
> {
> - struct dma_fence *fence, *f1, *f2, *f3, *f4, *f5;
> + struct dma_fence *fence, *f1, *f2, *f3, *f4, *f5, *stub;
> struct dma_fence_unwrap iter;
> int err = -ENOMEM;
>
> @@ -508,10 +508,11 @@ static int unwrap_merge_complex(void *arg)
> if (!f4)
> goto error_put_f3;
>
> + stub = dma_fence_get_stub();
> /* Signaled fences should be filtered, the two arrays merged. */
> - f5 = dma_fence_unwrap_merge(f3, f4, dma_fence_get_stub());
> + f5 = dma_fence_unwrap_merge(f3, f4, stub);
> if (!f5)
> - goto error_put_f4;
> + goto error_put_stub;
>
> err = 0;
> dma_fence_unwrap_for_each(fence, &iter, f5) {
> @@ -532,8 +533,10 @@ static int unwrap_merge_complex(void *arg)
> err = -EINVAL;
> }
>
> + dma_fence_put(stub);
> dma_fence_put(f5);
> -error_put_f4:
> +error_put_stub:
> + dma_fence_put(stub);
> dma_fence_put(f4);
> error_put_f3:
> dma_fence_put(f3);
The success path only releases a2 via dma_fence_put but does not release
a1, c1, or c2. The dma_fence_get calls at lines 440 and 445 were intended
to pass references to mock_chain, but mock_chain already acquires its own
references internally, making these extra gets surplus and permanently
leaked.
Cc: stable(a)vger.kernel.org
Fixes: b1cce631e61f ("dma-buf: add selftest for fence order after merge")
Signed-off-by: WenTao Liang <vulab(a)iscas.ac.cn>
---
drivers/dma-buf/st-dma-fence-unwrap.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/dma-buf/st-dma-fence-unwrap.c b/drivers/dma-buf/st-dma-fence-unwrap.c
index 9c74195f47fd..72ca632e3981 100644
--- a/drivers/dma-buf/st-dma-fence-unwrap.c
+++ b/drivers/dma-buf/st-dma-fence-unwrap.c
@@ -472,6 +472,8 @@ static int unwrap_merge_order(void *arg)
}
dma_fence_put(a2);
+ dma_fence_put(c2);
+ dma_fence_put(a1);
return err;
error_put_a1:
--
2.39.5 (Apple Git-154)
UDMABUF_CREATE_LIST copies an array whose element count comes from
userspace. The count is compared against list_limit, but list_limit is a
signed module parameter while the count is u32.
If the limit is raised too far or made negative, that comparison no
longer bounds the count to a range where sizeof(*list) * count fits in
the u32 temporary used for the copy length. A wrapped copy length lets
memdup_user() copy fewer entries than udmabuf_create() subsequently
walks, leading to out-of-bounds reads from the copied list.
Take a positive snapshot of the module limit and use memdup_array_user()
so the multiplication is checked before copying.
Signed-off-by: Yousef Alhouseen <alhouseenyousef(a)gmail.com>
---
drivers/dma-buf/udmabuf.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
index bced421c0..b4078ec84 100644
--- a/drivers/dma-buf/udmabuf.c
+++ b/drivers/dma-buf/udmabuf.c
@@ -469,14 +469,15 @@ static long udmabuf_ioctl_create_list(struct file *filp, unsigned long arg)
struct udmabuf_create_list head;
struct udmabuf_create_item *list;
int ret = -EINVAL;
- u32 lsize;
+ int limit;
if (copy_from_user(&head, (void __user *)arg, sizeof(head)))
return -EFAULT;
- if (head.count > list_limit)
+ limit = READ_ONCE(list_limit);
+ if (!head.count || limit <= 0 || head.count > limit)
return -EINVAL;
- lsize = sizeof(struct udmabuf_create_item) * head.count;
- list = memdup_user((void __user *)(arg + sizeof(head)), lsize);
+ list = memdup_array_user((void __user *)(arg + sizeof(head)),
+ head.count, sizeof(*list));
if (IS_ERR(list))
return PTR_ERR(list);
--
2.54.0
The commit mentioned in the fixes tag below introduced a mechanism
through which fence producers can fully decouple from fence consumers.
This, desirable, mechanism is based on the fence's signaled-bit as the
"decoupling point".
A sophisticated interaction between RCU and atomic instructions attempts
to ensure that fence consumers can still interact with fence producers
through the dma_fence_ops, callback pointers into the producer.
This is the desired behavior: to check for decoupling, the signaled-bit
is first checked. If it's not yet signaled, RCU ensures that the ops
pointer cannot yet be NULL.
Hereby, dma_fence_signal_timestamp_locked() first sets the signaled-bit,
and then sets the ops pointer to NULL. Readers first load the ops
pointer, and then check through the signaled-bit whether the pointer can
legally be accessed.
These set and load operations could occur out of order on weakly ordered
platforms. Hence, we need to enforce strict ordering all the time.
Add the appropriate memory barriers.
Cc: stable(a)vger.kernel.org
Fixes: f4cc3ab824d6 ("dma-buf: protected fence ops by RCU v8")
Signed-off-by: Philipp Stanner <phasta(a)kernel.org>
---
Tested with dmabuf and drm_sched unit tests.
Memory barriers are notoriously difficult, so I would appreciate if some
of the more experienced folks can check this. Notably, I am not sure
whether the smp_wmb() is necessary.
The documentation for test_and_set_bit() makes the mysterious statement
"This is an atomic fully-ordered operation (implied full memory
barrier)", but the kcsan_mb() seems to be some sort of debugging
barrier, and in any case the docu doesn't make it obvious to me whether
that "full barrier" comes before or after the bit setting takes place.
Moreover, in my opinion we should order dma_fence_is_signaled(), too –
but if we agree to merge Christian's new series [1] that need should
disappear.
[1] https://lore.kernel.org/dri-devel/20260624122917.2483-1-christian.koenig@am…
---
drivers/dma-buf/dma-fence.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
index c7ea1e75d38a..2e80b01499de 100644
--- a/drivers/dma-buf/dma-fence.c
+++ b/drivers/dma-buf/dma-fence.c
@@ -363,6 +363,18 @@ void dma_fence_signal_timestamp_locked(struct dma_fence *fence,
&fence->flags)))
return;
+ /*
+ * Fully order setting of the bit above with setting of the ops pointer
+ * to NULL below, so that all parties can use the signaled flag to
+ * detect that the fence decoupled from its ops in a safe manner.
+ *
+ * The counter parts of this barrier are in dma_fence_timeline_name()
+ * and dma_fence_driver_name(). All other future parties that rely on
+ * the signaled flag for valid access to the ops pointer will need a
+ * memory barrier.
+ */
+ smp_wmb();
+
trace_dma_fence_signaled(fence);
/*
@@ -1170,6 +1182,12 @@ const char __rcu *dma_fence_driver_name(struct dma_fence *fence)
/* RCU protection is required for safe access to returned string */
ops = rcu_dereference(fence->ops);
+ /*
+ * Fully order the dereference above with the flag check. Otherwise,
+ * ops could be dereferenced as a NULL pointer. The barrier's
+ * counterpart is in dma_fence_signal_timestamp_locked().
+ */
+ smp_rmb();
if (!dma_fence_test_signaled_flag(fence))
return (const char __rcu *)ops->get_driver_name(fence);
else
@@ -1203,6 +1221,12 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence)
/* RCU protection is required for safe access to returned string */
ops = rcu_dereference(fence->ops);
+ /*
+ * Fully order the dereference above with the flag check. Otherwise,
+ * ops could be dereferenced as a NULL pointer. The barrier's
+ * counterpart is in dma_fence_signal_timestamp_locked().
+ */
+ smp_rmb();
if (!dma_fence_test_signaled_flag(fence))
return (const char __rcu *)ops->get_driver_name(fence);
else
base-commit: cdeb2ccd993ed8647adbbda2c3b103aa717fd6f7
--
2.54.0
Most of this patch series has already been pushed upstream, this is just
the second half of the patch series that has not been pushed yet + some
additional changes which were required to implement changes requested by
the mailing list. This patch series is originally from Asahi, previously
posted by Daniel Almeida.
The previous version of the patch series can be found here:
https://patchwork.freedesktop.org/series/164580/
Branch with patches applied available here:
https://gitlab.freedesktop.org/lyudess/linux/-/commits/rust/gem-shmem
This patch series applies on top of drm-rust-next
Patch-series wide changes since V15:
* Fix some major rebasing errors I somehow didn't notice :(
* Drop the dependency on LazyInit, use the trick that Alice suggested
instead.
* Fix dependency ordering so that Tyr can get the vmap stuff first
without the other bits.
Patch-series wide changes since V16:
* Fix ordering one more time (SetOnce::reset() doesn't need to come
before adding vmap functions)
* Rebase against the latest DeviceContext changes from me that got
pushed.
Patch-series wide changes since V20:
* Lots of Sashiko fixes, excluding the comments that I couldn't prove
weren't just bogus.
Lyude Paul (4):
rust: drm: gem: shmem: Add DmaResvGuard helper
rust: drm: gem: shmem: Add vmap functions
rust: faux: Allow retrieving a bound Device
rust: drm: gem: Introduce shmem::Object::sg_table()
rust/kernel/drm/gem/shmem.rs | 547 ++++++++++++++++++++++++++++++++++-
rust/kernel/faux.rs | 18 +-
2 files changed, 549 insertions(+), 16 deletions(-)
base-commit: 550dc7536644db2d67c6f8cf525bba682fba08d9
--
2.54.0
dma_fence_timeline_name() incorrectly invokes ops->get_driver_name()
instead of ops->get_timeline_name(), so every caller receives the
driver name where the timeline name was expected.
This is a copy-paste regression that has resurfaced twice. It was
originally introduced by commit 62918542b7bf ("dma-fence: Fix sparse
warnings due __rcu annotations") when adding the __rcu casts, fixed
by commit 033559473dd3 ("dma-fence: Fix safe access wrapper to call
timeline name method"), and then accidentally reintroduced by commit
e58b4dea9054 ("dma-buf/dma-fence: Add dma_fence_test_signaled_flag()")
when both wrappers were refactored to use the new helper.
Signed-off-by: Baineng Shou <shoubaineng(a)gmail.com>
---
drivers/dma-buf/dma-fence.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
index b3bfa6943a8e..5292d714419b 100644
--- a/drivers/dma-buf/dma-fence.c
+++ b/drivers/dma-buf/dma-fence.c
@@ -1202,7 +1202,7 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence)
/* RCU protection is required for safe access to returned string */
ops = rcu_dereference(fence->ops);
if (!dma_fence_test_signaled_flag(fence))
- return (const char __rcu *)ops->get_driver_name(fence);
+ return (const char __rcu *)ops->get_timeline_name(fence);
else
return (const char __rcu *)"signaled-timeline";
}
--
2.34.1
From: Bryam Vargas <hexlabsecurity(a)proton.me>
begin_cpu_udmabuf() builds and caches ubuf->sg with an unserialised
check-then-set, and end_cpu_udmabuf() reads the same field unlocked. The
core invokes both cpu-access hooks without holding the reservation lock and
DMA_BUF_IOCTL_SYNC is unlocked, so concurrent SYNC ioctls on a shared
udmabuf fd race on ubuf->sg: two begins can both observe NULL and both call
get_sg_table(), and the later store orphans the earlier table and its DMA
mapping, which release_udmabuf() never frees. Each won race permanently
leaks an sg_table and an unbalanced DMA mapping.
Serialize both hooks under the buffer's reservation lock, as panfrost and
panthor do. Take it interruptibly: the lock can be held across a wait for
hardware to finish, so an uninterruptible acquire would park a SYNC
ioctl in TASK_UNINTERRUPTIBLE. dma_buf_begin/end_cpu_access() already
annotate might_lock() on that lock, so taking it here matches the
documented contract. Single-threaded callers are unaffected.
Fixes: 284562e1f348 ("udmabuf: implement begin_cpu_access/end_cpu_access hooks")
Cc: stable(a)vger.kernel.org
Signed-off-by: Bryam Vargas <hexlabsecurity(a)proton.me>
---
v2: Take the reservation lock interruptibly (dma_resv_lock_interruptible())
in both hooks instead of the uninterruptible dma_resv_lock(), and return
the error; the lock can be held across a wait for hardware to finish, so
an uninterruptible acquire could park a SYNC ioctl in
TASK_UNINTERRUPTIBLE. With a NULL ww_acquire_ctx the call returns only 0
or -EINTR, so a single error check is enough. (Christian König)
v1: https://lore.kernel.org/all/20260625-b4-disp-67d1f3db-v1-1-a47fb9edab9e@pro…
Same leak-with-dangling-pointer class as CVE-2024-56712 (export_udmabuf()
error path) -- a distinct site the 2024 fix does not cover.
udmabuf is the only exporter that lazily builds its sg_table cache inside the
cpu-access hook without serialising the check-then-set. The exporters that do
comparable in-hook cache work all take a lock first: panfrost and panthor
dma_resv_lock() (both hooks), omapdrm omap_obj->lock around its lazy page-get,
the dma-heaps buffer->lock, and the TTM/GEM exporters (amdgpu, i915, xe) their
object's reservation lock. tegra and videobuf2 take no lock here because they
only sync an sg_table built earlier, so there is nothing to serialise.
Confirmed with an out-of-tree A/B exercising the begin/begin race: this driver
built as a module with get_sg_table()/put_sg_table() counting allocations
against frees, driven by a userspace racer that creates 3000 udmabufs and fires
DMA_BUF_IOCTL_SYNC(SYNC_START) from N threads on each shared fd. The lock
serialises the check-then-set identically whether it is taken interruptibly or
not; the run below used the reservation lock:
arm leaked sg_tables (of 3000 buffers)
vulnerable, 4 threads 4761
control, 1 thread 0
patched (resv lock), 4 threads 0
One sg_table and its DMA mapping leak per won race; the single-thread control
does not leak, isolating the race; with the lock the lazy-init runs once per
buffer (3000 allocations, zero leaked). end_cpu_udmabuf() is locked for the
same field too: an unlocked end could otherwise observe the transient IS_ERR
store begin makes before resetting ubuf->sg to NULL, and dereference it. In a
tighter 5000-iteration loop the unpatched leak runs around 15-20 MB/s of slab.
---
drivers/dma-buf/udmabuf.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
index bced421c0d65..d6a137f0de1f 100644
--- a/drivers/dma-buf/udmabuf.c
+++ b/drivers/dma-buf/udmabuf.c
@@ -226,6 +226,10 @@ static int begin_cpu_udmabuf(struct dma_buf *buf,
struct device *dev = ubuf->device->this_device;
int ret = 0;
+ ret = dma_resv_lock_interruptible(buf->resv, NULL);
+ if (ret)
+ return ret;
+
if (!ubuf->sg) {
ubuf->sg = get_sg_table(dev, buf, direction);
if (IS_ERR(ubuf->sg)) {
@@ -238,6 +242,8 @@ static int begin_cpu_udmabuf(struct dma_buf *buf,
dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
}
+ dma_resv_unlock(buf->resv);
+
return ret;
}
@@ -246,12 +252,20 @@ static int end_cpu_udmabuf(struct dma_buf *buf,
{
struct udmabuf *ubuf = buf->priv;
struct device *dev = ubuf->device->this_device;
+ int ret = 0;
+
+ ret = dma_resv_lock_interruptible(buf->resv, NULL);
+ if (ret)
+ return ret;
if (!ubuf->sg)
- return -EINVAL;
+ ret = -EINVAL;
+ else
+ dma_sync_sgtable_for_device(dev, ubuf->sg, direction);
- dma_sync_sgtable_for_device(dev, ubuf->sg, direction);
- return 0;
+ dma_resv_unlock(buf->resv);
+
+ return ret;
}
static const struct dma_buf_ops udmabuf_ops = {
---
base-commit: 7eed1fb17959e721031555e5b5654083fe6a7d02
change-id: 20260625-b4-disp-a9216ef0-d068373aff05
Best regards,
--
Bryam Vargas <hexlabsecurity(a)proton.me>