RE: [PATCH] drm/amdgpu: fix NULL pointer deref in amdgpu_vm_handle_moved for BO-less mappings
"Zhang, Jesse(Jie)" <[email protected]> Wed, 5 Aug 2026 10:20:54 +0000
| Newsgroups | org.freedesktop.lists.amd-gfx |
|---|---|
| Message-ID | <DM4PR12MB51520AE0F19083A795F9E7B9E3D32@DM4PR12MB5152.namprd12.prod.outlook.com> |
AMD General > -----Original Message----- > From: Koenig, Christian <[email protected]> > Sent: Wednesday, August 5, 2026 5:31 PM > To: Zhang, Jesse(Jie) <[email protected]>; [email protected] > Cc: Deucher, Alexander <[email protected]> > Subject: Re: [PATCH] drm/amdgpu: fix NULL pointer deref in > amdgpu_vm_handle_moved for BO-less mappings > > On 8/5/26 11:01, Zhang, Jesse(Jie) wrote: > > AMD General > > > >> -----Original Message----- > >> From: Koenig, Christian <[email protected]> > >> Sent: Wednesday, August 5, 2026 3:53 PM > >> To: Zhang, Jesse(Jie) <[email protected]>; > >> [email protected] > >> Cc: Deucher, Alexander <[email protected]> > >> Subject: Re: [PATCH] drm/amdgpu: fix NULL pointer deref in > >> amdgpu_vm_handle_moved for BO-less mappings > >> > >> On 8/5/26 05:00, Zhang, Jesse(Jie) wrote: > >>> AMD General > >>> > >>>> -----Original Message----- > >>>> From: Koenig, Christian <[email protected]> > >>>> Sent: Tuesday, August 4, 2026 9:58 PM > >>>> To: Zhang, Jesse(Jie) <[email protected]>; > >>>> [email protected] > >>>> Cc: Deucher, Alexander <[email protected]> > >>>> Subject: Re: [PATCH] drm/amdgpu: fix NULL pointer deref in > >>>> amdgpu_vm_handle_moved for BO-less mappings > >>>> > >>>> On 7/27/26 10:40, Jesse Zhang wrote: > >>>>> The individual.needs_update loop in amdgpu_vm_handle_moved() > >>>>> unconditionally dereferenced bo_va->base.bo (to read its > >>>>> reservation object, ttm_tt and to test for a DMABuf import). > >>>>> However bo_va->base.bo can legitimately be NULL for PRT/sparse > >>>>> mappings - > >>>>> amdgpu_vm_bo_update() already handles a NULL bo via its "else if > >>>>> (!bo)" PRT path. When such a bo_va reaches the moved list (e.g. > >>>>> the userq eviction restore worker running amdgpu_vm_handle_moved() > >>>>> while a user > >>>> queue is being torn down during a GPU reset), the NULL deref > >>>> crashes the > >> kernel: > >>>>> > >>>>> BUG: kernel NULL pointer dereference, address: 0000000000000158 > >>>>> #PF: supervisor read access in kernel mode > >>>>> Oops: 0000 [#1] SMP NOPTI > >>>>> Workqueue: events amdgpu_userq_restore_worker [amdgpu] > >>>>> RIP: 0010:amdgpu_vm_handle_moved+0x17a/0x200 [amdgpu] > >>>>> Call Trace: > >>>>> <TASK> > >>>>> amdgpu_userq_vm_validate_and_restore_queue+0x2ce/0x920 [amdgpu] > >>>>> amdgpu_userq_restore_worker+0xce/0x210 [amdgpu] > >>>>> process_scheduled_works+0xa6/0x460 > >>>>> worker_thread+0x13c/0x290 > >>>>> kthread+0xfb/0x140 > >>>>> ret_from_fork+0x1b6/0x2b0 > >>>>> ret_from_fork_asm+0x1a/0x30 > >>>>> </TASK> > >>>>> > >>>>> The faulting instruction is "mov rax,[rdx+0x158]" with rdx (bo) == > >>>>> 0 and > >>>>> CR2 == 0x158, i.e. reading bo->tbo.base.resv off a NULL bo. > >>>>> > >>>>> Guard the BO-less case: skip the reservation dance and the > >>>>> DMABuf-import check when bo is NULL, and let amdgpu_vm_bo_update() > >>>>> take > >>>> its existing PRT path. > >>>> > >>>> Clear NAK. PRT mappings should *never* be on the moved list in the first > place. > >>> Thanks for the review. > >>> I tracked the issue to the state-machine path: amdgpu_vm_bo_update() > >>> can put a NULL-bo bo_va onto individual.idle via > >>> amdgpu_vm_bo_idle(), and then > >>> amdgpu_vm_bo_reset_state_machine() promotes it to > >>> individual.needs_update > >> after a VM generation reset. > >> > >> Something is seriously wrong here. I've fixed > >> amdgpu_vm_bo_reset_state_machine() to not do that weeks ago. > >> > >> Where do you see that issue? On some release branch? > > Hi Christian, > > > > Thanks — I found the fix you mean: 989bdec8ca04 ("drm/amdgpu: fix > amdgpu_vm_bo_reset_state_machine", 2026-05-22)。 It's already in amd-staging- > drm-next. > > > > But that commit addresses a different problem: it replaces the list_splice in > reset_state_machine() so the root PD gets its special handling and the moved flag > is set correctly. > > It does not touch the NULL-bo case — after it, an entry on individual.idle is still > list_move'd onto individual.needs_update. > > > > The crash I'm reporting has a different root cause: a PRT/sparse mapping's > bo_va has base.bo == NULL (fpriv->prt_va, created via amdgpu_vm_bo_add(vm, > NULL)). > > The tail of amdgpu_vm_bo_update() unconditionally calls amdgpu_vm_bo_idle() in > the !always_valid else branch, putting that NULL-bo bo_va onto individual.idle. > > After a GPU reset reset_state_machine() promotes it to individual.needs_update, > and amdgpu_vm_handle_moved() then dereferences bo_va->base.bo > unconditionally. > > > > I can still reproduce this on the latest amd-staging-drm-next (HEAD includes > 989bdec8ca04). > > the log: > > [ 3107.632098] RIP: 0010:amdgpu_vm_handle_moved+0x17a/0x200 [amdgpu] [ > > 3107.632469] Code: e8 5b c7 f9 ee 49 8b 84 24 60 02 00 00 48 3b 45 b8 > > 0f 84 45 ff ff ff 4d 8b ac 24 60 02 00 00 4c 89 ff 49 8b 55 f0 4d 8d > > 75 e8 <48> 8b 82 58 01 00 00 48 89 55 c8 48 89 45 d0 e8 92 c0 f9 ee 0f > > b6 [ 3107.632478] RSP: 0018:ffffcda785f23cb0 EFLAGS: 00010283 [ > > 3107.632484] RAX: ffff8ad9cc15fe98 RBX: ffff8ada86100000 RCX: > > ffff8ad9cf4a8188 [ 3107.632488] RDX: 0000000000000000 RSI: > > 000000000000000f RDI: ffff8adab01c0248 [ 3107.632493] RBP: > > ffffcda785f23d00 R08: 0000000000000000 R09: 0000000000000004 [ > > 3107.632497] R10: 0000000000000000 R11: 0000000000000000 R12: > > ffff8adab01c0000 [ 3107.632501] R13: ffff8ad9cc15fe98 R14: > > ffff8ad9cc15fe80 R15: ffff8adab01c0248 [ 3107.632505] FS: > > 0000000000000000(0000) GS:ffff8add3c8ff000(0000) knlGS:0000000000000000 > [ 3107.632510] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 > [ 3107.632515] CR2: 0000000000000158 CR3: 0000000117903000 CR4: > 0000000000750ef0 [ 3107.632520] PKRU: 55555554 [ 3107.632523] Call Trace: > > [ 3107.632527] <TASK> > > [ 3107.632533] amdgpu_userq_vm_validate_and_restore_queue+0x2ce/0x920 > > [amdgpu] [ 3107.632846] amdgpu_userq_restore_worker+0xa6/0x180 > > [amdgpu] [ 3107.633036] process_scheduled_works+0xa6/0x460 > > [ 3107.633042] worker_thread+0x13c/0x290 [ 3107.633045] > > kthread+0xfb/0x140 [ 3107.633048] ? __pfx_worker_thread+0x10/0x10 [ > > 3107.633050] ? __pfx_kthread+0x10/0x10 [ 3107.633053] > > ret_from_fork+0x1b6/0x2b0 [ 3107.633056] ? __pfx_kthread+0x10/0x10 [ > > 3107.633058] ret_from_fork_asm+0x1a/0x30 > > There is still something going wrong here. > > The PRT should be updated in the PTEs separately, otherwise we use the wrong > fence to sync it. > > There should be something like this in the userq validation code as well: > > r = amdgpu_vm_bo_update(adev, fpriv->prt_va, false); > if (r) > return r; > > r = amdgpu_sync_fence(&p->sync, fpriv->prt_va->last_pt_update, > GFP_KERNEL); ... > > Similar to how we have it in the CS code. You're right — the PRT PTEs are updated separately, so their fence lands in prt_va->last_pt_update, not vm->last_update, and nothing waited on it. v3 adds an explicit dma_fence_wait on fpriv->prt_va->last_pt_update in the userq restore path before restarting the queues, mirroring the CS path. Thanks Jesse > > Regards, > Christian. > > > > > > > So this is a distinct issue from 989bdec8ca04. > > My v2 keeps the PRT bo_va off the state lists at the producer side > (amdgpu_vm_bo_update: else → else if (bo)), which is what your earlier feedback > asked for. > > > > Thanks, > > Jesse > >> > >> Regards, > >> Christian. > >> > >>> > >>> I’ll send v2 that fixes this at the producer side by keeping NULL-bo > >>> (PRT) mappings off the vm_bo state lists, and I’ll drop the > >>> consumer-side NULL guard from v1. > >>> > >>> Thanks, > >>> Jesse > >>>> > >>>> Regards, > >>>> Christian. > >>>> > >>>>> > >>>>> Signed-off-by: Jesse Zhang <[email protected]> > >>>>> --- > >>>>> drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 12 +++++++++--- > >>>>> 1 file changed, 9 insertions(+), 3 deletions(-) > >>>>> > >>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c > >>>>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c > >>>>> index aac8ace9d7a6..fbb76c32bff9 100644 > >>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c > >>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c > >>>>> @@ -1631,11 +1631,17 @@ int amdgpu_vm_handle_moved(struct > >>>> amdgpu_device *adev, > >>>>> bo_va = list_first_entry(&vm->individual.needs_update, > >>>>> typeof(*bo_va), base.vm_status); > >>>>> bo = bo_va->base.bo; > >>>>> - resv = bo->tbo.base.resv; > >>>>> + resv = bo ? bo->tbo.base.resv : NULL; > >>>>> spin_unlock(&vm->individual_lock); > >>>>> > >>>>> + /* PRT/sparse mappings have no BO to reserve; just update the > >>>>> + * page tables (amdgpu_vm_bo_update() handles a NULL bo). > >>>>> + */ > >>>>> + if (!bo) { > >>>>> + clear = false; > >>>>> + unlock = false; > >>>>> /* Try to reserve the BO to avoid clearing its ptes */ > >>>>> - if (!adev->debug_vm && !amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) > >>>> && > >>>>> + } else if (!adev->debug_vm && > >>>>> +!amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) && > >>>>> dma_resv_trylock(resv)) { > >>>>> clear = false; > >>>>> unlock = true; @@ -1659,7 +1665,7 @@ int > >>>>> amdgpu_vm_handle_moved(struct amdgpu_device > >>>> *adev, > >>>>> /* Remember evicted DMABuf imports in compute VMs for later > >>>>> * validation > >>>>> */ > >>>>> - if (vm->is_compute_context && > >>>>> + if (bo && vm->is_compute_context && > >>>>> drm_gem_is_imported(&bo_va->base.bo->tbo.base) && > >>>>> (!bo_va->base.bo->tbo.resource || > >>>>> bo_va->base.bo->tbo.resource->mem_type == > >>>> TTM_PL_SYSTEM)) > >>> > >