* [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
@ 2026-06-01 10:15 Nitin Gote
2026-06-01 10:46 ` Matthew Auld
0 siblings, 1 reply; 15+ messages in thread
From: Nitin Gote @ 2026-06-01 10:15 UTC (permalink / raw)
To: intel-xe
Cc: Nitin Gote, stable, Thomas Hellstrom, Matthew Brost, Matthew Auld
xe_dma_buf_create_obj() creates the importer BO with obj->resv
pointing at the exporter's dma_buf->resv. When dma_buf_dynamic_attach()
fails, no dma_buf reference is held so the exporter can be freed
immediately. Since ttm_bo_release() now always defers cleanup for
ttm_bo_type_sg BOs to the TTM workqueue, the worker later calls
dma_resv_lock() on the already-freed exporter resv, causing a UAF.
Reset obj->resv to the BO's private _resv before calling xe_bo_put()
in the error path. The BO is not yet published (attach failed) and
carries no fences, so the switch is safe.
Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG (QEMU):
Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6b9c
Workqueue: ttm ttm_bo_delayed_delete [ttm]
RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
Call Trace:
<TASK>
? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
ww_mutex_lock+0x3c/0xb0
ttm_bo_delayed_delete+0x41/0xc0 [ttm]
process_one_work+0x239/0x740
worker_thread+0x200/0x3f0
kthread+0x10d/0x150
ret_from_fork+0x3bd/0x470
ret_from_fork_asm+0x1a/0x30
</TASK>
Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed cleanup path for imported bos")
Cc: stable@vger.kernel.org # v6.8+
Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Matthew Auld <matthew.auld@intel.com>
Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
---
drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
index 8a920e58245c..6d944bd4065c 100644
--- a/drivers/gpu/drm/xe/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/xe_dma_buf.c
@@ -384,6 +384,14 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, obj);
if (IS_ERR(attach)) {
+ /*
+ * The BO was created with resv = dma_buf->resv (exporter's
+ * resv). Since attach failed, no dma_buf reference is held and
+ * the exporter may be freed before TTM's delayed_delete worker
+ * runs. Switch to the BO's own resv to prevent a UAF when
+ * ttm_bo_delayed_delete() tries to lock the stale pointer.
+ */
+ obj->resv = &obj->_resv;
xe_bo_put(gem_to_xe_bo(obj));
return ERR_CAST(attach);
}
--
2.50.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-01 10:15 [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure Nitin Gote
@ 2026-06-01 10:46 ` Matthew Auld
2026-06-01 11:39 ` Christian König
0 siblings, 1 reply; 15+ messages in thread
From: Matthew Auld @ 2026-06-01 10:46 UTC (permalink / raw)
To: Nitin Gote, intel-xe, christian König, Christian König
Cc: stable, Thomas Hellstrom, Matthew Brost
On 01/06/2026 11:15, Nitin Gote wrote:
> xe_dma_buf_create_obj() creates the importer BO with obj->resv
> pointing at the exporter's dma_buf->resv. When dma_buf_dynamic_attach()
> fails, no dma_buf reference is held so the exporter can be freed
> immediately. Since ttm_bo_release() now always defers cleanup for
> ttm_bo_type_sg BOs to the TTM workqueue, the worker later calls
> dma_resv_lock() on the already-freed exporter resv, causing a UAF.
>
> Reset obj->resv to the BO's private _resv before calling xe_bo_put()
> in the error path. The BO is not yet published (attach failed) and
> carries no fences, so the switch is safe.
>
> Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG (QEMU):
>
> Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6b9c
> Workqueue: ttm ttm_bo_delayed_delete [ttm]
> RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
> Call Trace:
> <TASK>
> ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
> ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> ww_mutex_lock+0x3c/0xb0
> ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> process_one_work+0x239/0x740
> worker_thread+0x200/0x3f0
> kthread+0x10d/0x150
> ret_from_fork+0x3bd/0x470
> ret_from_fork_asm+0x1a/0x30
> </TASK>
>
> Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
> Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed cleanup path for imported bos")
> Cc: stable@vger.kernel.org # v6.8+
> Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
> Cc: Matthew Brost <matthew.brost@intel.com>
> Cc: Matthew Auld <matthew.auld@intel.com>
> Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
> ---
> drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
> index 8a920e58245c..6d944bd4065c 100644
> --- a/drivers/gpu/drm/xe/xe_dma_buf.c
> +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
> @@ -384,6 +384,14 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
>
> attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, obj);
> if (IS_ERR(attach)) {
> + /*
> + * The BO was created with resv = dma_buf->resv (exporter's
> + * resv). Since attach failed, no dma_buf reference is held and
> + * the exporter may be freed before TTM's delayed_delete worker
> + * runs. Switch to the BO's own resv to prevent a UAF when
> + * ttm_bo_delayed_delete() tries to lock the stale pointer.
> + */
> + obj->resv = &obj->_resv;
+Christian, does amdgpu not have the type of same issue here? Also any
thoughts here?
> xe_bo_put(gem_to_xe_bo(obj));
> return ERR_CAST(attach);
> }
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-01 10:46 ` Matthew Auld
@ 2026-06-01 11:39 ` Christian König
2026-06-01 12:01 ` Matthew Auld
0 siblings, 1 reply; 15+ messages in thread
From: Christian König @ 2026-06-01 11:39 UTC (permalink / raw)
To: Matthew Auld, Nitin Gote, intel-xe, Christian König
Cc: stable, Thomas Hellstrom, Matthew Brost, Prosyak, Vitaly
On 6/1/26 12:46, Matthew Auld wrote:
> On 01/06/2026 11:15, Nitin Gote wrote:
>> xe_dma_buf_create_obj() creates the importer BO with obj->resv
>> pointing at the exporter's dma_buf->resv. When dma_buf_dynamic_attach()
>> fails, no dma_buf reference is held so the exporter can be freed
>> immediately. Since ttm_bo_release() now always defers cleanup for
>> ttm_bo_type_sg BOs to the TTM workqueue, the worker later calls
>> dma_resv_lock() on the already-freed exporter resv, causing a UAF.
>>
>> Reset obj->resv to the BO's private _resv before calling xe_bo_put()
>> in the error path. The BO is not yet published (attach failed) and
>> carries no fences, so the switch is safe.
>>
>> Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG (QEMU):
>>
>> Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6b9c
>> Workqueue: ttm ttm_bo_delayed_delete [ttm]
>> RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
>> Call Trace:
>> <TASK>
>> ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
>> ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
>> ww_mutex_lock+0x3c/0xb0
>> ttm_bo_delayed_delete+0x41/0xc0 [ttm]
>> process_one_work+0x239/0x740
>> worker_thread+0x200/0x3f0
>> kthread+0x10d/0x150
>> ret_from_fork+0x3bd/0x470
>> ret_from_fork_asm+0x1a/0x30
>> </TASK>
>>
>> Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
>> Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed cleanup path for imported bos")
>> Cc: stable@vger.kernel.org # v6.8+
>> Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
>> Cc: Matthew Brost <matthew.brost@intel.com>
>> Cc: Matthew Auld <matthew.auld@intel.com>
>> Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
>> ---
>> drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
>> 1 file changed, 8 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
>> index 8a920e58245c..6d944bd4065c 100644
>> --- a/drivers/gpu/drm/xe/xe_dma_buf.c
>> +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
>> @@ -384,6 +384,14 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
>> attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, obj);
>> if (IS_ERR(attach)) {
>> + /*
>> + * The BO was created with resv = dma_buf->resv (exporter's
>> + * resv). Since attach failed, no dma_buf reference is held and
>> + * the exporter may be freed before TTM's delayed_delete worker
>> + * runs. Switch to the BO's own resv to prevent a UAF when
>> + * ttm_bo_delayed_delete() tries to lock the stale pointer.
>> + */
>> + obj->resv = &obj->_resv;
>
> +Christian, does amdgpu not have the type of same issue here? Also any thoughts here?
Oh, good catch. Yeah I think we have the same problem on amdgpu as well.
How the heck did you found that? Do we have a dummy driver (VGEM?) which could be made to always fail attachment for a test case?
@Vitaly can you take a look and try to come up with a test case for that? Thanks in advance.
Thanks for the notice,
Christian.
>
>> xe_bo_put(gem_to_xe_bo(obj));
>> return ERR_CAST(attach);
>> }
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-01 11:39 ` Christian König
@ 2026-06-01 12:01 ` Matthew Auld
2026-06-01 12:17 ` Christian König
0 siblings, 1 reply; 15+ messages in thread
From: Matthew Auld @ 2026-06-01 12:01 UTC (permalink / raw)
To: Christian König, Nitin Gote, intel-xe, Christian König
Cc: stable, Thomas Hellstrom, Matthew Brost, Prosyak, Vitaly
On 01/06/2026 12:39, Christian König wrote:
>
>
> On 6/1/26 12:46, Matthew Auld wrote:
>> On 01/06/2026 11:15, Nitin Gote wrote:
>>> xe_dma_buf_create_obj() creates the importer BO with obj->resv
>>> pointing at the exporter's dma_buf->resv. When dma_buf_dynamic_attach()
>>> fails, no dma_buf reference is held so the exporter can be freed
>>> immediately. Since ttm_bo_release() now always defers cleanup for
>>> ttm_bo_type_sg BOs to the TTM workqueue, the worker later calls
>>> dma_resv_lock() on the already-freed exporter resv, causing a UAF.
>>>
>>> Reset obj->resv to the BO's private _resv before calling xe_bo_put()
>>> in the error path. The BO is not yet published (attach failed) and
>>> carries no fences, so the switch is safe.
>>>
>>> Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG (QEMU):
>>>
>>> Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6b9c
>>> Workqueue: ttm ttm_bo_delayed_delete [ttm]
>>> RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
>>> Call Trace:
>>> <TASK>
>>> ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
>>> ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
>>> ww_mutex_lock+0x3c/0xb0
>>> ttm_bo_delayed_delete+0x41/0xc0 [ttm]
>>> process_one_work+0x239/0x740
>>> worker_thread+0x200/0x3f0
>>> kthread+0x10d/0x150
>>> ret_from_fork+0x3bd/0x470
>>> ret_from_fork_asm+0x1a/0x30
>>> </TASK>
>>>
>>> Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
>>> Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed cleanup path for imported bos")
>>> Cc: stable@vger.kernel.org # v6.8+
>>> Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
>>> Cc: Matthew Brost <matthew.brost@intel.com>
>>> Cc: Matthew Auld <matthew.auld@intel.com>
>>> Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
>>> ---
>>> drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
>>> 1 file changed, 8 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
>>> index 8a920e58245c..6d944bd4065c 100644
>>> --- a/drivers/gpu/drm/xe/xe_dma_buf.c
>>> +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
>>> @@ -384,6 +384,14 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
>>> attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, obj);
>>> if (IS_ERR(attach)) {
>>> + /*
>>> + * The BO was created with resv = dma_buf->resv (exporter's
>>> + * resv). Since attach failed, no dma_buf reference is held and
>>> + * the exporter may be freed before TTM's delayed_delete worker
>>> + * runs. Switch to the BO's own resv to prevent a UAF when
>>> + * ttm_bo_delayed_delete() tries to lock the stale pointer.
>>> + */
>>> + obj->resv = &obj->_resv;
>>
>> +Christian, does amdgpu not have the type of same issue here? Also any thoughts here?
>
> Oh, good catch. Yeah I think we have the same problem on amdgpu as well.
Maybe dumb question, but why does the ttm_bo_individualize_resv() skip
the final switch of the resv for type_sg? It goes through the trouble of
copying the fences across?
If we do need to handle this here, do we also need to grab the lru lock,
like we do in ttm_bo_individualize_resv() when doing the swap?
Ideally xe and amdgpu can just have identical solutions here.
>
> How the heck did you found that? Do we have a dummy driver (VGEM?) which could be made to always fail attachment for a test case?
>
> @Vitaly can you take a look and try to come up with a test case for that? Thanks in advance.
>
> Thanks for the notice,
> Christian.
>
>>
>>> xe_bo_put(gem_to_xe_bo(obj));
>>> return ERR_CAST(attach);
>>> }
>>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-01 12:01 ` Matthew Auld
@ 2026-06-01 12:17 ` Christian König
2026-06-01 15:27 ` Gote, Nitin R
0 siblings, 1 reply; 15+ messages in thread
From: Christian König @ 2026-06-01 12:17 UTC (permalink / raw)
To: Matthew Auld, Nitin Gote, intel-xe, Christian König
Cc: stable, Thomas Hellstrom, Matthew Brost, Prosyak, Vitaly
On 6/1/26 14:01, Matthew Auld wrote:
> On 01/06/2026 12:39, Christian König wrote:
>>
>>
>> On 6/1/26 12:46, Matthew Auld wrote:
>>> On 01/06/2026 11:15, Nitin Gote wrote:
>>>> xe_dma_buf_create_obj() creates the importer BO with obj->resv
>>>> pointing at the exporter's dma_buf->resv. When dma_buf_dynamic_attach()
>>>> fails, no dma_buf reference is held so the exporter can be freed
>>>> immediately. Since ttm_bo_release() now always defers cleanup for
>>>> ttm_bo_type_sg BOs to the TTM workqueue, the worker later calls
>>>> dma_resv_lock() on the already-freed exporter resv, causing a UAF.
>>>>
>>>> Reset obj->resv to the BO's private _resv before calling xe_bo_put()
>>>> in the error path. The BO is not yet published (attach failed) and
>>>> carries no fences, so the switch is safe.
>>>>
>>>> Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG (QEMU):
>>>>
>>>> Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6b9c
>>>> Workqueue: ttm ttm_bo_delayed_delete [ttm]
>>>> RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
>>>> Call Trace:
>>>> <TASK>
>>>> ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
>>>> ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
>>>> ww_mutex_lock+0x3c/0xb0
>>>> ttm_bo_delayed_delete+0x41/0xc0 [ttm]
>>>> process_one_work+0x239/0x740
>>>> worker_thread+0x200/0x3f0
>>>> kthread+0x10d/0x150
>>>> ret_from_fork+0x3bd/0x470
>>>> ret_from_fork_asm+0x1a/0x30
>>>> </TASK>
>>>>
>>>> Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
>>>> Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed cleanup path for imported bos")
>>>> Cc: stable@vger.kernel.org # v6.8+
>>>> Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
>>>> Cc: Matthew Brost <matthew.brost@intel.com>
>>>> Cc: Matthew Auld <matthew.auld@intel.com>
>>>> Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
>>>> ---
>>>> drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
>>>> 1 file changed, 8 insertions(+)
>>>>
>>>> diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
>>>> index 8a920e58245c..6d944bd4065c 100644
>>>> --- a/drivers/gpu/drm/xe/xe_dma_buf.c
>>>> +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
>>>> @@ -384,6 +384,14 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
>>>> attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, obj);
>>>> if (IS_ERR(attach)) {
>>>> + /*
>>>> + * The BO was created with resv = dma_buf->resv (exporter's
>>>> + * resv). Since attach failed, no dma_buf reference is held and
>>>> + * the exporter may be freed before TTM's delayed_delete worker
>>>> + * runs. Switch to the BO's own resv to prevent a UAF when
>>>> + * ttm_bo_delayed_delete() tries to lock the stale pointer.
>>>> + */
>>>> + obj->resv = &obj->_resv;
>>>
>>> +Christian, does amdgpu not have the type of same issue here? Also any thoughts here?
>>
>> Oh, good catch. Yeah I think we have the same problem on amdgpu as well.
>
> Maybe dumb question, but why does the ttm_bo_individualize_resv() skip the final switch of the resv for type_sg?
Because we need the original resv object for cleaning up the mapping should the initial attach and then map have succeed.
> It goes through the trouble of copying the fences across?
Because we need to know when the import can be cleaned up.
In other words TTM takes a copy of the current fences and only unmap, detach and then do the final cleanup after we are sure that the set of fences which was active on destruction is now signaled.
If new fences are added to the resv object (maybe by the exporter itself or other importers) after our reference count got down to zero then we don't care about that.
> If we do need to handle this here, do we also need to grab the lru lock, like we do in ttm_bo_individualize_resv() when doing the swap?
Good question, of hand I would say yes but I clearly need to check the source code as well.
Might be better to switch the type of the BO on error so that the normal cleanup will just switch over to the local dma_resv object.
Since we don't need the original dma_resv for the cleanup that should work fine.
> Ideally xe and amdgpu can just have identical solutions here.
Yeah completely agree.
Regards,
Christian.
>
>>
>> How the heck did you found that? Do we have a dummy driver (VGEM?) which could be made to always fail attachment for a test case?
>>
>> @Vitaly can you take a look and try to come up with a test case for that? Thanks in advance.
>>
>> Thanks for the notice,
>> Christian.
>>
>>>
>>>> xe_bo_put(gem_to_xe_bo(obj));
>>>> return ERR_CAST(attach);
>>>> }
>>>
>>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-01 12:17 ` Christian König
@ 2026-06-01 15:27 ` Gote, Nitin R
2026-06-04 4:54 ` Gote, Nitin R
0 siblings, 1 reply; 15+ messages in thread
From: Gote, Nitin R @ 2026-06-01 15:27 UTC (permalink / raw)
To: Christian König, Auld, Matthew,
intel-xe@lists.freedesktop.org, Christian König
Cc: stable@vger.kernel.org, Thomas Hellstrom, Brost, Matthew,
Prosyak, Vitaly
Hi Christian,
> -----Original Message-----
> From: Christian König <christian.koenig@amd.com>
> Sent: Monday, June 1, 2026 5:47 PM
> To: Auld, Matthew <matthew.auld@intel.com>; Gote, Nitin R
> <nitin.r.gote@intel.com>; intel-xe@lists.freedesktop.org; Christian König
> <ckoenig.leichtzumerken@gmail.com>
> Cc: stable@vger.kernel.org; Thomas Hellstrom
> <thomas.hellstrom@linux.intel.com>; Brost, Matthew
> <matthew.brost@intel.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
> Subject: Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
>
> On 6/1/26 14:01, Matthew Auld wrote:
> > On 01/06/2026 12:39, Christian König wrote:
> >>
> >>
> >> On 6/1/26 12:46, Matthew Auld wrote:
> >>> On 01/06/2026 11:15, Nitin Gote wrote:
> >>>> xe_dma_buf_create_obj() creates the importer BO with obj->resv
> >>>> pointing at the exporter's dma_buf->resv. When
> >>>> dma_buf_dynamic_attach() fails, no dma_buf reference is held so the
> >>>> exporter can be freed immediately. Since ttm_bo_release() now
> >>>> always defers cleanup for ttm_bo_type_sg BOs to the TTM workqueue,
> >>>> the worker later calls
> >>>> dma_resv_lock() on the already-freed exporter resv, causing a UAF.
> >>>>
> >>>> Reset obj->resv to the BO's private _resv before calling
> >>>> xe_bo_put() in the error path. The BO is not yet published (attach
> >>>> failed) and carries no fences, so the switch is safe.
> >>>>
> >>>> Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG (QEMU):
> >>>>
> >>>> Oops: general protection fault, probably for non-canonical
> >>>> address 0x6b6b6b6b6b6b6b9c
> >>>> Workqueue: ttm ttm_bo_delayed_delete [ttm]
> >>>> RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
> >>>> Call Trace:
> >>>> <TASK>
> >>>> ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
> >>>> ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> >>>> ww_mutex_lock+0x3c/0xb0
> >>>> ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> >>>> process_one_work+0x239/0x740
> >>>> worker_thread+0x200/0x3f0
> >>>> kthread+0x10d/0x150
> >>>> ret_from_fork+0x3bd/0x470
> >>>> ret_from_fork_asm+0x1a/0x30
> >>>> </TASK>
> >>>>
> >>>> Closes:
> >>>> https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
> >>>> Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed cleanup
> >>>> path for imported bos")
> >>>> Cc: stable@vger.kernel.org # v6.8+
> >>>> Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
> >>>> Cc: Matthew Brost <matthew.brost@intel.com>
> >>>> Cc: Matthew Auld <matthew.auld@intel.com>
> >>>> Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
> >>>> ---
> >>>> drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
> >>>> 1 file changed, 8 insertions(+)
> >>>>
> >>>> diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c
> >>>> b/drivers/gpu/drm/xe/xe_dma_buf.c index 8a920e58245c..6d944bd4065c
> >>>> 100644
> >>>> --- a/drivers/gpu/drm/xe/xe_dma_buf.c
> >>>> +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
> >>>> @@ -384,6 +384,14 @@ struct drm_gem_object
> >>>> *xe_gem_prime_import(struct drm_device *dev,
> >>>> attach = dma_buf_dynamic_attach(dma_buf, dev->dev,
> >>>> attach_ops, obj);
> >>>> if (IS_ERR(attach)) {
> >>>> + /*
> >>>> + * The BO was created with resv = dma_buf->resv
> >>>> +(exporter's
> >>>> + * resv). Since attach failed, no dma_buf reference is
> >>>> +held and
> >>>> + * the exporter may be freed before TTM's delayed_delete
> >>>> +worker
> >>>> + * runs. Switch to the BO's own resv to prevent a UAF when
> >>>> + * ttm_bo_delayed_delete() tries to lock the stale pointer.
> >>>> + */
> >>>> + obj->resv = &obj->_resv;
> >>>
> >>> +Christian, does amdgpu not have the type of same issue here? Also any
> thoughts here?
> >>
> >> Oh, good catch. Yeah I think we have the same problem on amdgpu as well.
> >
> > Maybe dumb question, but why does the ttm_bo_individualize_resv() skip the
> final switch of the resv for type_sg?
>
> Because we need the original resv object for cleaning up the mapping should the
> initial attach and then map have succeed.
>
> > It goes through the trouble of copying the fences across?
>
> Because we need to know when the import can be cleaned up.
>
> In other words TTM takes a copy of the current fences and only unmap, detach
> and then do the final cleanup after we are sure that the set of fences which was
> active on destruction is now signaled.
>
> If new fences are added to the resv object (maybe by the exporter itself or other
> importers) after our reference count got down to zero then we don't care about
> that.
> > If we do need to handle this here, do we also need to grab the lru lock, like we
> do in ttm_bo_individualize_resv() when doing the swap?
>
> Good question, of hand I would say yes but I clearly need to check the source
> code as well.
>
> Might be better to switch the type of the BO on error so that the normal cleanup
> will just switch over to the local dma_resv object.
>
- obj->resv = &obj->_resv;
+ gem_to_xe_bo(obj)->ttm.type = ttm_bo_type_kernel;
Switching the type to ttm_bo_type_kernel lets ttm_bo_individualize_resv() swap resv to the BO's private _resv under lru_lock, which prevents UAF without needing any manual locking.
This seems a more correct approach.
> Since we don't need the original dma_resv for the cleanup that should work fine.
>
> > Ideally xe and amdgpu can just have identical solutions here.
>
> Yeah completely agree.
>
> Regards,
> Christian.
>
> >
> >>
> >> How the heck did you found that? Do we have a dummy driver (VGEM?) which
> could be made to always fail attachment for a test case?
The bug was found via the existing KUnit test (xe_dma_buf_kunit), which was failing on a BMG VM device. The test runs 20 parameter combinations.
the failing ones use force_different_devices=true + mem_mask=XE_BO_FLAG_VRAM0 + nop2p_attach_ops, where dma_buf_dynamic_attach() returns -EOPNOTSUPP, hitting the error path.
On bare metal BMG the race window is too narrow to hit the issue. To make it more deterministic, added a small msleep(100) in ttm_bo_delayed_delete() just before the dma_resv_lock() call, which widened the race window.
With KASAN enabled, that gave a clear slab-use-after-free in __ww_mutex_lock — the 0x6b6b6b6b SLUB poison pattern in the faulting address confirmed the UAF.
Thanks,
Nitin
> >>
> >> @Vitaly can you take a look and try to come up with a test case for that?
> Thanks in advance.
> >>
> >> Thanks for the notice,
> >> Christian.
> >>
> >>>
> >>>> xe_bo_put(gem_to_xe_bo(obj));
> >>>> return ERR_CAST(attach);
> >>>> }
> >>>
> >>
> >
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-01 15:27 ` Gote, Nitin R
@ 2026-06-04 4:54 ` Gote, Nitin R
2026-06-04 11:14 ` Thomas Hellström
0 siblings, 1 reply; 15+ messages in thread
From: Gote, Nitin R @ 2026-06-04 4:54 UTC (permalink / raw)
To: Gote, Nitin R, Christian König, Auld, Matthew,
intel-xe@lists.freedesktop.org, Christian König
Cc: stable@vger.kernel.org, Thomas Hellstrom, Brost, Matthew,
Prosyak, Vitaly
Hi,
> -----Original Message-----
> From: Intel-xe <intel-xe-bounces@lists.freedesktop.org> On Behalf Of Gote, Nitin
> R
> Sent: Monday, June 1, 2026 8:57 PM
> To: Christian König <christian.koenig@amd.com>; Auld, Matthew
> <matthew.auld@intel.com>; intel-xe@lists.freedesktop.org; Christian König
> <ckoenig.leichtzumerken@gmail.com>
> Cc: stable@vger.kernel.org; Thomas Hellstrom
> <thomas.hellstrom@linux.intel.com>; Brost, Matthew
> <matthew.brost@intel.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
> Subject: RE: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
>
> Hi Christian,
>
> > -----Original Message-----
> > From: Christian König <christian.koenig@amd.com>
> > Sent: Monday, June 1, 2026 5:47 PM
> > To: Auld, Matthew <matthew.auld@intel.com>; Gote, Nitin R
> > <nitin.r.gote@intel.com>; intel-xe@lists.freedesktop.org; Christian
> > König <ckoenig.leichtzumerken@gmail.com>
> > Cc: stable@vger.kernel.org; Thomas Hellstrom
> > <thomas.hellstrom@linux.intel.com>; Brost, Matthew
> > <matthew.brost@intel.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
> > Subject: Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on
> > attach failure
> >
> > On 6/1/26 14:01, Matthew Auld wrote:
> > > On 01/06/2026 12:39, Christian König wrote:
> > >>
> > >>
> > >> On 6/1/26 12:46, Matthew Auld wrote:
> > >>> On 01/06/2026 11:15, Nitin Gote wrote:
> > >>>> xe_dma_buf_create_obj() creates the importer BO with obj->resv
> > >>>> pointing at the exporter's dma_buf->resv. When
> > >>>> dma_buf_dynamic_attach() fails, no dma_buf reference is held so
> > >>>> the exporter can be freed immediately. Since ttm_bo_release() now
> > >>>> always defers cleanup for ttm_bo_type_sg BOs to the TTM
> > >>>> workqueue, the worker later calls
> > >>>> dma_resv_lock() on the already-freed exporter resv, causing a UAF.
> > >>>>
> > >>>> Reset obj->resv to the BO's private _resv before calling
> > >>>> xe_bo_put() in the error path. The BO is not yet published
> > >>>> (attach
> > >>>> failed) and carries no fences, so the switch is safe.
> > >>>>
> > >>>> Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG (QEMU):
> > >>>>
> > >>>> Oops: general protection fault, probably for non-canonical
> > >>>> address 0x6b6b6b6b6b6b6b9c
> > >>>> Workqueue: ttm ttm_bo_delayed_delete [ttm]
> > >>>> RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
> > >>>> Call Trace:
> > >>>> <TASK>
> > >>>> ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
> > >>>> ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> > >>>> ww_mutex_lock+0x3c/0xb0
> > >>>> ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> > >>>> process_one_work+0x239/0x740
> > >>>> worker_thread+0x200/0x3f0
> > >>>> kthread+0x10d/0x150
> > >>>> ret_from_fork+0x3bd/0x470
> > >>>> ret_from_fork_asm+0x1a/0x30
> > >>>> </TASK>
> > >>>>
> > >>>> Closes:
> > >>>> https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
> > >>>> Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed cleanup
> > >>>> path for imported bos")
> > >>>> Cc: stable@vger.kernel.org # v6.8+
> > >>>> Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
> > >>>> Cc: Matthew Brost <matthew.brost@intel.com>
> > >>>> Cc: Matthew Auld <matthew.auld@intel.com>
> > >>>> Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
> > >>>> ---
> > >>>> drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
> > >>>> 1 file changed, 8 insertions(+)
> > >>>>
> > >>>> diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c
> > >>>> b/drivers/gpu/drm/xe/xe_dma_buf.c index
> > >>>> 8a920e58245c..6d944bd4065c
> > >>>> 100644
> > >>>> --- a/drivers/gpu/drm/xe/xe_dma_buf.c
> > >>>> +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
> > >>>> @@ -384,6 +384,14 @@ struct drm_gem_object
> > >>>> *xe_gem_prime_import(struct drm_device *dev,
> > >>>> attach = dma_buf_dynamic_attach(dma_buf, dev->dev,
> > >>>> attach_ops, obj);
> > >>>> if (IS_ERR(attach)) {
> > >>>> + /*
> > >>>> + * The BO was created with resv = dma_buf->resv
> > >>>> +(exporter's
> > >>>> + * resv). Since attach failed, no dma_buf reference is
> > >>>> +held and
> > >>>> + * the exporter may be freed before TTM's delayed_delete
> > >>>> +worker
> > >>>> + * runs. Switch to the BO's own resv to prevent a UAF
> > >>>> +when
> > >>>> + * ttm_bo_delayed_delete() tries to lock the stale pointer.
> > >>>> + */
> > >>>> + obj->resv = &obj->_resv;
> > >>>
> > >>> +Christian, does amdgpu not have the type of same issue here? Also
> > >>> +any
> > thoughts here?
> > >>
> > >> Oh, good catch. Yeah I think we have the same problem on amdgpu as well.
> > >
> > > Maybe dumb question, but why does the ttm_bo_individualize_resv()
> > > skip the
> > final switch of the resv for type_sg?
> >
> > Because we need the original resv object for cleaning up the mapping
> > should the initial attach and then map have succeed.
> >
> > > It goes through the trouble of copying the fences across?
> >
> > Because we need to know when the import can be cleaned up.
> >
> > In other words TTM takes a copy of the current fences and only unmap,
> > detach and then do the final cleanup after we are sure that the set of
> > fences which was active on destruction is now signaled.
> >
> > If new fences are added to the resv object (maybe by the exporter
> > itself or other
> > importers) after our reference count got down to zero then we don't
> > care about that.
> > > If we do need to handle this here, do we also need to grab the lru
> > > lock, like we
> > do in ttm_bo_individualize_resv() when doing the swap?
> >
> > Good question, of hand I would say yes but I clearly need to check the
> > source code as well.
> >
> > Might be better to switch the type of the BO on error so that the
> > normal cleanup will just switch over to the local dma_resv object.
> >
>
> - obj->resv = &obj->_resv;
> + gem_to_xe_bo(obj)->ttm.type = ttm_bo_type_kernel;
>
> Switching the type to ttm_bo_type_kernel lets ttm_bo_individualize_resv() swap
> resv to the BO's private _resv under lru_lock, which prevents UAF without
> needing any manual locking.
Checked all bo->type readers (xe_evict_flags(), xe_bo_move(), xe_bo_can_migrate()) and found they can be called concurrently by the shrinker or eviction paths without any synchronization, making the bo->type change unsafe.
Switching resv to &obj->_resv under lru_lock, mirroring ttm_bo_individualize_resv(), is the more reasonable.
I'll send this as v2, along with a separate patch fixing the same issue in amdgpu.
- Nitin
> > Since we don't need the original dma_resv for the cleanup that should work
> fine.
> >
> > > Ideally xe and amdgpu can just have identical solutions here.
> >
> > Yeah completely agree.
> >
> > Regards,
> > Christian.
> >
> > >
> > >>
> > >> How the heck did you found that? Do we have a dummy driver (VGEM?)
> > >> which
> > could be made to always fail attachment for a test case?
>
> The bug was found via the existing KUnit test (xe_dma_buf_kunit), which was
> failing on a BMG VM device. The test runs 20 parameter combinations.
> the failing ones use force_different_devices=true +
> mem_mask=XE_BO_FLAG_VRAM0 + nop2p_attach_ops, where
> dma_buf_dynamic_attach() returns -EOPNOTSUPP, hitting the error path.
>
> On bare metal BMG the race window is too narrow to hit the issue. To make it
> more deterministic, added a small msleep(100) in ttm_bo_delayed_delete() just
> before the dma_resv_lock() call, which widened the race window.
> With KASAN enabled, that gave a clear slab-use-after-free in __ww_mutex_lock
> — the 0x6b6b6b6b SLUB poison pattern in the faulting address confirmed the
> UAF.
>
> Thanks,
> Nitin
>
> > >>
> > >> @Vitaly can you take a look and try to come up with a test case for that?
> > Thanks in advance.
> > >>
> > >> Thanks for the notice,
> > >> Christian.
> > >>
> > >>>
> > >>>> xe_bo_put(gem_to_xe_bo(obj));
> > >>>> return ERR_CAST(attach);
> > >>>> }
> > >>>
> > >>
> > >
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
@ 2026-06-04 10:23 Nitin Gote
0 siblings, 0 replies; 15+ messages in thread
From: Nitin Gote @ 2026-06-04 10:23 UTC (permalink / raw)
To: intel-xe
Cc: Nitin Gote, stable, Thomas Hellstrom, Christian Konig,
Matthew Brost, Matthew Auld
xe_dma_buf_create_obj() creates the importer BO with obj->resv
pointing at the exporter's dma_buf->resv. When dma_buf_dynamic_attach()
fails, no dma_buf reference is held so the exporter can be freed
immediately. Since ttm_bo_release() now always defers cleanup for
ttm_bo_type_sg BOs to the TTM workqueue, the worker later calls
dma_resv_lock() on the already-freed exporter resv, causing a UAF.
Switch obj->resv to the BO's private _resv under lru_lock before
dropping the last reference, mirroring ttm_bo_individualize_resv().
lru_lock is needed because the BO is already on the LRU list and
the shrinker can read bo->base.resv concurrently.
Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG (QEMU):
Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6b9c
Workqueue: ttm ttm_bo_delayed_delete [ttm]
RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
Call Trace:
<TASK>
? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
ww_mutex_lock+0x3c/0xb0
ttm_bo_delayed_delete+0x41/0xc0 [ttm]
process_one_work+0x239/0x740
worker_thread+0x200/0x3f0
kthread+0x10d/0x150
ret_from_fork+0x3bd/0x470
ret_from_fork_asm+0x1a/0x30
</TASK>
v2: Switch resv to &obj->_resv under lru_lock
(Matthew Auld)
Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed cleanup path for imported bos")
Cc: stable@vger.kernel.org # v6.8+
Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
Cc: Christian Konig <christian.koenig@amd.com>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Matthew Auld <matthew.auld@intel.com>
Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
---
drivers/gpu/drm/xe/xe_dma_buf.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
index 8a920e58245c..e3ccb3cc6218 100644
--- a/drivers/gpu/drm/xe/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/xe_dma_buf.c
@@ -384,6 +384,16 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, obj);
if (IS_ERR(attach)) {
+ /*
+ * Attach failed with no dma_buf ref held; switch resv to the BO's
+ * private _resv under lru_lock before the last put, so
+ * ttm_bo_delayed_delete() doesn't dereference the stale exporter
+ * resv.
+ */
+ spin_lock(&gem_to_xe_bo(obj)->ttm.bdev->lru_lock);
+ obj->resv = &obj->_resv;
+ spin_unlock(&gem_to_xe_bo(obj)->ttm.bdev->lru_lock);
+
xe_bo_put(gem_to_xe_bo(obj));
return ERR_CAST(attach);
}
--
2.50.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-04 4:54 ` Gote, Nitin R
@ 2026-06-04 11:14 ` Thomas Hellström
2026-06-04 11:32 ` Matthew Auld
2026-06-05 10:11 ` Christian König
0 siblings, 2 replies; 15+ messages in thread
From: Thomas Hellström @ 2026-06-04 11:14 UTC (permalink / raw)
To: Gote, Nitin R, Christian König, Auld, Matthew,
intel-xe@lists.freedesktop.org, Christian König
Cc: stable@vger.kernel.org, Brost, Matthew, Prosyak, Vitaly
Hi,
On Thu, 2026-06-04 at 04:54 +0000, Gote, Nitin R wrote:
> Hi,
>
> > -----Original Message-----
> > From: Intel-xe <intel-xe-bounces@lists.freedesktop.org> On Behalf
> > Of Gote, Nitin
> > R
> > Sent: Monday, June 1, 2026 8:57 PM
> > To: Christian König <christian.koenig@amd.com>; Auld, Matthew
> > <matthew.auld@intel.com>; intel-xe@lists.freedesktop.org; Christian
> > König
> > <ckoenig.leichtzumerken@gmail.com>
> > Cc: stable@vger.kernel.org; Thomas Hellstrom
> > <thomas.hellstrom@linux.intel.com>; Brost, Matthew
> > <matthew.brost@intel.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
> > Subject: RE: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on
> > attach failure
> >
> > Hi Christian,
> >
> > > -----Original Message-----
> > > From: Christian König <christian.koenig@amd.com>
> > > Sent: Monday, June 1, 2026 5:47 PM
> > > To: Auld, Matthew <matthew.auld@intel.com>; Gote, Nitin R
> > > <nitin.r.gote@intel.com>; intel-xe@lists.freedesktop.org;
> > > Christian
> > > König <ckoenig.leichtzumerken@gmail.com>
> > > Cc: stable@vger.kernel.org; Thomas Hellstrom
> > > <thomas.hellstrom@linux.intel.com>; Brost, Matthew
> > > <matthew.brost@intel.com>; Prosyak, Vitaly
> > > <Vitaly.Prosyak@amd.com>
> > > Subject: Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on
> > > attach failure
> > >
> > > On 6/1/26 14:01, Matthew Auld wrote:
> > > > On 01/06/2026 12:39, Christian König wrote:
> > > > >
> > > > >
> > > > > On 6/1/26 12:46, Matthew Auld wrote:
> > > > > > On 01/06/2026 11:15, Nitin Gote wrote:
> > > > > > > xe_dma_buf_create_obj() creates the importer BO with obj-
> > > > > > > >resv
> > > > > > > pointing at the exporter's dma_buf->resv. When
> > > > > > > dma_buf_dynamic_attach() fails, no dma_buf reference is
> > > > > > > held so
> > > > > > > the exporter can be freed immediately. Since
> > > > > > > ttm_bo_release() now
> > > > > > > always defers cleanup for ttm_bo_type_sg BOs to the TTM
> > > > > > > workqueue, the worker later calls
> > > > > > > dma_resv_lock() on the already-freed exporter resv,
> > > > > > > causing a UAF.
> > > > > > >
> > > > > > > Reset obj->resv to the BO's private _resv before calling
> > > > > > > xe_bo_put() in the error path. The BO is not yet
> > > > > > > published
> > > > > > > (attach
> > > > > > > failed) and carries no fences, so the switch is safe.
> > > > > > >
> > > > > > > Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG
> > > > > > > (QEMU):
> > > > > > >
> > > > > > > Oops: general protection fault, probably for non-
> > > > > > > canonical
> > > > > > > address 0x6b6b6b6b6b6b6b9c
> > > > > > > Workqueue: ttm ttm_bo_delayed_delete [ttm]
> > > > > > > RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
> > > > > > > Call Trace:
> > > > > > > <TASK>
> > > > > > > ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
> > > > > > > ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> > > > > > > ww_mutex_lock+0x3c/0xb0
> > > > > > > ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> > > > > > > process_one_work+0x239/0x740
> > > > > > > worker_thread+0x200/0x3f0
> > > > > > > kthread+0x10d/0x150
> > > > > > > ret_from_fork+0x3bd/0x470
> > > > > > > ret_from_fork_asm+0x1a/0x30
> > > > > > > </TASK>
> > > > > > >
> > > > > > > Closes:
> > > > > > > https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
> > > > > > > Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed
> > > > > > > cleanup
> > > > > > > path for imported bos")
> > > > > > > Cc: stable@vger.kernel.org # v6.8+
> > > > > > > Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
> > > > > > > Cc: Matthew Brost <matthew.brost@intel.com>
> > > > > > > Cc: Matthew Auld <matthew.auld@intel.com>
> > > > > > > Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
> > > > > > > ---
> > > > > > > drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
> > > > > > > 1 file changed, 8 insertions(+)
> > > > > > >
> > > > > > > diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c
> > > > > > > b/drivers/gpu/drm/xe/xe_dma_buf.c index
> > > > > > > 8a920e58245c..6d944bd4065c
> > > > > > > 100644
> > > > > > > --- a/drivers/gpu/drm/xe/xe_dma_buf.c
> > > > > > > +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
> > > > > > > @@ -384,6 +384,14 @@ struct drm_gem_object
> > > > > > > *xe_gem_prime_import(struct drm_device *dev,
> > > > > > > attach = dma_buf_dynamic_attach(dma_buf, dev-
> > > > > > > >dev,
> > > > > > > attach_ops, obj);
> > > > > > > if (IS_ERR(attach)) {
> > > > > > > + /*
> > > > > > > + * The BO was created with resv = dma_buf->resv
> > > > > > > +(exporter's
> > > > > > > + * resv). Since attach failed, no dma_buf
> > > > > > > reference is
> > > > > > > +held and
> > > > > > > + * the exporter may be freed before TTM's
> > > > > > > delayed_delete
> > > > > > > +worker
> > > > > > > + * runs. Switch to the BO's own resv to prevent
> > > > > > > a UAF
> > > > > > > +when
> > > > > > > + * ttm_bo_delayed_delete() tries to lock the
> > > > > > > stale pointer.
> > > > > > > + */
> > > > > > > + obj->resv = &obj->_resv;
> > > > > >
> > > > > > +Christian, does amdgpu not have the type of same issue
> > > > > > here? Also
> > > > > > +any
> > > thoughts here?
> > > > >
> > > > > Oh, good catch. Yeah I think we have the same problem on
> > > > > amdgpu as well.
> > > >
> > > > Maybe dumb question, but why does the
> > > > ttm_bo_individualize_resv()
> > > > skip the
> > > final switch of the resv for type_sg?
> > >
> > > Because we need the original resv object for cleaning up the
> > > mapping
> > > should the initial attach and then map have succeed.
> > >
> > > > It goes through the trouble of copying the fences across?
> > >
> > > Because we need to know when the import can be cleaned up.
> > >
> > > In other words TTM takes a copy of the current fences and only
> > > unmap,
> > > detach and then do the final cleanup after we are sure that the
> > > set of
> > > fences which was active on destruction is now signaled.
> > >
> > > If new fences are added to the resv object (maybe by the exporter
> > > itself or other
> > > importers) after our reference count got down to zero then we
> > > don't
> > > care about that.
> > > > If we do need to handle this here, do we also need to grab the
> > > > lru
> > > > lock, like we
> > > do in ttm_bo_individualize_resv() when doing the swap?
> > >
> > > Good question, of hand I would say yes but I clearly need to
> > > check the
> > > source code as well.
> > >
> > > Might be better to switch the type of the BO on error so that the
> > > normal cleanup will just switch over to the local dma_resv
> > > object.
> > >
> >
> > - obj->resv = &obj->_resv;
> > + gem_to_xe_bo(obj)->ttm.type = ttm_bo_type_kernel;
> >
> > Switching the type to ttm_bo_type_kernel lets
> > ttm_bo_individualize_resv() swap
> > resv to the BO's private _resv under lru_lock, which prevents UAF
> > without
> > needing any manual locking.
The lru lock is IIRC only needed and safe when the ttm refcount is zero
(in the TTM destruction path) to protect against a racing LRU walk
trylock succeeds against the incorrect resv.
I wonder whether this was actually why xe code initially took care not
to publish the bo on the LRUs until the attachment succeeded.
A TTM LRU walker may pick up the exporting resv as soon as the resource
is published on the LRU, and then try to lock it using
ttm_lru_walk_ticketlock(). The lru lock doesn't protect against that.
So we have a sort of moment22, since with that approach move_notify()
could be called without the bo being fully initialized.
One way to move forward would perhaps be to, for now, reinstate that
and have move_notify check if the bo is a stub or fully initialized
before doing anything.
Also perhaps we should in the future consider allowing dma-buf
attachment removal under a separate lower-level lock than the resv.
Thanks,
Thomas
>
> Checked all bo->type readers (xe_evict_flags(), xe_bo_move(),
> xe_bo_can_migrate()) and found they can be called concurrently by the
> shrinker or eviction paths without any synchronization, making the
> bo->type change unsafe.
>
> Switching resv to &obj->_resv under lru_lock, mirroring
> ttm_bo_individualize_resv(), is the more reasonable.
> I'll send this as v2, along with a separate patch fixing the same
> issue in amdgpu.
>
> - Nitin
>
> > > Since we don't need the original dma_resv for the cleanup that
> > > should work
> > fine.
> > >
> > > > Ideally xe and amdgpu can just have identical solutions here.
> > >
> > > Yeah completely agree.
> > >
> > > Regards,
> > > Christian.
> > >
> > > >
> > > > >
> > > > > How the heck did you found that? Do we have a dummy driver
> > > > > (VGEM?)
> > > > > which
> > > could be made to always fail attachment for a test case?
> >
> > The bug was found via the existing KUnit test (xe_dma_buf_kunit),
> > which was
> > failing on a BMG VM device. The test runs 20 parameter
> > combinations.
> > the failing ones use force_different_devices=true +
> > mem_mask=XE_BO_FLAG_VRAM0 + nop2p_attach_ops, where
> > dma_buf_dynamic_attach() returns -EOPNOTSUPP, hitting the error
> > path.
> >
> > On bare metal BMG the race window is too narrow to hit the issue.
> > To make it
> > more deterministic, added a small msleep(100) in
> > ttm_bo_delayed_delete() just
> > before the dma_resv_lock() call, which widened the race window.
> > With KASAN enabled, that gave a clear slab-use-after-free in
> > __ww_mutex_lock
> > — the 0x6b6b6b6b SLUB poison pattern in the faulting address
> > confirmed the
> > UAF.
> >
> > Thanks,
> > Nitin
> >
> > > > >
> > > > > @Vitaly can you take a look and try to come up with a test
> > > > > case for that?
> > > Thanks in advance.
> > > > >
> > > > > Thanks for the notice,
> > > > > Christian.
> > > > >
> > > > > >
> > > > > > > xe_bo_put(gem_to_xe_bo(obj));
> > > > > > > return ERR_CAST(attach);
> > > > > > > }
> > > > > >
> > > > >
> > > >
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-04 11:14 ` Thomas Hellström
@ 2026-06-04 11:32 ` Matthew Auld
2026-06-04 11:55 ` Thomas Hellström
2026-06-05 9:56 ` Christian König
2026-06-05 10:11 ` Christian König
1 sibling, 2 replies; 15+ messages in thread
From: Matthew Auld @ 2026-06-04 11:32 UTC (permalink / raw)
To: Thomas Hellström, Gote, Nitin R, Christian König,
intel-xe@lists.freedesktop.org, Christian König
Cc: stable@vger.kernel.org, Brost, Matthew, Prosyak, Vitaly
On 04/06/2026 12:14, Thomas Hellström wrote:
> Hi,
>
> On Thu, 2026-06-04 at 04:54 +0000, Gote, Nitin R wrote:
>> Hi,
>>
>>> -----Original Message-----
>>> From: Intel-xe <intel-xe-bounces@lists.freedesktop.org> On Behalf
>>> Of Gote, Nitin
>>> R
>>> Sent: Monday, June 1, 2026 8:57 PM
>>> To: Christian König <christian.koenig@amd.com>; Auld, Matthew
>>> <matthew.auld@intel.com>; intel-xe@lists.freedesktop.org; Christian
>>> König
>>> <ckoenig.leichtzumerken@gmail.com>
>>> Cc: stable@vger.kernel.org; Thomas Hellstrom
>>> <thomas.hellstrom@linux.intel.com>; Brost, Matthew
>>> <matthew.brost@intel.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
>>> Subject: RE: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on
>>> attach failure
>>>
>>> Hi Christian,
>>>
>>>> -----Original Message-----
>>>> From: Christian König <christian.koenig@amd.com>
>>>> Sent: Monday, June 1, 2026 5:47 PM
>>>> To: Auld, Matthew <matthew.auld@intel.com>; Gote, Nitin R
>>>> <nitin.r.gote@intel.com>; intel-xe@lists.freedesktop.org;
>>>> Christian
>>>> König <ckoenig.leichtzumerken@gmail.com>
>>>> Cc: stable@vger.kernel.org; Thomas Hellstrom
>>>> <thomas.hellstrom@linux.intel.com>; Brost, Matthew
>>>> <matthew.brost@intel.com>; Prosyak, Vitaly
>>>> <Vitaly.Prosyak@amd.com>
>>>> Subject: Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on
>>>> attach failure
>>>>
>>>> On 6/1/26 14:01, Matthew Auld wrote:
>>>>> On 01/06/2026 12:39, Christian König wrote:
>>>>>>
>>>>>>
>>>>>> On 6/1/26 12:46, Matthew Auld wrote:
>>>>>>> On 01/06/2026 11:15, Nitin Gote wrote:
>>>>>>>> xe_dma_buf_create_obj() creates the importer BO with obj-
>>>>>>>>> resv
>>>>>>>> pointing at the exporter's dma_buf->resv. When
>>>>>>>> dma_buf_dynamic_attach() fails, no dma_buf reference is
>>>>>>>> held so
>>>>>>>> the exporter can be freed immediately. Since
>>>>>>>> ttm_bo_release() now
>>>>>>>> always defers cleanup for ttm_bo_type_sg BOs to the TTM
>>>>>>>> workqueue, the worker later calls
>>>>>>>> dma_resv_lock() on the already-freed exporter resv,
>>>>>>>> causing a UAF.
>>>>>>>>
>>>>>>>> Reset obj->resv to the BO's private _resv before calling
>>>>>>>> xe_bo_put() in the error path. The BO is not yet
>>>>>>>> published
>>>>>>>> (attach
>>>>>>>> failed) and carries no fences, so the switch is safe.
>>>>>>>>
>>>>>>>> Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG
>>>>>>>> (QEMU):
>>>>>>>>
>>>>>>>> Oops: general protection fault, probably for non-
>>>>>>>> canonical
>>>>>>>> address 0x6b6b6b6b6b6b6b9c
>>>>>>>> Workqueue: ttm ttm_bo_delayed_delete [ttm]
>>>>>>>> RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
>>>>>>>> Call Trace:
>>>>>>>> <TASK>
>>>>>>>> ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
>>>>>>>> ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
>>>>>>>> ww_mutex_lock+0x3c/0xb0
>>>>>>>> ttm_bo_delayed_delete+0x41/0xc0 [ttm]
>>>>>>>> process_one_work+0x239/0x740
>>>>>>>> worker_thread+0x200/0x3f0
>>>>>>>> kthread+0x10d/0x150
>>>>>>>> ret_from_fork+0x3bd/0x470
>>>>>>>> ret_from_fork_asm+0x1a/0x30
>>>>>>>> </TASK>
>>>>>>>>
>>>>>>>> Closes:
>>>>>>>> https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
>>>>>>>> Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed
>>>>>>>> cleanup
>>>>>>>> path for imported bos")
>>>>>>>> Cc: stable@vger.kernel.org # v6.8+
>>>>>>>> Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
>>>>>>>> Cc: Matthew Brost <matthew.brost@intel.com>
>>>>>>>> Cc: Matthew Auld <matthew.auld@intel.com>
>>>>>>>> Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
>>>>>>>> ---
>>>>>>>> drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
>>>>>>>> 1 file changed, 8 insertions(+)
>>>>>>>>
>>>>>>>> diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c
>>>>>>>> b/drivers/gpu/drm/xe/xe_dma_buf.c index
>>>>>>>> 8a920e58245c..6d944bd4065c
>>>>>>>> 100644
>>>>>>>> --- a/drivers/gpu/drm/xe/xe_dma_buf.c
>>>>>>>> +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
>>>>>>>> @@ -384,6 +384,14 @@ struct drm_gem_object
>>>>>>>> *xe_gem_prime_import(struct drm_device *dev,
>>>>>>>> attach = dma_buf_dynamic_attach(dma_buf, dev-
>>>>>>>>> dev,
>>>>>>>> attach_ops, obj);
>>>>>>>> if (IS_ERR(attach)) {
>>>>>>>> + /*
>>>>>>>> + * The BO was created with resv = dma_buf->resv
>>>>>>>> +(exporter's
>>>>>>>> + * resv). Since attach failed, no dma_buf
>>>>>>>> reference is
>>>>>>>> +held and
>>>>>>>> + * the exporter may be freed before TTM's
>>>>>>>> delayed_delete
>>>>>>>> +worker
>>>>>>>> + * runs. Switch to the BO's own resv to prevent
>>>>>>>> a UAF
>>>>>>>> +when
>>>>>>>> + * ttm_bo_delayed_delete() tries to lock the
>>>>>>>> stale pointer.
>>>>>>>> + */
>>>>>>>> + obj->resv = &obj->_resv;
>>>>>>>
>>>>>>> +Christian, does amdgpu not have the type of same issue
>>>>>>> here? Also
>>>>>>> +any
>>>> thoughts here?
>>>>>>
>>>>>> Oh, good catch. Yeah I think we have the same problem on
>>>>>> amdgpu as well.
>>>>>
>>>>> Maybe dumb question, but why does the
>>>>> ttm_bo_individualize_resv()
>>>>> skip the
>>>> final switch of the resv for type_sg?
>>>>
>>>> Because we need the original resv object for cleaning up the
>>>> mapping
>>>> should the initial attach and then map have succeed.
>>>>
>>>>> It goes through the trouble of copying the fences across?
>>>>
>>>> Because we need to know when the import can be cleaned up.
>>>>
>>>> In other words TTM takes a copy of the current fences and only
>>>> unmap,
>>>> detach and then do the final cleanup after we are sure that the
>>>> set of
>>>> fences which was active on destruction is now signaled.
>>>>
>>>> If new fences are added to the resv object (maybe by the exporter
>>>> itself or other
>>>> importers) after our reference count got down to zero then we
>>>> don't
>>>> care about that.
>>>>> If we do need to handle this here, do we also need to grab the
>>>>> lru
>>>>> lock, like we
>>>> do in ttm_bo_individualize_resv() when doing the swap?
>>>>
>>>> Good question, of hand I would say yes but I clearly need to
>>>> check the
>>>> source code as well.
>>>>
>>>> Might be better to switch the type of the BO on error so that the
>>>> normal cleanup will just switch over to the local dma_resv
>>>> object.
>>>>
>>>
>>> - obj->resv = &obj->_resv;
>>> + gem_to_xe_bo(obj)->ttm.type = ttm_bo_type_kernel;
>>>
>>> Switching the type to ttm_bo_type_kernel lets
>>> ttm_bo_individualize_resv() swap
>>> resv to the BO's private _resv under lru_lock, which prevents UAF
>>> without
>>> needing any manual locking.
>
> The lru lock is IIRC only needed and safe when the ttm refcount is zero
> (in the TTM destruction path) to protect against a racing LRU walk
> trylock succeeds against the incorrect resv.
>
> I wonder whether this was actually why xe code initially took care not
> to publish the bo on the LRUs until the attachment succeeded.
>
> A TTM LRU walker may pick up the exporting resv as soon as the resource
> is published on the LRU, and then try to lock it using
> ttm_lru_walk_ticketlock(). The lru lock doesn't protect against that.
>
> So we have a sort of moment22, since with that approach move_notify()
> could be called without the bo being fully initialized.
>
> One way to move forward would perhaps be to, for now, reinstate that
> and have move_notify check if the bo is a stub or fully initialized
> before doing anything.
>
> Also perhaps we should in the future consider allowing dma-buf
> attachment removal under a separate lower-level lock than the resv.
Is it plausible to check for drm_gem_is_imported() in
ttm_bo_individualize_resv()? If sg && !imported then it should be safe
to swap out the resv?
>
> Thanks,
> Thomas
>
>
>>
>> Checked all bo->type readers (xe_evict_flags(), xe_bo_move(),
>> xe_bo_can_migrate()) and found they can be called concurrently by the
>> shrinker or eviction paths without any synchronization, making the
>> bo->type change unsafe.
>>
>> Switching resv to &obj->_resv under lru_lock, mirroring
>> ttm_bo_individualize_resv(), is the more reasonable.
>> I'll send this as v2, along with a separate patch fixing the same
>> issue in amdgpu.
>>
>> - Nitin
>>
>>>> Since we don't need the original dma_resv for the cleanup that
>>>> should work
>>> fine.
>>>>
>>>>> Ideally xe and amdgpu can just have identical solutions here.
>>>>
>>>> Yeah completely agree.
>>>>
>>>> Regards,
>>>> Christian.
>>>>
>>>>>
>>>>>>
>>>>>> How the heck did you found that? Do we have a dummy driver
>>>>>> (VGEM?)
>>>>>> which
>>>> could be made to always fail attachment for a test case?
>>>
>>> The bug was found via the existing KUnit test (xe_dma_buf_kunit),
>>> which was
>>> failing on a BMG VM device. The test runs 20 parameter
>>> combinations.
>>> the failing ones use force_different_devices=true +
>>> mem_mask=XE_BO_FLAG_VRAM0 + nop2p_attach_ops, where
>>> dma_buf_dynamic_attach() returns -EOPNOTSUPP, hitting the error
>>> path.
>>>
>>> On bare metal BMG the race window is too narrow to hit the issue.
>>> To make it
>>> more deterministic, added a small msleep(100) in
>>> ttm_bo_delayed_delete() just
>>> before the dma_resv_lock() call, which widened the race window.
>>> With KASAN enabled, that gave a clear slab-use-after-free in
>>> __ww_mutex_lock
>>> — the 0x6b6b6b6b SLUB poison pattern in the faulting address
>>> confirmed the
>>> UAF.
>>>
>>> Thanks,
>>> Nitin
>>>
>>>>>>
>>>>>> @Vitaly can you take a look and try to come up with a test
>>>>>> case for that?
>>>> Thanks in advance.
>>>>>>
>>>>>> Thanks for the notice,
>>>>>> Christian.
>>>>>>
>>>>>>>
>>>>>>>> xe_bo_put(gem_to_xe_bo(obj));
>>>>>>>> return ERR_CAST(attach);
>>>>>>>> }
>>>>>>>
>>>>>>
>>>>>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-04 11:32 ` Matthew Auld
@ 2026-06-04 11:55 ` Thomas Hellström
2026-06-05 9:56 ` Christian König
1 sibling, 0 replies; 15+ messages in thread
From: Thomas Hellström @ 2026-06-04 11:55 UTC (permalink / raw)
To: Matthew Auld, Gote, Nitin R, Christian König,
intel-xe@lists.freedesktop.org, Christian König
Cc: stable@vger.kernel.org, Brost, Matthew, Prosyak, Vitaly
On Thu, 2026-06-04 at 12:32 +0100, Matthew Auld wrote:
> On 04/06/2026 12:14, Thomas Hellström wrote:
> > Hi,
> >
> > On Thu, 2026-06-04 at 04:54 +0000, Gote, Nitin R wrote:
> > > Hi,
> > >
> > > > -----Original Message-----
> > > > From: Intel-xe <intel-xe-bounces@lists.freedesktop.org> On
> > > > Behalf
> > > > Of Gote, Nitin
> > > > R
> > > > Sent: Monday, June 1, 2026 8:57 PM
> > > > To: Christian König <christian.koenig@amd.com>; Auld, Matthew
> > > > <matthew.auld@intel.com>; intel-xe@lists.freedesktop.org;
> > > > Christian
> > > > König
> > > > <ckoenig.leichtzumerken@gmail.com>
> > > > Cc: stable@vger.kernel.org; Thomas Hellstrom
> > > > <thomas.hellstrom@linux.intel.com>; Brost, Matthew
> > > > <matthew.brost@intel.com>; Prosyak, Vitaly
> > > > <Vitaly.Prosyak@amd.com>
> > > > Subject: RE: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import()
> > > > on
> > > > attach failure
> > > >
> > > > Hi Christian,
> > > >
> > > > > -----Original Message-----
> > > > > From: Christian König <christian.koenig@amd.com>
> > > > > Sent: Monday, June 1, 2026 5:47 PM
> > > > > To: Auld, Matthew <matthew.auld@intel.com>; Gote, Nitin R
> > > > > <nitin.r.gote@intel.com>; intel-xe@lists.freedesktop.org;
> > > > > Christian
> > > > > König <ckoenig.leichtzumerken@gmail.com>
> > > > > Cc: stable@vger.kernel.org; Thomas Hellstrom
> > > > > <thomas.hellstrom@linux.intel.com>; Brost, Matthew
> > > > > <matthew.brost@intel.com>; Prosyak, Vitaly
> > > > > <Vitaly.Prosyak@amd.com>
> > > > > Subject: Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import()
> > > > > on
> > > > > attach failure
> > > > >
> > > > > On 6/1/26 14:01, Matthew Auld wrote:
> > > > > > On 01/06/2026 12:39, Christian König wrote:
> > > > > > >
> > > > > > >
> > > > > > > On 6/1/26 12:46, Matthew Auld wrote:
> > > > > > > > On 01/06/2026 11:15, Nitin Gote wrote:
> > > > > > > > > xe_dma_buf_create_obj() creates the importer BO with
> > > > > > > > > obj-
> > > > > > > > > > resv
> > > > > > > > > pointing at the exporter's dma_buf->resv. When
> > > > > > > > > dma_buf_dynamic_attach() fails, no dma_buf reference
> > > > > > > > > is
> > > > > > > > > held so
> > > > > > > > > the exporter can be freed immediately. Since
> > > > > > > > > ttm_bo_release() now
> > > > > > > > > always defers cleanup for ttm_bo_type_sg BOs to the
> > > > > > > > > TTM
> > > > > > > > > workqueue, the worker later calls
> > > > > > > > > dma_resv_lock() on the already-freed exporter resv,
> > > > > > > > > causing a UAF.
> > > > > > > > >
> > > > > > > > > Reset obj->resv to the BO's private _resv before
> > > > > > > > > calling
> > > > > > > > > xe_bo_put() in the error path. The BO is not yet
> > > > > > > > > published
> > > > > > > > > (attach
> > > > > > > > > failed) and carries no fences, so the switch is safe.
> > > > > > > > >
> > > > > > > > > Observed with igt@xe_live_ktest@xe_dma_buf_kunit on
> > > > > > > > > BMG
> > > > > > > > > (QEMU):
> > > > > > > > >
> > > > > > > > > Oops: general protection fault, probably for
> > > > > > > > > non-
> > > > > > > > > canonical
> > > > > > > > > address 0x6b6b6b6b6b6b6b9c
> > > > > > > > > Workqueue: ttm ttm_bo_delayed_delete [ttm]
> > > > > > > > > RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
> > > > > > > > > Call Trace:
> > > > > > > > > <TASK>
> > > > > > > > > ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
> > > > > > > > > ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> > > > > > > > > ww_mutex_lock+0x3c/0xb0
> > > > > > > > > ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> > > > > > > > > process_one_work+0x239/0x740
> > > > > > > > > worker_thread+0x200/0x3f0
> > > > > > > > > kthread+0x10d/0x150
> > > > > > > > > ret_from_fork+0x3bd/0x470
> > > > > > > > > ret_from_fork_asm+0x1a/0x30
> > > > > > > > > </TASK>
> > > > > > > > >
> > > > > > > > > Closes:
> > > > > > > > > https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
> > > > > > > > > Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo
> > > > > > > > > delayed
> > > > > > > > > cleanup
> > > > > > > > > path for imported bos")
> > > > > > > > > Cc: stable@vger.kernel.org # v6.8+
> > > > > > > > > Cc: Thomas Hellstrom
> > > > > > > > > <thomas.hellstrom@linux.intel.com>
> > > > > > > > > Cc: Matthew Brost <matthew.brost@intel.com>
> > > > > > > > > Cc: Matthew Auld <matthew.auld@intel.com>
> > > > > > > > > Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
> > > > > > > > > ---
> > > > > > > > > drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
> > > > > > > > > 1 file changed, 8 insertions(+)
> > > > > > > > >
> > > > > > > > > diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c
> > > > > > > > > b/drivers/gpu/drm/xe/xe_dma_buf.c index
> > > > > > > > > 8a920e58245c..6d944bd4065c
> > > > > > > > > 100644
> > > > > > > > > --- a/drivers/gpu/drm/xe/xe_dma_buf.c
> > > > > > > > > +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
> > > > > > > > > @@ -384,6 +384,14 @@ struct drm_gem_object
> > > > > > > > > *xe_gem_prime_import(struct drm_device *dev,
> > > > > > > > > attach = dma_buf_dynamic_attach(dma_buf,
> > > > > > > > > dev-
> > > > > > > > > > dev,
> > > > > > > > > attach_ops, obj);
> > > > > > > > > if (IS_ERR(attach)) {
> > > > > > > > > + /*
> > > > > > > > > + * The BO was created with resv = dma_buf-
> > > > > > > > > >resv
> > > > > > > > > +(exporter's
> > > > > > > > > + * resv). Since attach failed, no dma_buf
> > > > > > > > > reference is
> > > > > > > > > +held and
> > > > > > > > > + * the exporter may be freed before TTM's
> > > > > > > > > delayed_delete
> > > > > > > > > +worker
> > > > > > > > > + * runs. Switch to the BO's own resv to
> > > > > > > > > prevent
> > > > > > > > > a UAF
> > > > > > > > > +when
> > > > > > > > > + * ttm_bo_delayed_delete() tries to lock the
> > > > > > > > > stale pointer.
> > > > > > > > > + */
> > > > > > > > > + obj->resv = &obj->_resv;
> > > > > > > >
> > > > > > > > +Christian, does amdgpu not have the type of same issue
> > > > > > > > here? Also
> > > > > > > > +any
> > > > > thoughts here?
> > > > > > >
> > > > > > > Oh, good catch. Yeah I think we have the same problem on
> > > > > > > amdgpu as well.
> > > > > >
> > > > > > Maybe dumb question, but why does the
> > > > > > ttm_bo_individualize_resv()
> > > > > > skip the
> > > > > final switch of the resv for type_sg?
> > > > >
> > > > > Because we need the original resv object for cleaning up the
> > > > > mapping
> > > > > should the initial attach and then map have succeed.
> > > > >
> > > > > > It goes through the trouble of copying the fences across?
> > > > >
> > > > > Because we need to know when the import can be cleaned up.
> > > > >
> > > > > In other words TTM takes a copy of the current fences and
> > > > > only
> > > > > unmap,
> > > > > detach and then do the final cleanup after we are sure that
> > > > > the
> > > > > set of
> > > > > fences which was active on destruction is now signaled.
> > > > >
> > > > > If new fences are added to the resv object (maybe by the
> > > > > exporter
> > > > > itself or other
> > > > > importers) after our reference count got down to zero then we
> > > > > don't
> > > > > care about that.
> > > > > > If we do need to handle this here, do we also need to grab
> > > > > > the
> > > > > > lru
> > > > > > lock, like we
> > > > > do in ttm_bo_individualize_resv() when doing the swap?
> > > > >
> > > > > Good question, of hand I would say yes but I clearly need to
> > > > > check the
> > > > > source code as well.
> > > > >
> > > > > Might be better to switch the type of the BO on error so that
> > > > > the
> > > > > normal cleanup will just switch over to the local dma_resv
> > > > > object.
> > > > >
> > > >
> > > > - obj->resv = &obj->_resv;
> > > > + gem_to_xe_bo(obj)->ttm.type =
> > > > ttm_bo_type_kernel;
> > > >
> > > > Switching the type to ttm_bo_type_kernel lets
> > > > ttm_bo_individualize_resv() swap
> > > > resv to the BO's private _resv under lru_lock, which prevents
> > > > UAF
> > > > without
> > > > needing any manual locking.
> >
> > The lru lock is IIRC only needed and safe when the ttm refcount is
> > zero
> > (in the TTM destruction path) to protect against a racing LRU walk
> > trylock succeeds against the incorrect resv.
> >
> > I wonder whether this was actually why xe code initially took care
> > not
> > to publish the bo on the LRUs until the attachment succeeded.
> >
> > A TTM LRU walker may pick up the exporting resv as soon as the
> > resource
> > is published on the LRU, and then try to lock it using
> > ttm_lru_walk_ticketlock(). The lru lock doesn't protect against
> > that.
> >
> > So we have a sort of moment22, since with that approach
> > move_notify()
> > could be called without the bo being fully initialized.
> >
> > One way to move forward would perhaps be to, for now, reinstate
> > that
> > and have move_notify check if the bo is a stub or fully initialized
> > before doing anything.
> >
> > Also perhaps we should in the future consider allowing dma-buf
> > attachment removal under a separate lower-level lock than the resv.
>
> Is it plausible to check for drm_gem_is_imported() in
> ttm_bo_individualize_resv()? If sg && !imported then it should be
> safe
> to swap out the resv?
That'd require we never dropped the exporter refcount until the
individualization happens. Hard to assert, and would be fragile. Also
not completely sure on the stance currently WRT gem and ttm layer code
separation, but that OTH is fixable.
/Thomas
>
> >
> > Thanks,
> > Thomas
> >
> >
> > >
> > > Checked all bo->type readers (xe_evict_flags(), xe_bo_move(),
> > > xe_bo_can_migrate()) and found they can be called concurrently by
> > > the
> > > shrinker or eviction paths without any synchronization, making
> > > the
> > > bo->type change unsafe.
> > >
> > > Switching resv to &obj->_resv under lru_lock, mirroring
> > > ttm_bo_individualize_resv(), is the more reasonable.
> > > I'll send this as v2, along with a separate patch fixing the same
> > > issue in amdgpu.
> > >
> > > - Nitin
> > >
> > > > > Since we don't need the original dma_resv for the cleanup
> > > > > that
> > > > > should work
> > > > fine.
> > > > >
> > > > > > Ideally xe and amdgpu can just have identical solutions
> > > > > > here.
> > > > >
> > > > > Yeah completely agree.
> > > > >
> > > > > Regards,
> > > > > Christian.
> > > > >
> > > > > >
> > > > > > >
> > > > > > > How the heck did you found that? Do we have a dummy
> > > > > > > driver
> > > > > > > (VGEM?)
> > > > > > > which
> > > > > could be made to always fail attachment for a test case?
> > > >
> > > > The bug was found via the existing KUnit test
> > > > (xe_dma_buf_kunit),
> > > > which was
> > > > failing on a BMG VM device. The test runs 20 parameter
> > > > combinations.
> > > > the failing ones use force_different_devices=true +
> > > > mem_mask=XE_BO_FLAG_VRAM0 + nop2p_attach_ops, where
> > > > dma_buf_dynamic_attach() returns -EOPNOTSUPP, hitting the error
> > > > path.
> > > >
> > > > On bare metal BMG the race window is too narrow to hit the
> > > > issue.
> > > > To make it
> > > > more deterministic, added a small msleep(100) in
> > > > ttm_bo_delayed_delete() just
> > > > before the dma_resv_lock() call, which widened the race window.
> > > > With KASAN enabled, that gave a clear slab-use-after-free in
> > > > __ww_mutex_lock
> > > > — the 0x6b6b6b6b SLUB poison pattern in the faulting address
> > > > confirmed the
> > > > UAF.
> > > >
> > > > Thanks,
> > > > Nitin
> > > >
> > > > > > >
> > > > > > > @Vitaly can you take a look and try to come up with a
> > > > > > > test
> > > > > > > case for that?
> > > > > Thanks in advance.
> > > > > > >
> > > > > > > Thanks for the notice,
> > > > > > > Christian.
> > > > > > >
> > > > > > > >
> > > > > > > > > xe_bo_put(gem_to_xe_bo(obj));
> > > > > > > > > return ERR_CAST(attach);
> > > > > > > > > }
> > > > > > > >
> > > > > > >
> > > > > >
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-04 11:32 ` Matthew Auld
2026-06-04 11:55 ` Thomas Hellström
@ 2026-06-05 9:56 ` Christian König
2026-06-11 13:04 ` Gote, Nitin R
1 sibling, 1 reply; 15+ messages in thread
From: Christian König @ 2026-06-05 9:56 UTC (permalink / raw)
To: Matthew Auld, Thomas Hellström, Gote, Nitin R,
intel-xe@lists.freedesktop.org, Christian König
Cc: stable@vger.kernel.org, Brost, Matthew, Prosyak, Vitaly
On 6/4/26 13:32, Matthew Auld wrote:
> On 04/06/2026 12:14, Thomas Hellström wrote:
>> Hi,
>>
>> On Thu, 2026-06-04 at 04:54 +0000, Gote, Nitin R wrote:
>>> Hi,
>>>
>>>> -----Original Message-----
>>>> From: Intel-xe <intel-xe-bounces@lists.freedesktop.org> On Behalf
>>>> Of Gote, Nitin
>>>> R
>>>> Sent: Monday, June 1, 2026 8:57 PM
>>>> To: Christian König <christian.koenig@amd.com>; Auld, Matthew
>>>> <matthew.auld@intel.com>; intel-xe@lists.freedesktop.org; Christian
>>>> König
>>>> <ckoenig.leichtzumerken@gmail.com>
>>>> Cc: stable@vger.kernel.org; Thomas Hellstrom
>>>> <thomas.hellstrom@linux.intel.com>; Brost, Matthew
>>>> <matthew.brost@intel.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
>>>> Subject: RE: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on
>>>> attach failure
>>>>
>>>> Hi Christian,
>>>>
>>>>> -----Original Message-----
>>>>> From: Christian König <christian.koenig@amd.com>
>>>>> Sent: Monday, June 1, 2026 5:47 PM
>>>>> To: Auld, Matthew <matthew.auld@intel.com>; Gote, Nitin R
>>>>> <nitin.r.gote@intel.com>; intel-xe@lists.freedesktop.org;
>>>>> Christian
>>>>> König <ckoenig.leichtzumerken@gmail.com>
>>>>> Cc: stable@vger.kernel.org; Thomas Hellstrom
>>>>> <thomas.hellstrom@linux.intel.com>; Brost, Matthew
>>>>> <matthew.brost@intel.com>; Prosyak, Vitaly
>>>>> <Vitaly.Prosyak@amd.com>
>>>>> Subject: Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on
>>>>> attach failure
>>>>>
>>>>> On 6/1/26 14:01, Matthew Auld wrote:
>>>>>> On 01/06/2026 12:39, Christian König wrote:
>>>>>>>
>>>>>>>
>>>>>>> On 6/1/26 12:46, Matthew Auld wrote:
>>>>>>>> On 01/06/2026 11:15, Nitin Gote wrote:
>>>>>>>>> xe_dma_buf_create_obj() creates the importer BO with obj-
>>>>>>>>>> resv
>>>>>>>>> pointing at the exporter's dma_buf->resv. When
>>>>>>>>> dma_buf_dynamic_attach() fails, no dma_buf reference is
>>>>>>>>> held so
>>>>>>>>> the exporter can be freed immediately. Since
>>>>>>>>> ttm_bo_release() now
>>>>>>>>> always defers cleanup for ttm_bo_type_sg BOs to the TTM
>>>>>>>>> workqueue, the worker later calls
>>>>>>>>> dma_resv_lock() on the already-freed exporter resv,
>>>>>>>>> causing a UAF.
>>>>>>>>>
>>>>>>>>> Reset obj->resv to the BO's private _resv before calling
>>>>>>>>> xe_bo_put() in the error path. The BO is not yet
>>>>>>>>> published
>>>>>>>>> (attach
>>>>>>>>> failed) and carries no fences, so the switch is safe.
>>>>>>>>>
>>>>>>>>> Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG
>>>>>>>>> (QEMU):
>>>>>>>>>
>>>>>>>>> Oops: general protection fault, probably for non-
>>>>>>>>> canonical
>>>>>>>>> address 0x6b6b6b6b6b6b6b9c
>>>>>>>>> Workqueue: ttm ttm_bo_delayed_delete [ttm]
>>>>>>>>> RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
>>>>>>>>> Call Trace:
>>>>>>>>> <TASK>
>>>>>>>>> ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
>>>>>>>>> ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
>>>>>>>>> ww_mutex_lock+0x3c/0xb0
>>>>>>>>> ttm_bo_delayed_delete+0x41/0xc0 [ttm]
>>>>>>>>> process_one_work+0x239/0x740
>>>>>>>>> worker_thread+0x200/0x3f0
>>>>>>>>> kthread+0x10d/0x150
>>>>>>>>> ret_from_fork+0x3bd/0x470
>>>>>>>>> ret_from_fork_asm+0x1a/0x30
>>>>>>>>> </TASK>
>>>>>>>>>
>>>>>>>>> Closes:
>>>>>>>>> https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
>>>>>>>>> Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed
>>>>>>>>> cleanup
>>>>>>>>> path for imported bos")
>>>>>>>>> Cc: stable@vger.kernel.org # v6.8+
>>>>>>>>> Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
>>>>>>>>> Cc: Matthew Brost <matthew.brost@intel.com>
>>>>>>>>> Cc: Matthew Auld <matthew.auld@intel.com>
>>>>>>>>> Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
>>>>>>>>> ---
>>>>>>>>> drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
>>>>>>>>> 1 file changed, 8 insertions(+)
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c
>>>>>>>>> b/drivers/gpu/drm/xe/xe_dma_buf.c index
>>>>>>>>> 8a920e58245c..6d944bd4065c
>>>>>>>>> 100644
>>>>>>>>> --- a/drivers/gpu/drm/xe/xe_dma_buf.c
>>>>>>>>> +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
>>>>>>>>> @@ -384,6 +384,14 @@ struct drm_gem_object
>>>>>>>>> *xe_gem_prime_import(struct drm_device *dev,
>>>>>>>>> attach = dma_buf_dynamic_attach(dma_buf, dev-
>>>>>>>>>> dev,
>>>>>>>>> attach_ops, obj);
>>>>>>>>> if (IS_ERR(attach)) {
>>>>>>>>> + /*
>>>>>>>>> + * The BO was created with resv = dma_buf->resv
>>>>>>>>> +(exporter's
>>>>>>>>> + * resv). Since attach failed, no dma_buf
>>>>>>>>> reference is
>>>>>>>>> +held and
>>>>>>>>> + * the exporter may be freed before TTM's
>>>>>>>>> delayed_delete
>>>>>>>>> +worker
>>>>>>>>> + * runs. Switch to the BO's own resv to prevent
>>>>>>>>> a UAF
>>>>>>>>> +when
>>>>>>>>> + * ttm_bo_delayed_delete() tries to lock the
>>>>>>>>> stale pointer.
>>>>>>>>> + */
>>>>>>>>> + obj->resv = &obj->_resv;
>>>>>>>>
>>>>>>>> +Christian, does amdgpu not have the type of same issue
>>>>>>>> here? Also
>>>>>>>> +any
>>>>> thoughts here?
>>>>>>>
>>>>>>> Oh, good catch. Yeah I think we have the same problem on
>>>>>>> amdgpu as well.
>>>>>>
>>>>>> Maybe dumb question, but why does the
>>>>>> ttm_bo_individualize_resv()
>>>>>> skip the
>>>>> final switch of the resv for type_sg?
>>>>>
>>>>> Because we need the original resv object for cleaning up the
>>>>> mapping
>>>>> should the initial attach and then map have succeed.
>>>>>
>>>>>> It goes through the trouble of copying the fences across?
>>>>>
>>>>> Because we need to know when the import can be cleaned up.
>>>>>
>>>>> In other words TTM takes a copy of the current fences and only
>>>>> unmap,
>>>>> detach and then do the final cleanup after we are sure that the
>>>>> set of
>>>>> fences which was active on destruction is now signaled.
>>>>>
>>>>> If new fences are added to the resv object (maybe by the exporter
>>>>> itself or other
>>>>> importers) after our reference count got down to zero then we
>>>>> don't
>>>>> care about that.
>>>>>> If we do need to handle this here, do we also need to grab the
>>>>>> lru
>>>>>> lock, like we
>>>>> do in ttm_bo_individualize_resv() when doing the swap?
>>>>>
>>>>> Good question, of hand I would say yes but I clearly need to
>>>>> check the
>>>>> source code as well.
>>>>>
>>>>> Might be better to switch the type of the BO on error so that the
>>>>> normal cleanup will just switch over to the local dma_resv
>>>>> object.
>>>>>
>>>>
>>>> - obj->resv = &obj->_resv;
>>>> + gem_to_xe_bo(obj)->ttm.type = ttm_bo_type_kernel;
>>>>
>>>> Switching the type to ttm_bo_type_kernel lets
>>>> ttm_bo_individualize_resv() swap
>>>> resv to the BO's private _resv under lru_lock, which prevents UAF
>>>> without
>>>> needing any manual locking.
>>
>> The lru lock is IIRC only needed and safe when the ttm refcount is zero
>> (in the TTM destruction path) to protect against a racing LRU walk
>> trylock succeeds against the incorrect resv.
>>
>> I wonder whether this was actually why xe code initially took care not
>> to publish the bo on the LRUs until the attachment succeeded.
>>
>> A TTM LRU walker may pick up the exporting resv as soon as the resource
>> is published on the LRU, and then try to lock it using
>> ttm_lru_walk_ticketlock(). The lru lock doesn't protect against that.
>> So we have a sort of moment22, since with that approach move_notify()
>> could be called without the bo being fully initialized.
>>
>> One way to move forward would perhaps be to, for now, reinstate that
>> and have move_notify check if the bo is a stub or fully initialized
>> before doing anything.
>>
>> Also perhaps we should in the future consider allowing dma-buf
>> attachment removal under a separate lower-level lock than the resv.
>
> Is it plausible to check for drm_gem_is_imported() in ttm_bo_individualize_resv()? If sg && !imported then it should be safe to swap out the resv?
That's also a solution which came to my mind. We should probably completely stop checking for ttm_bo_type_sg there.
Regards,
Christian.
>
>>
>> Thanks,
>> Thomas
>>
>>
>>>
>>> Checked all bo->type readers (xe_evict_flags(), xe_bo_move(),
>>> xe_bo_can_migrate()) and found they can be called concurrently by the
>>> shrinker or eviction paths without any synchronization, making the
>>> bo->type change unsafe.
>>>
>>> Switching resv to &obj->_resv under lru_lock, mirroring
>>> ttm_bo_individualize_resv(), is the more reasonable.
>>> I'll send this as v2, along with a separate patch fixing the same
>>> issue in amdgpu.
>>>
>>> - Nitin
>>>
>>>>> Since we don't need the original dma_resv for the cleanup that
>>>>> should work
>>>> fine.
>>>>>
>>>>>> Ideally xe and amdgpu can just have identical solutions here.
>>>>>
>>>>> Yeah completely agree.
>>>>>
>>>>> Regards,
>>>>> Christian.
>>>>>
>>>>>>
>>>>>>>
>>>>>>> How the heck did you found that? Do we have a dummy driver
>>>>>>> (VGEM?)
>>>>>>> which
>>>>> could be made to always fail attachment for a test case?
>>>>
>>>> The bug was found via the existing KUnit test (xe_dma_buf_kunit),
>>>> which was
>>>> failing on a BMG VM device. The test runs 20 parameter
>>>> combinations.
>>>> the failing ones use force_different_devices=true +
>>>> mem_mask=XE_BO_FLAG_VRAM0 + nop2p_attach_ops, where
>>>> dma_buf_dynamic_attach() returns -EOPNOTSUPP, hitting the error
>>>> path.
>>>>
>>>> On bare metal BMG the race window is too narrow to hit the issue.
>>>> To make it
>>>> more deterministic, added a small msleep(100) in
>>>> ttm_bo_delayed_delete() just
>>>> before the dma_resv_lock() call, which widened the race window.
>>>> With KASAN enabled, that gave a clear slab-use-after-free in
>>>> __ww_mutex_lock
>>>> — the 0x6b6b6b6b SLUB poison pattern in the faulting address
>>>> confirmed the
>>>> UAF.
>>>>
>>>> Thanks,
>>>> Nitin
>>>>
>>>>>>>
>>>>>>> @Vitaly can you take a look and try to come up with a test
>>>>>>> case for that?
>>>>> Thanks in advance.
>>>>>>>
>>>>>>> Thanks for the notice,
>>>>>>> Christian.
>>>>>>>
>>>>>>>>
>>>>>>>>> xe_bo_put(gem_to_xe_bo(obj));
>>>>>>>>> return ERR_CAST(attach);
>>>>>>>>> }
>>>>>>>>
>>>>>>>
>>>>>>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-04 11:14 ` Thomas Hellström
2026-06-04 11:32 ` Matthew Auld
@ 2026-06-05 10:11 ` Christian König
1 sibling, 0 replies; 15+ messages in thread
From: Christian König @ 2026-06-05 10:11 UTC (permalink / raw)
To: Thomas Hellström, Gote, Nitin R, Auld, Matthew,
intel-xe@lists.freedesktop.org, Christian König
Cc: stable@vger.kernel.org, Brost, Matthew, Prosyak, Vitaly
On 6/4/26 13:14, Thomas Hellström wrote:
> Hi,
>
> On Thu, 2026-06-04 at 04:54 +0000, Gote, Nitin R wrote:
>> Hi,
>>
>>> -----Original Message-----
>>> From: Intel-xe <intel-xe-bounces@lists.freedesktop.org> On Behalf
>>> Of Gote, Nitin
>>> R
>>> Sent: Monday, June 1, 2026 8:57 PM
>>> To: Christian König <christian.koenig@amd.com>; Auld, Matthew
>>> <matthew.auld@intel.com>; intel-xe@lists.freedesktop.org; Christian
>>> König
>>> <ckoenig.leichtzumerken@gmail.com>
>>> Cc: stable@vger.kernel.org; Thomas Hellstrom
>>> <thomas.hellstrom@linux.intel.com>; Brost, Matthew
>>> <matthew.brost@intel.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
>>> Subject: RE: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on
>>> attach failure
>>>
>>> Hi Christian,
>>>
>>>> -----Original Message-----
>>>> From: Christian König <christian.koenig@amd.com>
>>>> Sent: Monday, June 1, 2026 5:47 PM
>>>> To: Auld, Matthew <matthew.auld@intel.com>; Gote, Nitin R
>>>> <nitin.r.gote@intel.com>; intel-xe@lists.freedesktop.org;
>>>> Christian
>>>> König <ckoenig.leichtzumerken@gmail.com>
>>>> Cc: stable@vger.kernel.org; Thomas Hellstrom
>>>> <thomas.hellstrom@linux.intel.com>; Brost, Matthew
>>>> <matthew.brost@intel.com>; Prosyak, Vitaly
>>>> <Vitaly.Prosyak@amd.com>
>>>> Subject: Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on
>>>> attach failure
>>>>
>>>> On 6/1/26 14:01, Matthew Auld wrote:
>>>>> On 01/06/2026 12:39, Christian König wrote:
>>>>>>
>>>>>>
>>>>>> On 6/1/26 12:46, Matthew Auld wrote:
>>>>>>> On 01/06/2026 11:15, Nitin Gote wrote:
>>>>>>>> xe_dma_buf_create_obj() creates the importer BO with obj-
>>>>>>>>> resv
>>>>>>>> pointing at the exporter's dma_buf->resv. When
>>>>>>>> dma_buf_dynamic_attach() fails, no dma_buf reference is
>>>>>>>> held so
>>>>>>>> the exporter can be freed immediately. Since
>>>>>>>> ttm_bo_release() now
>>>>>>>> always defers cleanup for ttm_bo_type_sg BOs to the TTM
>>>>>>>> workqueue, the worker later calls
>>>>>>>> dma_resv_lock() on the already-freed exporter resv,
>>>>>>>> causing a UAF.
>>>>>>>>
>>>>>>>> Reset obj->resv to the BO's private _resv before calling
>>>>>>>> xe_bo_put() in the error path. The BO is not yet
>>>>>>>> published
>>>>>>>> (attach
>>>>>>>> failed) and carries no fences, so the switch is safe.
>>>>>>>>
>>>>>>>> Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG
>>>>>>>> (QEMU):
>>>>>>>>
>>>>>>>> Oops: general protection fault, probably for non-
>>>>>>>> canonical
>>>>>>>> address 0x6b6b6b6b6b6b6b9c
>>>>>>>> Workqueue: ttm ttm_bo_delayed_delete [ttm]
>>>>>>>> RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
>>>>>>>> Call Trace:
>>>>>>>> <TASK>
>>>>>>>> ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
>>>>>>>> ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
>>>>>>>> ww_mutex_lock+0x3c/0xb0
>>>>>>>> ttm_bo_delayed_delete+0x41/0xc0 [ttm]
>>>>>>>> process_one_work+0x239/0x740
>>>>>>>> worker_thread+0x200/0x3f0
>>>>>>>> kthread+0x10d/0x150
>>>>>>>> ret_from_fork+0x3bd/0x470
>>>>>>>> ret_from_fork_asm+0x1a/0x30
>>>>>>>> </TASK>
>>>>>>>>
>>>>>>>> Closes:
>>>>>>>> https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
>>>>>>>> Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed
>>>>>>>> cleanup
>>>>>>>> path for imported bos")
>>>>>>>> Cc: stable@vger.kernel.org # v6.8+
>>>>>>>> Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
>>>>>>>> Cc: Matthew Brost <matthew.brost@intel.com>
>>>>>>>> Cc: Matthew Auld <matthew.auld@intel.com>
>>>>>>>> Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
>>>>>>>> ---
>>>>>>>> drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
>>>>>>>> 1 file changed, 8 insertions(+)
>>>>>>>>
>>>>>>>> diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c
>>>>>>>> b/drivers/gpu/drm/xe/xe_dma_buf.c index
>>>>>>>> 8a920e58245c..6d944bd4065c
>>>>>>>> 100644
>>>>>>>> --- a/drivers/gpu/drm/xe/xe_dma_buf.c
>>>>>>>> +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
>>>>>>>> @@ -384,6 +384,14 @@ struct drm_gem_object
>>>>>>>> *xe_gem_prime_import(struct drm_device *dev,
>>>>>>>> attach = dma_buf_dynamic_attach(dma_buf, dev-
>>>>>>>>> dev,
>>>>>>>> attach_ops, obj);
>>>>>>>> if (IS_ERR(attach)) {
>>>>>>>> + /*
>>>>>>>> + * The BO was created with resv = dma_buf->resv
>>>>>>>> +(exporter's
>>>>>>>> + * resv). Since attach failed, no dma_buf
>>>>>>>> reference is
>>>>>>>> +held and
>>>>>>>> + * the exporter may be freed before TTM's
>>>>>>>> delayed_delete
>>>>>>>> +worker
>>>>>>>> + * runs. Switch to the BO's own resv to prevent
>>>>>>>> a UAF
>>>>>>>> +when
>>>>>>>> + * ttm_bo_delayed_delete() tries to lock the
>>>>>>>> stale pointer.
>>>>>>>> + */
>>>>>>>> + obj->resv = &obj->_resv;
>>>>>>>
>>>>>>> +Christian, does amdgpu not have the type of same issue
>>>>>>> here? Also
>>>>>>> +any
>>>> thoughts here?
>>>>>>
>>>>>> Oh, good catch. Yeah I think we have the same problem on
>>>>>> amdgpu as well.
>>>>>
>>>>> Maybe dumb question, but why does the
>>>>> ttm_bo_individualize_resv()
>>>>> skip the
>>>> final switch of the resv for type_sg?
>>>>
>>>> Because we need the original resv object for cleaning up the
>>>> mapping
>>>> should the initial attach and then map have succeed.
>>>>
>>>>> It goes through the trouble of copying the fences across?
>>>>
>>>> Because we need to know when the import can be cleaned up.
>>>>
>>>> In other words TTM takes a copy of the current fences and only
>>>> unmap,
>>>> detach and then do the final cleanup after we are sure that the
>>>> set of
>>>> fences which was active on destruction is now signaled.
>>>>
>>>> If new fences are added to the resv object (maybe by the exporter
>>>> itself or other
>>>> importers) after our reference count got down to zero then we
>>>> don't
>>>> care about that.
>>>>> If we do need to handle this here, do we also need to grab the
>>>>> lru
>>>>> lock, like we
>>>> do in ttm_bo_individualize_resv() when doing the swap?
>>>>
>>>> Good question, of hand I would say yes but I clearly need to
>>>> check the
>>>> source code as well.
>>>>
>>>> Might be better to switch the type of the BO on error so that the
>>>> normal cleanup will just switch over to the local dma_resv
>>>> object.
>>>>
>>>
>>> - obj->resv = &obj->_resv;
>>> + gem_to_xe_bo(obj)->ttm.type = ttm_bo_type_kernel;
>>>
>>> Switching the type to ttm_bo_type_kernel lets
>>> ttm_bo_individualize_resv() swap
>>> resv to the BO's private _resv under lru_lock, which prevents UAF
>>> without
>>> needing any manual locking.
>
> The lru lock is IIRC only needed and safe when the ttm refcount is zero
> (in the TTM destruction path) to protect against a racing LRU walk
> trylock succeeds against the incorrect resv.
>
> I wonder whether this was actually why xe code initially took care not
> to publish the bo on the LRUs until the attachment succeeded.
I think that having a resource object attached to the BO before we are done with the import is a bad idea.
IIRC amdgpu_gem_object_create() does exactly that. So bo->resource should be NULL.
We should also use the ttm.type = ttm_bo_type_kernel and only switch to ttm_bo_type_sg after the attachment suceeded.
Regards,
Christian.
>
> A TTM LRU walker may pick up the exporting resv as soon as the resource
> is published on the LRU, and then try to lock it using
> ttm_lru_walk_ticketlock(). The lru lock doesn't protect against that.
>
> So we have a sort of moment22, since with that approach move_notify()
> could be called without the bo being fully initialized.
>
> One way to move forward would perhaps be to, for now, reinstate that
> and have move_notify check if the bo is a stub or fully initialized
> before doing anything.
>
> Also perhaps we should in the future consider allowing dma-buf
> attachment removal under a separate lower-level lock than the resv.
>
> Thanks,
> Thomas
>
>
>>
>> Checked all bo->type readers (xe_evict_flags(), xe_bo_move(),
>> xe_bo_can_migrate()) and found they can be called concurrently by the
>> shrinker or eviction paths without any synchronization, making the
>> bo->type change unsafe.
>>
>> Switching resv to &obj->_resv under lru_lock, mirroring
>> ttm_bo_individualize_resv(), is the more reasonable.
>> I'll send this as v2, along with a separate patch fixing the same
>> issue in amdgpu.
>>
>> - Nitin
>>
>>>> Since we don't need the original dma_resv for the cleanup that
>>>> should work
>>> fine.
>>>>
>>>>> Ideally xe and amdgpu can just have identical solutions here.
>>>>
>>>> Yeah completely agree.
>>>>
>>>> Regards,
>>>> Christian.
>>>>
>>>>>
>>>>>>
>>>>>> How the heck did you found that? Do we have a dummy driver
>>>>>> (VGEM?)
>>>>>> which
>>>> could be made to always fail attachment for a test case?
>>>
>>> The bug was found via the existing KUnit test (xe_dma_buf_kunit),
>>> which was
>>> failing on a BMG VM device. The test runs 20 parameter
>>> combinations.
>>> the failing ones use force_different_devices=true +
>>> mem_mask=XE_BO_FLAG_VRAM0 + nop2p_attach_ops, where
>>> dma_buf_dynamic_attach() returns -EOPNOTSUPP, hitting the error
>>> path.
>>>
>>> On bare metal BMG the race window is too narrow to hit the issue.
>>> To make it
>>> more deterministic, added a small msleep(100) in
>>> ttm_bo_delayed_delete() just
>>> before the dma_resv_lock() call, which widened the race window.
>>> With KASAN enabled, that gave a clear slab-use-after-free in
>>> __ww_mutex_lock
>>> — the 0x6b6b6b6b SLUB poison pattern in the faulting address
>>> confirmed the
>>> UAF.
>>>
>>> Thanks,
>>> Nitin
>>>
>>>>>>
>>>>>> @Vitaly can you take a look and try to come up with a test
>>>>>> case for that?
>>>> Thanks in advance.
>>>>>>
>>>>>> Thanks for the notice,
>>>>>> Christian.
>>>>>>
>>>>>>>
>>>>>>>> xe_bo_put(gem_to_xe_bo(obj));
>>>>>>>> return ERR_CAST(attach);
>>>>>>>> }
>>>>>>>
>>>>>>
>>>>>
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-05 9:56 ` Christian König
@ 2026-06-11 13:04 ` Gote, Nitin R
2026-06-11 14:06 ` Thomas Hellström
0 siblings, 1 reply; 15+ messages in thread
From: Gote, Nitin R @ 2026-06-11 13:04 UTC (permalink / raw)
To: Christian König, Auld, Matthew, Thomas Hellström,
intel-xe@lists.freedesktop.org, Christian König
Cc: stable@vger.kernel.org, Brost, Matthew, Prosyak, Vitaly
Hi,
> -----Original Message-----
> From: Christian König <christian.koenig@amd.com>
> Sent: Friday, June 5, 2026 3:26 PM
> To: Auld, Matthew <matthew.auld@intel.com>; Thomas Hellström
> <thomas.hellstrom@linux.intel.com>; Gote, Nitin R <nitin.r.gote@intel.com>;
> intel-xe@lists.freedesktop.org; Christian König
> <ckoenig.leichtzumerken@gmail.com>
> Cc: stable@vger.kernel.org; Brost, Matthew <matthew.brost@intel.com>;
> Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
> Subject: Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
>
> On 6/4/26 13:32, Matthew Auld wrote:
> > On 04/06/2026 12:14, Thomas Hellström wrote:
> >> Hi,
> >>
> >> On Thu, 2026-06-04 at 04:54 +0000, Gote, Nitin R wrote:
> >>> Hi,
> >>>
> >>>> -----Original Message-----
> >>>> From: Intel-xe <intel-xe-bounces@lists.freedesktop.org> On Behalf
> >>>> Of Gote, Nitin R
> >>>> Sent: Monday, June 1, 2026 8:57 PM
> >>>> To: Christian König <christian.koenig@amd.com>; Auld, Matthew
> >>>> <matthew.auld@intel.com>; intel-xe@lists.freedesktop.org; Christian
> >>>> König <ckoenig.leichtzumerken@gmail.com>
> >>>> Cc: stable@vger.kernel.org; Thomas Hellstrom
> >>>> <thomas.hellstrom@linux.intel.com>; Brost, Matthew
> >>>> <matthew.brost@intel.com>; Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
> >>>> Subject: RE: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on
> >>>> attach failure
> >>>>
> >>>> Hi Christian,
> >>>>
> >>>>> -----Original Message-----
> >>>>> From: Christian König <christian.koenig@amd.com>
> >>>>> Sent: Monday, June 1, 2026 5:47 PM
> >>>>> To: Auld, Matthew <matthew.auld@intel.com>; Gote, Nitin R
> >>>>> <nitin.r.gote@intel.com>; intel-xe@lists.freedesktop.org;
> >>>>> Christian König <ckoenig.leichtzumerken@gmail.com>
> >>>>> Cc: stable@vger.kernel.org; Thomas Hellstrom
> >>>>> <thomas.hellstrom@linux.intel.com>; Brost, Matthew
> >>>>> <matthew.brost@intel.com>; Prosyak, Vitaly
> >>>>> <Vitaly.Prosyak@amd.com>
> >>>>> Subject: Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on
> >>>>> attach failure
> >>>>>
> >>>>> On 6/1/26 14:01, Matthew Auld wrote:
> >>>>>> On 01/06/2026 12:39, Christian König wrote:
> >>>>>>>
> >>>>>>>
> >>>>>>> On 6/1/26 12:46, Matthew Auld wrote:
> >>>>>>>> On 01/06/2026 11:15, Nitin Gote wrote:
> >>>>>>>>> xe_dma_buf_create_obj() creates the importer BO with obj-
> >>>>>>>>>> resv
> >>>>>>>>> pointing at the exporter's dma_buf->resv. When
> >>>>>>>>> dma_buf_dynamic_attach() fails, no dma_buf reference is held
> >>>>>>>>> so the exporter can be freed immediately. Since
> >>>>>>>>> ttm_bo_release() now
> >>>>>>>>> always defers cleanup for ttm_bo_type_sg BOs to the TTM
> >>>>>>>>> workqueue, the worker later calls
> >>>>>>>>> dma_resv_lock() on the already-freed exporter resv, causing a
> >>>>>>>>> UAF.
> >>>>>>>>>
> >>>>>>>>> Reset obj->resv to the BO's private _resv before calling
> >>>>>>>>> xe_bo_put() in the error path. The BO is not yet published
> >>>>>>>>> (attach
> >>>>>>>>> failed) and carries no fences, so the switch is safe.
> >>>>>>>>>
> >>>>>>>>> Observed with igt@xe_live_ktest@xe_dma_buf_kunit on BMG
> >>>>>>>>> (QEMU):
> >>>>>>>>>
> >>>>>>>>> Oops: general protection fault, probably for non-
> >>>>>>>>> canonical address 0x6b6b6b6b6b6b6b9c
> >>>>>>>>> Workqueue: ttm ttm_bo_delayed_delete [ttm]
> >>>>>>>>> RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
> >>>>>>>>> Call Trace:
> >>>>>>>>> <TASK>
> >>>>>>>>> ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
> >>>>>>>>> ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> >>>>>>>>> ww_mutex_lock+0x3c/0xb0
> >>>>>>>>> ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> >>>>>>>>> process_one_work+0x239/0x740
> >>>>>>>>> worker_thread+0x200/0x3f0
> >>>>>>>>> kthread+0x10d/0x150
> >>>>>>>>> ret_from_fork+0x3bd/0x470
> >>>>>>>>> ret_from_fork_asm+0x1a/0x30
> >>>>>>>>> </TASK>
> >>>>>>>>>
> >>>>>>>>> Closes:
> >>>>>>>>> https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
> >>>>>>>>> Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo delayed
> >>>>>>>>> cleanup path for imported bos")
> >>>>>>>>> Cc: stable@vger.kernel.org # v6.8+
> >>>>>>>>> Cc: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
> >>>>>>>>> Cc: Matthew Brost <matthew.brost@intel.com>
> >>>>>>>>> Cc: Matthew Auld <matthew.auld@intel.com>
> >>>>>>>>> Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
> >>>>>>>>> ---
> >>>>>>>>> drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
> >>>>>>>>> 1 file changed, 8 insertions(+)
> >>>>>>>>>
> >>>>>>>>> diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c
> >>>>>>>>> b/drivers/gpu/drm/xe/xe_dma_buf.c index
> >>>>>>>>> 8a920e58245c..6d944bd4065c
> >>>>>>>>> 100644
> >>>>>>>>> --- a/drivers/gpu/drm/xe/xe_dma_buf.c
> >>>>>>>>> +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
> >>>>>>>>> @@ -384,6 +384,14 @@ struct drm_gem_object
> >>>>>>>>> *xe_gem_prime_import(struct drm_device *dev,
> >>>>>>>>> attach = dma_buf_dynamic_attach(dma_buf, dev-
> >>>>>>>>>> dev,
> >>>>>>>>> attach_ops, obj);
> >>>>>>>>> if (IS_ERR(attach)) {
> >>>>>>>>> + /*
> >>>>>>>>> + * The BO was created with resv = dma_buf->resv
> >>>>>>>>> +(exporter's
> >>>>>>>>> + * resv). Since attach failed, no dma_buf
> >>>>>>>>> reference is
> >>>>>>>>> +held and
> >>>>>>>>> + * the exporter may be freed before TTM's
> >>>>>>>>> delayed_delete
> >>>>>>>>> +worker
> >>>>>>>>> + * runs. Switch to the BO's own resv to prevent
> >>>>>>>>> a UAF
> >>>>>>>>> +when
> >>>>>>>>> + * ttm_bo_delayed_delete() tries to lock the
> >>>>>>>>> stale pointer.
> >>>>>>>>> + */
> >>>>>>>>> + obj->resv = &obj->_resv;
> >>>>>>>>
> >>>>>>>> +Christian, does amdgpu not have the type of same issue
> >>>>>>>> here? Also
> >>>>>>>> +any
> >>>>> thoughts here?
> >>>>>>>
> >>>>>>> Oh, good catch. Yeah I think we have the same problem on amdgpu
> >>>>>>> as well.
> >>>>>>
> >>>>>> Maybe dumb question, but why does the
> >>>>>> ttm_bo_individualize_resv()
> >>>>>> skip the
> >>>>> final switch of the resv for type_sg?
> >>>>>
> >>>>> Because we need the original resv object for cleaning up the
> >>>>> mapping should the initial attach and then map have succeed.
> >>>>>
> >>>>>> It goes through the trouble of copying the fences across?
> >>>>>
> >>>>> Because we need to know when the import can be cleaned up.
> >>>>>
> >>>>> In other words TTM takes a copy of the current fences and only
> >>>>> unmap, detach and then do the final cleanup after we are sure that
> >>>>> the set of fences which was active on destruction is now signaled.
> >>>>>
> >>>>> If new fences are added to the resv object (maybe by the exporter
> >>>>> itself or other
> >>>>> importers) after our reference count got down to zero then we
> >>>>> don't care about that.
> >>>>>> If we do need to handle this here, do we also need to grab the
> >>>>>> lru lock, like we
> >>>>> do in ttm_bo_individualize_resv() when doing the swap?
> >>>>>
> >>>>> Good question, of hand I would say yes but I clearly need to
> >>>>> check the
> >>>>> source code as well.
> >>>>>
> >>>>> Might be better to switch the type of the BO on error so that the
> >>>>> normal cleanup will just switch over to the local dma_resv
> >>>>> object.
> >>>>>
> >>>>
> >>>> - obj->resv = &obj->_resv;
> >>>> + gem_to_xe_bo(obj)->ttm.type = ttm_bo_type_kernel;
> >>>>
> >>>> Switching the type to ttm_bo_type_kernel lets
> >>>> ttm_bo_individualize_resv() swap
> >>>> resv to the BO's private _resv under lru_lock, which prevents UAF
> >>>> without
> >>>> needing any manual locking.
> >>
> >> The lru lock is IIRC only needed and safe when the ttm refcount is zero
> >> (in the TTM destruction path) to protect against a racing LRU walk
> >> trylock succeeds against the incorrect resv.
> >>
> >> I wonder whether this was actually why xe code initially took care not
> >> to publish the bo on the LRUs until the attachment succeeded.
> >>
> >> A TTM LRU walker may pick up the exporting resv as soon as the resource
> >> is published on the LRU, and then try to lock it using
> >> ttm_lru_walk_ticketlock(). The lru lock doesn't protect against that.
> >> So we have a sort of moment22, since with that approach move_notify()
> >> could be called without the bo being fully initialized.
> >>
> >> One way to move forward would perhaps be to, for now, reinstate that
> >> and have move_notify check if the bo is a stub or fully initialized
> >> before doing anything.
> >>
> >> Also perhaps we should in the future consider allowing dma-buf
> >> attachment removal under a separate lower-level lock than the resv.
> >
> > Is it plausible to check for drm_gem_is_imported() in
> ttm_bo_individualize_resv()? If sg && !imported then it should be safe to swap
> out the resv?
>
> That's also a solution which came to my mind. We should probably completely
> stop checking for ttm_bo_type_sg there.
>
Thank you, Matt, Christian and Thomas for the direction
Will implement this as:
1. Remove the if (bo->type != ttm_bo_type_sg) guard in ttm_bo_individualize_resv() entirely — at refcount==0 fences are already copied, the swap is safe for all types.
2. In xe and amdgpu:
Will create import BO as ttm_bo_type_kernel with private resv; switch to ttm_bo_type_sg + dma_buf->resv under lru_lock only after successful attach.
This also eliminates the dummy_obj + drm_exec block in xe_dma_buf_create_obj().
Thanks,
Nitin
> Regards,
> Christian.
>
> >
> >>
> >> Thanks,
> >> Thomas
> >>
> >>
> >>>
> >>> Checked all bo->type readers (xe_evict_flags(), xe_bo_move(),
> >>> xe_bo_can_migrate()) and found they can be called concurrently by the
> >>> shrinker or eviction paths without any synchronization, making the
> >>> bo->type change unsafe.
> >>>
> >>> Switching resv to &obj->_resv under lru_lock, mirroring
> >>> ttm_bo_individualize_resv(), is the more reasonable.
> >>> I'll send this as v2, along with a separate patch fixing the same
> >>> issue in amdgpu.
> >>>
> >>> - Nitin
> >>>
> >>>>> Since we don't need the original dma_resv for the cleanup that
> >>>>> should work
> >>>> fine.
> >>>>>
> >>>>>> Ideally xe and amdgpu can just have identical solutions here.
> >>>>>
> >>>>> Yeah completely agree.
> >>>>>
> >>>>> Regards,
> >>>>> Christian.
> >>>>>
> >>>>>>
> >>>>>>>
> >>>>>>> How the heck did you found that? Do we have a dummy driver
> >>>>>>> (VGEM?)
> >>>>>>> which
> >>>>> could be made to always fail attachment for a test case?
> >>>>
> >>>> The bug was found via the existing KUnit test (xe_dma_buf_kunit),
> >>>> which was
> >>>> failing on a BMG VM device. The test runs 20 parameter
> >>>> combinations.
> >>>> the failing ones use force_different_devices=true +
> >>>> mem_mask=XE_BO_FLAG_VRAM0 + nop2p_attach_ops, where
> >>>> dma_buf_dynamic_attach() returns -EOPNOTSUPP, hitting the error
> >>>> path.
> >>>>
> >>>> On bare metal BMG the race window is too narrow to hit the issue.
> >>>> To make it
> >>>> more deterministic, added a small msleep(100) in
> >>>> ttm_bo_delayed_delete() just
> >>>> before the dma_resv_lock() call, which widened the race window.
> >>>> With KASAN enabled, that gave a clear slab-use-after-free in
> >>>> __ww_mutex_lock
> >>>> — the 0x6b6b6b6b SLUB poison pattern in the faulting address
> >>>> confirmed the
> >>>> UAF.
> >>>>
> >>>> Thanks,
> >>>> Nitin
> >>>>
> >>>>>>>
> >>>>>>> @Vitaly can you take a look and try to come up with a test
> >>>>>>> case for that?
> >>>>> Thanks in advance.
> >>>>>>>
> >>>>>>> Thanks for the notice,
> >>>>>>> Christian.
> >>>>>>>
> >>>>>>>>
> >>>>>>>>> xe_bo_put(gem_to_xe_bo(obj));
> >>>>>>>>> return ERR_CAST(attach);
> >>>>>>>>> }
> >>>>>>>>
> >>>>>>>
> >>>>>>
> >
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure
2026-06-11 13:04 ` Gote, Nitin R
@ 2026-06-11 14:06 ` Thomas Hellström
0 siblings, 0 replies; 15+ messages in thread
From: Thomas Hellström @ 2026-06-11 14:06 UTC (permalink / raw)
To: Gote, Nitin R, Christian König, Auld, Matthew,
intel-xe@lists.freedesktop.org, Christian König
Cc: stable@vger.kernel.org, Brost, Matthew, Prosyak, Vitaly
Hi, Nitin,
On Thu, 2026-06-11 at 13:04 +0000, Gote, Nitin R wrote:
> Hi,
>
> > -----Original Message-----
> > From: Christian König <christian.koenig@amd.com>
> > Sent: Friday, June 5, 2026 3:26 PM
> > To: Auld, Matthew <matthew.auld@intel.com>; Thomas Hellström
> > <thomas.hellstrom@linux.intel.com>; Gote, Nitin R
> > <nitin.r.gote@intel.com>;
> > intel-xe@lists.freedesktop.org; Christian König
> > <ckoenig.leichtzumerken@gmail.com>
> > Cc: stable@vger.kernel.org; Brost, Matthew
> > <matthew.brost@intel.com>;
> > Prosyak, Vitaly <Vitaly.Prosyak@amd.com>
> > Subject: Re: [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on
> > attach failure
> >
> > On 6/4/26 13:32, Matthew Auld wrote:
> > > On 04/06/2026 12:14, Thomas Hellström wrote:
> > > > Hi,
> > > >
> > > > On Thu, 2026-06-04 at 04:54 +0000, Gote, Nitin R wrote:
> > > > > Hi,
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Intel-xe <intel-xe-bounces@lists.freedesktop.org> On
> > > > > > Behalf
> > > > > > Of Gote, Nitin R
> > > > > > Sent: Monday, June 1, 2026 8:57 PM
> > > > > > To: Christian König <christian.koenig@amd.com>; Auld,
> > > > > > Matthew
> > > > > > <matthew.auld@intel.com>; intel-xe@lists.freedesktop.org;
> > > > > > Christian
> > > > > > König <ckoenig.leichtzumerken@gmail.com>
> > > > > > Cc: stable@vger.kernel.org; Thomas Hellstrom
> > > > > > <thomas.hellstrom@linux.intel.com>; Brost, Matthew
> > > > > > <matthew.brost@intel.com>; Prosyak, Vitaly
> > > > > > <Vitaly.Prosyak@amd.com>
> > > > > > Subject: RE: [PATCH] drm/xe: Fix UAF in
> > > > > > xe_gem_prime_import() on
> > > > > > attach failure
> > > > > >
> > > > > > Hi Christian,
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: Christian König <christian.koenig@amd.com>
> > > > > > > Sent: Monday, June 1, 2026 5:47 PM
> > > > > > > To: Auld, Matthew <matthew.auld@intel.com>; Gote, Nitin R
> > > > > > > <nitin.r.gote@intel.com>; intel-xe@lists.freedesktop.org;
> > > > > > > Christian König <ckoenig.leichtzumerken@gmail.com>
> > > > > > > Cc: stable@vger.kernel.org; Thomas Hellstrom
> > > > > > > <thomas.hellstrom@linux.intel.com>; Brost, Matthew
> > > > > > > <matthew.brost@intel.com>; Prosyak, Vitaly
> > > > > > > <Vitaly.Prosyak@amd.com>
> > > > > > > Subject: Re: [PATCH] drm/xe: Fix UAF in
> > > > > > > xe_gem_prime_import() on
> > > > > > > attach failure
> > > > > > >
> > > > > > > On 6/1/26 14:01, Matthew Auld wrote:
> > > > > > > > On 01/06/2026 12:39, Christian König wrote:
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > On 6/1/26 12:46, Matthew Auld wrote:
> > > > > > > > > > On 01/06/2026 11:15, Nitin Gote wrote:
> > > > > > > > > > > xe_dma_buf_create_obj() creates the importer BO
> > > > > > > > > > > with obj-
> > > > > > > > > > > > resv
> > > > > > > > > > > pointing at the exporter's dma_buf->resv. When
> > > > > > > > > > > dma_buf_dynamic_attach() fails, no dma_buf
> > > > > > > > > > > reference is held
> > > > > > > > > > > so the exporter can be freed immediately. Since
> > > > > > > > > > > ttm_bo_release() now
> > > > > > > > > > > always defers cleanup for ttm_bo_type_sg BOs to
> > > > > > > > > > > the TTM
> > > > > > > > > > > workqueue, the worker later calls
> > > > > > > > > > > dma_resv_lock() on the already-freed exporter
> > > > > > > > > > > resv, causing a
> > > > > > > > > > > UAF.
> > > > > > > > > > >
> > > > > > > > > > > Reset obj->resv to the BO's private _resv before
> > > > > > > > > > > calling
> > > > > > > > > > > xe_bo_put() in the error path. The BO is not yet
> > > > > > > > > > > published
> > > > > > > > > > > (attach
> > > > > > > > > > > failed) and carries no fences, so the switch is
> > > > > > > > > > > safe.
> > > > > > > > > > >
> > > > > > > > > > > Observed with igt@xe_live_ktest@xe_dma_buf_kunit
> > > > > > > > > > > on BMG
> > > > > > > > > > > (QEMU):
> > > > > > > > > > >
> > > > > > > > > > > Oops: general protection fault, probably for
> > > > > > > > > > > non-
> > > > > > > > > > > canonical address 0x6b6b6b6b6b6b6b9c
> > > > > > > > > > > Workqueue: ttm ttm_bo_delayed_delete [ttm]
> > > > > > > > > > > RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0
> > > > > > > > > > > Call Trace:
> > > > > > > > > > > <TASK>
> > > > > > > > > > > ? __ww_mutex_lock.constprop.0+0x2dd/0x18e0
> > > > > > > > > > > ? ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> > > > > > > > > > > ww_mutex_lock+0x3c/0xb0
> > > > > > > > > > > ttm_bo_delayed_delete+0x41/0xc0 [ttm]
> > > > > > > > > > > process_one_work+0x239/0x740
> > > > > > > > > > > worker_thread+0x200/0x3f0
> > > > > > > > > > > kthread+0x10d/0x150
> > > > > > > > > > > ret_from_fork+0x3bd/0x470
> > > > > > > > > > > ret_from_fork_asm+0x1a/0x30
> > > > > > > > > > > </TASK>
> > > > > > > > > > >
> > > > > > > > > > > Closes:
> > > > > > > > > > > https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023
> > > > > > > > > > > Fixes: d99fbd9aab62 ("drm/ttm: Always take the bo
> > > > > > > > > > > delayed
> > > > > > > > > > > cleanup path for imported bos")
> > > > > > > > > > > Cc: stable@vger.kernel.org # v6.8+
> > > > > > > > > > > Cc: Thomas Hellstrom
> > > > > > > > > > > <thomas.hellstrom@linux.intel.com>
> > > > > > > > > > > Cc: Matthew Brost <matthew.brost@intel.com>
> > > > > > > > > > > Cc: Matthew Auld <matthew.auld@intel.com>
> > > > > > > > > > > Signed-off-by: Nitin Gote
> > > > > > > > > > > <nitin.r.gote@intel.com>
> > > > > > > > > > > ---
> > > > > > > > > > > drivers/gpu/drm/xe/xe_dma_buf.c | 8 ++++++++
> > > > > > > > > > > 1 file changed, 8 insertions(+)
> > > > > > > > > > >
> > > > > > > > > > > diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c
> > > > > > > > > > > b/drivers/gpu/drm/xe/xe_dma_buf.c index
> > > > > > > > > > > 8a920e58245c..6d944bd4065c
> > > > > > > > > > > 100644
> > > > > > > > > > > --- a/drivers/gpu/drm/xe/xe_dma_buf.c
> > > > > > > > > > > +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
> > > > > > > > > > > @@ -384,6 +384,14 @@ struct drm_gem_object
> > > > > > > > > > > *xe_gem_prime_import(struct drm_device *dev,
> > > > > > > > > > > attach =
> > > > > > > > > > > dma_buf_dynamic_attach(dma_buf, dev-
> > > > > > > > > > > > dev,
> > > > > > > > > > > attach_ops, obj);
> > > > > > > > > > > if (IS_ERR(attach)) {
> > > > > > > > > > > + /*
> > > > > > > > > > > + * The BO was created with resv =
> > > > > > > > > > > dma_buf->resv
> > > > > > > > > > > +(exporter's
> > > > > > > > > > > + * resv). Since attach failed, no
> > > > > > > > > > > dma_buf
> > > > > > > > > > > reference is
> > > > > > > > > > > +held and
> > > > > > > > > > > + * the exporter may be freed before
> > > > > > > > > > > TTM's
> > > > > > > > > > > delayed_delete
> > > > > > > > > > > +worker
> > > > > > > > > > > + * runs. Switch to the BO's own resv to
> > > > > > > > > > > prevent
> > > > > > > > > > > a UAF
> > > > > > > > > > > +when
> > > > > > > > > > > + * ttm_bo_delayed_delete() tries to lock
> > > > > > > > > > > the
> > > > > > > > > > > stale pointer.
> > > > > > > > > > > + */
> > > > > > > > > > > + obj->resv = &obj->_resv;
> > > > > > > > > >
> > > > > > > > > > +Christian, does amdgpu not have the type of same
> > > > > > > > > > issue
> > > > > > > > > > here? Also
> > > > > > > > > > +any
> > > > > > > thoughts here?
> > > > > > > > >
> > > > > > > > > Oh, good catch. Yeah I think we have the same problem
> > > > > > > > > on amdgpu
> > > > > > > > > as well.
> > > > > > > >
> > > > > > > > Maybe dumb question, but why does the
> > > > > > > > ttm_bo_individualize_resv()
> > > > > > > > skip the
> > > > > > > final switch of the resv for type_sg?
> > > > > > >
> > > > > > > Because we need the original resv object for cleaning up
> > > > > > > the
> > > > > > > mapping should the initial attach and then map have
> > > > > > > succeed.
> > > > > > >
> > > > > > > > It goes through the trouble of copying the fences
> > > > > > > > across?
> > > > > > >
> > > > > > > Because we need to know when the import can be cleaned
> > > > > > > up.
> > > > > > >
> > > > > > > In other words TTM takes a copy of the current fences and
> > > > > > > only
> > > > > > > unmap, detach and then do the final cleanup after we are
> > > > > > > sure that
> > > > > > > the set of fences which was active on destruction is now
> > > > > > > signaled.
> > > > > > >
> > > > > > > If new fences are added to the resv object (maybe by the
> > > > > > > exporter
> > > > > > > itself or other
> > > > > > > importers) after our reference count got down to zero
> > > > > > > then we
> > > > > > > don't care about that.
> > > > > > > > If we do need to handle this here, do we also need to
> > > > > > > > grab the
> > > > > > > > lru lock, like we
> > > > > > > do in ttm_bo_individualize_resv() when doing the swap?
> > > > > > >
> > > > > > > Good question, of hand I would say yes but I clearly need
> > > > > > > to
> > > > > > > check the
> > > > > > > source code as well.
> > > > > > >
> > > > > > > Might be better to switch the type of the BO on error so
> > > > > > > that the
> > > > > > > normal cleanup will just switch over to the local
> > > > > > > dma_resv
> > > > > > > object.
> > > > > > >
> > > > > >
> > > > > > - obj->resv = &obj->_resv;
> > > > > > + gem_to_xe_bo(obj)->ttm.type =
> > > > > > ttm_bo_type_kernel;
> > > > > >
> > > > > > Switching the type to ttm_bo_type_kernel lets
> > > > > > ttm_bo_individualize_resv() swap
> > > > > > resv to the BO's private _resv under lru_lock, which
> > > > > > prevents UAF
> > > > > > without
> > > > > > needing any manual locking.
> > > >
> > > > The lru lock is IIRC only needed and safe when the ttm refcount
> > > > is zero
> > > > (in the TTM destruction path) to protect against a racing LRU
> > > > walk
> > > > trylock succeeds against the incorrect resv.
> > > >
> > > > I wonder whether this was actually why xe code initially took
> > > > care not
> > > > to publish the bo on the LRUs until the attachment succeeded.
> > > >
> > > > A TTM LRU walker may pick up the exporting resv as soon as the
> > > > resource
> > > > is published on the LRU, and then try to lock it using
> > > > ttm_lru_walk_ticketlock(). The lru lock doesn't protect against
> > > > that.
> > > > So we have a sort of moment22, since with that approach
> > > > move_notify()
> > > > could be called without the bo being fully initialized.
> > > >
> > > > One way to move forward would perhaps be to, for now, reinstate
> > > > that
> > > > and have move_notify check if the bo is a stub or fully
> > > > initialized
> > > > before doing anything.
> > > >
> > > > Also perhaps we should in the future consider allowing dma-buf
> > > > attachment removal under a separate lower-level lock than the
> > > > resv.
> > >
> > > Is it plausible to check for drm_gem_is_imported() in
> > ttm_bo_individualize_resv()? If sg && !imported then it should be
> > safe to swap
> > out the resv?
> >
> > That's also a solution which came to my mind. We should probably
> > completely
> > stop checking for ttm_bo_type_sg there.
> >
>
> Thank you, Matt, Christian and Thomas for the direction
>
> Will implement this as:
>
> 1. Remove the if (bo->type != ttm_bo_type_sg) guard in
> ttm_bo_individualize_resv() entirely — at refcount==0 fences are
> already copied, the swap is safe for all types.
I don't think this is safe. The imported bo needs to lock the shared
resv when it finally releases the attachment. That's why the ttm bo,
destruction path distinguishes between _resv and resv.
> 2. In xe and amdgpu:
> Will create import BO as ttm_bo_type_kernel with private resv;
> switch to ttm_bo_type_sg + dma_buf->resv under lru_lock only after
> successful attach.
Problem is that once the xe object is initialized, it is published on
the LRUs, and a LRU walker may lock the resv. We can't swich resv at
that point. And as said before, the lru_lock is only a safe way of
switching locks if the ttm refcount is zero, that is, it can't be used
outside TTM.
TBH I don't see a safe solution that doesn't involve *not* publishing
the bo on the LRU until the attachment is in place.
That is except for Matt Auld's idea of individualizing if there is no
attachment, but then we need to somehow guarantee that the exporter
stays alive, and that means assuming that ttm_bo_finalize() always
individualizes synchronously and rely on the caller to keep the
exporter alive while we finalize the ttm_bo.
/Thomas
> This also eliminates the dummy_obj + drm_exec block in
> xe_dma_buf_create_obj().
>
> Thanks,
> Nitin
>
> > Regards,
> > Christian.
> >
> > >
> > > >
> > > > Thanks,
> > > > Thomas
> > > >
> > > >
> > > > >
> > > > > Checked all bo->type readers (xe_evict_flags(), xe_bo_move(),
> > > > > xe_bo_can_migrate()) and found they can be called
> > > > > concurrently by the
> > > > > shrinker or eviction paths without any synchronization,
> > > > > making the
> > > > > bo->type change unsafe.
> > > > >
> > > > > Switching resv to &obj->_resv under lru_lock, mirroring
> > > > > ttm_bo_individualize_resv(), is the more reasonable.
> > > > > I'll send this as v2, along with a separate patch fixing the
> > > > > same
> > > > > issue in amdgpu.
> > > > >
> > > > > - Nitin
> > > > >
> > > > > > > Since we don't need the original dma_resv for the cleanup
> > > > > > > that
> > > > > > > should work
> > > > > > fine.
> > > > > > >
> > > > > > > > Ideally xe and amdgpu can just have identical solutions
> > > > > > > > here.
> > > > > > >
> > > > > > > Yeah completely agree.
> > > > > > >
> > > > > > > Regards,
> > > > > > > Christian.
> > > > > > >
> > > > > > > >
> > > > > > > > >
> > > > > > > > > How the heck did you found that? Do we have a dummy
> > > > > > > > > driver
> > > > > > > > > (VGEM?)
> > > > > > > > > which
> > > > > > > could be made to always fail attachment for a test case?
> > > > > >
> > > > > > The bug was found via the existing KUnit test
> > > > > > (xe_dma_buf_kunit),
> > > > > > which was
> > > > > > failing on a BMG VM device. The test runs 20 parameter
> > > > > > combinations.
> > > > > > the failing ones use force_different_devices=true +
> > > > > > mem_mask=XE_BO_FLAG_VRAM0 + nop2p_attach_ops, where
> > > > > > dma_buf_dynamic_attach() returns -EOPNOTSUPP, hitting the
> > > > > > error
> > > > > > path.
> > > > > >
> > > > > > On bare metal BMG the race window is too narrow to hit the
> > > > > > issue.
> > > > > > To make it
> > > > > > more deterministic, added a small msleep(100) in
> > > > > > ttm_bo_delayed_delete() just
> > > > > > before the dma_resv_lock() call, which widened the race
> > > > > > window.
> > > > > > With KASAN enabled, that gave a clear slab-use-after-free
> > > > > > in
> > > > > > __ww_mutex_lock
> > > > > > — the 0x6b6b6b6b SLUB poison pattern in the faulting
> > > > > > address
> > > > > > confirmed the
> > > > > > UAF.
> > > > > >
> > > > > > Thanks,
> > > > > > Nitin
> > > > > >
> > > > > > > > >
> > > > > > > > > @Vitaly can you take a look and try to come up with a
> > > > > > > > > test
> > > > > > > > > case for that?
> > > > > > > Thanks in advance.
> > > > > > > > >
> > > > > > > > > Thanks for the notice,
> > > > > > > > > Christian.
> > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > > xe_bo_put(gem_to_xe_bo(obj));
> > > > > > > > > > > return ERR_CAST(attach);
> > > > > > > > > > > }
> > > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > >
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-06-11 14:07 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-01 10:15 [PATCH] drm/xe: Fix UAF in xe_gem_prime_import() on attach failure Nitin Gote
2026-06-01 10:46 ` Matthew Auld
2026-06-01 11:39 ` Christian König
2026-06-01 12:01 ` Matthew Auld
2026-06-01 12:17 ` Christian König
2026-06-01 15:27 ` Gote, Nitin R
2026-06-04 4:54 ` Gote, Nitin R
2026-06-04 11:14 ` Thomas Hellström
2026-06-04 11:32 ` Matthew Auld
2026-06-04 11:55 ` Thomas Hellström
2026-06-05 9:56 ` Christian König
2026-06-11 13:04 ` Gote, Nitin R
2026-06-11 14:06 ` Thomas Hellström
2026-06-05 10:11 ` Christian König
-- strict thread matches above, loose matches on Subject: below --
2026-06-04 10:23 Nitin Gote
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox