* [PATCH] drm/nouveau/uvmm: fix UAF in bind job cleanup
@ 2026-09-01 6:32 Peiyang He
2026-09-01 6:49 ` sashiko-bot
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Peiyang He @ 2026-09-01 6:32 UTC (permalink / raw)
To: lyude, dakr; +Cc: dri-devel, nouveau, linux-kernel, stable, Peiyang He
OP_UNMAP_SPARSE stores the result of nouveau_uvma_region_find()
in op->reg before the bind job owns the region.
A first sparse unmap can look up the region, prepare its unmap
state and mark the region dirty. A second async sparse unmap of
the same range then sees the dirty region and fails out through
unwind_continue. However, op->reg is left pointing at the looked-up
region even though this job never took a reference and never became
responsible for cleaning it up.
nouveau_uvmm_bind_job_cleanup() later treats any non-NULL op->reg
as job-owned cleanup state. It can therefore tear down the region
that is still owned by the first unmap job, leading to a UAF when
the failed submit cleans itself up.
Fix by clearing op->reg on the unwind_continue paths so failed
submits do not carry a stale region pointer into cleanup.
Found when fuzzing the nouveau driver with a modified Syzkaller:
BUG: KASAN: slab-use-after-free in nouveau_uvma_region_sparse_unref drivers/gpu/drm/nouveau/nouveau_uvmm.c:174 [inline]
BUG: KASAN: slab-use-after-free in nouveau_uvmm_bind_job_cleanup+0x6e1/0x7e0 drivers/gpu/drm/nouveau/nouveau_uvmm.c:1568
Read of size 8 at addr ffff888129b7c108 by task syz.2.125/2454
CPU: 1 UID: 0 PID: 2454 Comm: syz.2.125 Not tainted 7.2.0 #5 PREEMPT(lazy)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x95/0xe0 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:378 [inline]
print_report+0xcb/0x5a0 mm/kasan/report.c:482
kasan_report+0xca/0x100 mm/kasan/report.c:595
nouveau_uvma_region_sparse_unref drivers/gpu/drm/nouveau/nouveau_uvmm.c:174 [inline]
nouveau_uvmm_bind_job_cleanup+0x6e1/0x7e0 drivers/gpu/drm/nouveau/nouveau_uvmm.c:1568
nouveau_uvmm_vm_bind drivers/gpu/drm/nouveau/nouveau_uvmm.c:1731 [inline]
nouveau_uvmm_ioctl_vm_bind+0xadc/0xd20 drivers/gpu/drm/nouveau/nouveau_uvmm.c:1817
drm_ioctl_kernel+0x192/0x350 drivers/gpu/drm/drm_ioctl.c:817
drm_ioctl+0x4f8/0xb40 drivers/gpu/drm/drm_ioctl.c:914
nouveau_drm_ioctl+0xea/0x2c0 drivers/gpu/drm/nouveau/nouveau_drm.c:1338
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl fs/ioctl.c:583 [inline]
__x64_sys_ioctl+0x180/0x1d0 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x690 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fd3f9f8594d
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fd3f89e7008 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007fd3fa215fa0 RCX: 00007fd3f9f8594d
RDX: 0000200000000600 RSI: 00000000c0286451 RDI: 0000000000000004
RBP: 00007fd3fa02c22b R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fd3fa216038 R14: 00007fd3fa215fa0 R15: 00007ffef3a1fa40
</TASK>
clocksource: Watchdog remote CPU 1 read timed out
Allocated by task 2457 on cpu 1 at 138.974794s:
kasan_save_stack+0x24/0x50 mm/kasan/common.c:57
kasan_save_track+0x17/0x60 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__kmalloc_cache_noprof+0x299/0x630 mm/slub.c:5489
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
nouveau_uvma_region_alloc drivers/gpu/drm/nouveau/nouveau_uvmm.c:249 [inline]
nouveau_uvma_region_create drivers/gpu/drm/nouveau/nouveau_uvmm.c:341 [inline]
nouveau_uvmm_bind_job_submit+0x1a4a/0x2820 drivers/gpu/drm/nouveau/nouveau_uvmm.c:1310
nouveau_job_submit+0x70c/0x1240 drivers/gpu/drm/nouveau/nouveau_sched.c:301
nouveau_uvmm_vm_bind drivers/gpu/drm/nouveau/nouveau_uvmm.c:1724 [inline]
nouveau_uvmm_ioctl_vm_bind+0x8d6/0xd20 drivers/gpu/drm/nouveau/nouveau_uvmm.c:1817
drm_ioctl_kernel+0x192/0x350 drivers/gpu/drm/drm_ioctl.c:817
drm_ioctl+0x4f8/0xb40 drivers/gpu/drm/drm_ioctl.c:914
nouveau_drm_ioctl+0xea/0x2c0 drivers/gpu/drm/nouveau/nouveau_drm.c:1338
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl fs/ioctl.c:583 [inline]
__x64_sys_ioctl+0x180/0x1d0 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x690 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 125 on cpu 1 at 139.013117s:
kasan_save_stack+0x24/0x50 mm/kasan/common.c:57
kasan_save_track+0x17/0x60 mm/kasan/common.c:78
kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x61/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2677 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x383/0x590 mm/slub.c:6692
nouveau_uvma_region_free drivers/gpu/drm/nouveau/nouveau_uvmm.c:264 [inline]
kref_put include/linux/kref.h:65 [inline]
nouveau_uvma_region_put drivers/gpu/drm/nouveau/nouveau_uvmm.c:276 [inline]
nouveau_uvmm_bind_job_cleanup+0x4df/0x7e0 drivers/gpu/drm/nouveau/nouveau_uvmm.c:1573
drm_sched_free_job_work+0x33b/0x5d0 drivers/gpu/drm/scheduler/sched_main.c:1013
process_one_work+0x8a5/0x1900 kernel/workqueue.c:3322
process_scheduled_works kernel/workqueue.c:3405 [inline]
worker_thread+0x5dd/0xd80 kernel/workqueue.c:3486
kthread+0x31d/0x420 kernel/kthread.c:436
ret_from_fork+0x662/0x940 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
The buggy address belongs to the object at ffff888129b7c100
which belongs to the cache kmalloc-128 of size 128
The buggy address is located 8 bytes inside of
freed 128-byte region [ffff888129b7c100, ffff888129b7c180)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x129b7c
flags: 0x200000000000000(node=0|zone=2)
page_type: f5(slab)
raw: 0200000000000000 ffff888100041a00 dead000000000100 dead000000000122
raw: 0000000000000000 0000000000100010 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888129b7c000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fc
ffff888129b7c080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888129b7c100: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888129b7c180: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888129b7c200: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
Fixes: b88baab82871 ("drm/nouveau: implement new VM_BIND uAPI")
Cc: stable@vger.kernel.org
Tested-by: Peiyang He <peiyang_he@smail.nju.edu.cn>
Signed-off-by: Peiyang He <peiyang_he@smail.nju.edu.cn>
Assisted-by: Codex:gpt-5.4
---
The first clearing of op->reg is enough to prevent the UAF,
but I also add a second clearing. Now all unwind_continue paths
in the OP_UNMAP_SPARSE case can properly clear op->reg.
drivers/gpu/drm/nouveau/nouveau_uvmm.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
index f5e4756b4de4..aad14316cbef 100644
--- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
@@ -1318,6 +1318,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
op->reg = nouveau_uvma_region_find(uvmm, op->va.addr,
op->va.range);
if (!op->reg || op->reg->dirty) {
+ op->reg = NULL;
ret = -ENOENT;
goto unwind_continue;
}
@@ -1327,6 +1328,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
op->va.range);
if (IS_ERR(op->ops)) {
ret = PTR_ERR(op->ops);
+ op->reg = NULL;
goto unwind_continue;
}
base-commit: 786262be6048deab760f68c8acc2c85607165894
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH] drm/nouveau/uvmm: fix UAF in bind job cleanup
2026-09-01 6:32 [PATCH] drm/nouveau/uvmm: fix UAF in bind job cleanup Peiyang He
@ 2026-09-01 6:49 ` sashiko-bot
2026-09-01 7:36 ` Peiyang He
2026-09-02 2:38 ` Peiyang He
2 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-09-01 6:49 UTC (permalink / raw)
To: Peiyang He; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Unwinding a successfully prepared OP_MAP_SPARSE operation in nouveau_uvmm_bind_job_submit() causes a guaranteed NULL pointer dereference.
- [High] Unwinding a successfully prepared OP_UNMAP_SPARSE operation leaves the region permanently dirty, causing uninterruptible hangs for future operations.
--
commit 203035037bce29ea5a47c8cbbdd0cff202e6dbea
Author: Peiyang He <peiyang_he@smail.nju.edu.cn>
Subject: drm/nouveau/uvmm: fix UAF in bind job cleanup
This patch clears op->reg on the unwind_continue paths for OP_UNMAP_SPARSE.
This prevents a stale region pointer from being passed to cleanup after an
async sparse unmap fails, avoiding a use-after-free.
> diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> index f5e4756b4de4a..aad14316cbefd 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> @@ -1318,6 +1318,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
> op->reg = nouveau_uvma_region_find(uvmm, op->va.addr,
> op->va.range);
> if (!op->reg || op->reg->dirty) {
> + op->reg = NULL;
> ret = -ENOENT;
> goto unwind_continue;
> }
> @@ -1327,6 +1328,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
> op->va.range);
> if (IS_ERR(op->ops)) {
> ret = PTR_ERR(op->ops);
> + op->reg = NULL;
> goto unwind_continue;
> }
[Severity: High]
This isn't a bug introduced by this patch, but looking at the unwind path
triggered by goto unwind_continue, does the unwind loop cause a null pointer
dereference for OP_MAP_SPARSE operations?
When unwinding a successfully prepared OP_MAP_SPARSE operation, the code
unconditionally calls drm_gpuva_ops_free(&uvmm->base, op->ops). However,
op->ops is never allocated for OP_MAP_SPARSE and remains NULL.
The drm_gpuva_ops_free() function does not check for NULL and evaluates
&(ops)->list inside the drm_gpuva_for_each_op_safe() iterator:
drm_gpuvm.c:drm_gpuva_ops_free() {
struct drm_gpuva_op *op, *next;
drm_gpuva_for_each_op_safe(op, next, ops) {
list_del(&op->entry);
...
Could this crash if a VM_BIND job submits an OP_MAP_SPARSE operation
followed by a malformed operation that fails?
[Severity: High]
This is also a pre-existing issue in the same unwind path, but does
unwinding a successfully prepared OP_UNMAP_SPARSE operation leave the region
permanently dirty?
The prepare phase for OP_UNMAP_SPARSE calls nouveau_uvma_region_dirty(op->reg).
If a subsequent operation fails, the unwind loop restores the region to the
maple tree and unconditionally clears op->reg = NULL without clearing the
dirty flag or signaling the completion:
nouveau_uvmm.c:nouveau_uvmm_bind_job_submit() unwind path {
...
case OP_UNMAP_SPARSE:
__nouveau_uvma_region_insert(uvmm, op->reg);
nouveau_uvmm_sm_unmap_prepare_unwind(uvmm, &op->new, op->ops);
break;
...
drm_gpuva_ops_free(&uvmm->base, op->ops);
op->ops = NULL;
op->reg = NULL;
...
Because op->reg is cleared here, the nouveau_uvmm_bind_job_cleanup() function
skips it, meaning nouveau_uvma_region_complete(op->reg) is never called.
Will future tasks attempting to map over this leaked region hang forever
waiting on ®->complete in bind_validate_map_common()?
--
Sashiko AI review · https://sashiko.dev/#/patchset/7F26D29D0BD60619+20260901063255.661503-1-peiyang_he@smail.nju.edu.cn?part=1
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH] drm/nouveau/uvmm: fix UAF in bind job cleanup
2026-09-01 6:32 [PATCH] drm/nouveau/uvmm: fix UAF in bind job cleanup Peiyang He
2026-09-01 6:49 ` sashiko-bot
@ 2026-09-01 7:36 ` Peiyang He
2026-09-02 2:38 ` Peiyang He
2 siblings, 0 replies; 4+ messages in thread
From: Peiyang He @ 2026-09-01 7:36 UTC (permalink / raw)
To: lyude, dakr; +Cc: dri-devel, nouveau, linux-kernel, stable
On 2026/9/1 14:32, Peiyang He wrote:
> drivers/gpu/drm/nouveau/nouveau_uvmm.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> index f5e4756b4de4..aad14316cbef 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> @@ -1318,6 +1318,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
> op->reg = nouveau_uvma_region_find(uvmm, op->va.addr,
> op->va.range);
> if (!op->reg || op->reg->dirty) {
> + op->reg = NULL;
> ret = -ENOENT;
> goto unwind_continue;
> }
> @@ -1327,6 +1328,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
> op->va.range);
> if (IS_ERR(op->ops)) {
> ret = PTR_ERR(op->ops);
> + op->reg = NULL;
> goto unwind_continue;
> }
None of the two issues pointed by Sashiko AI are introduced by this patch.
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH] drm/nouveau/uvmm: fix UAF in bind job cleanup
2026-09-01 6:32 [PATCH] drm/nouveau/uvmm: fix UAF in bind job cleanup Peiyang He
2026-09-01 6:49 ` sashiko-bot
2026-09-01 7:36 ` Peiyang He
@ 2026-09-02 2:38 ` Peiyang He
2 siblings, 0 replies; 4+ messages in thread
From: Peiyang He @ 2026-09-02 2:38 UTC (permalink / raw)
To: lyude, dakr; +Cc: dri-devel, nouveau, linux-kernel, stable
On 2026/9/1 14:32, Peiyang He wrote:
> drivers/gpu/drm/nouveau/nouveau_uvmm.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> index f5e4756b4de4..aad14316cbef 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> @@ -1318,6 +1318,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
> op->reg = nouveau_uvma_region_find(uvmm, op->va.addr,
> op->va.range);
> if (!op->reg || op->reg->dirty) {
> + op->reg = NULL;
> ret = -ENOENT;
> goto unwind_continue;
> }
> @@ -1327,6 +1328,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
> op->va.range);
> if (IS_ERR(op->ops)) {
> ret = PTR_ERR(op->ops);
> + op->reg = NULL;
> goto unwind_continue;
> }
>
>
> base-commit: 786262be6048deab760f68c8acc2c85607165894
This patch is identical to Zhenhao's[1], so please ignore this patch.
Sorry for the disturbance.
[1]: https://lore.kernel.org/all/20260811-nouveau-uvmm-vmbind-fixes-v2-2-aaee4b395d04@gmail.com/
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-03 7:34 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-01 6:32 [PATCH] drm/nouveau/uvmm: fix UAF in bind job cleanup Peiyang He
2026-09-01 6:49 ` sashiko-bot
2026-09-01 7:36 ` Peiyang He
2026-09-02 2:38 ` Peiyang He
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox