Reviewed-by: Lyude Paul <[email protected]>
Will push to drm-misc-fixes in just a moment
On Mon, 2026-09-07 at 13:22 +0800, Peiyang He wrote:
> nouveau_uvmm_sm() calls op_map(), which passes bo->resource through
> nouveau_mem() to nouveau_uvma_map(). nouveau_uvmm_vmm_map() then
> reads
> mem->mem.type.
>
> But this is only valid when bo->resource is backed by struct
> nouveau_mem,
> as is the case for VRAM and TT resources. If the BO is left in
> TTM_PL_SYSTEM, bo->resource is only a struct ttm_resource. Treating
> it
> as struct nouveau_mem makes the mem->mem.type read past the end of
> the
> resource, causing a KASAN: slab-use-after-free Read in
> nouveau_uvmm_sm
> report:
>
> BUG: KASAN: slab-use-after-free in nouveau_uvmm_vmm_map
> drivers/gpu/drm/nouveau/nouveau_uvmm.c:152 [inline]
> BUG: KASAN: slab-use-after-free in nouveau_uvma_map
> drivers/gpu/drm/nouveau/nouveau_uvmm.c:199 [inline]
> BUG: KASAN: slab-use-after-free in op_map
> drivers/gpu/drm/nouveau/nouveau_uvmm.c:849 [inline]
> BUG: KASAN: slab-use-after-free in
> nouveau_uvmm_sm.constprop.0+0x6ab/0x900
> drivers/gpu/drm/nouveau/nouveau_uvmm.c:903
> Read of size 1 at addr ffff888127d3e3a0 by task kworker/0:1/11
>
> CPU: 0 UID: 0 PID: 11 Comm: kworker/0:1 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
> Workqueue: nouveau_sched_wq_2224 drm_sched_run_job_work
> 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_uvmm_vmm_map drivers/gpu/drm/nouveau/nouveau_uvmm.c:152
> [inline]
> nouveau_uvma_map drivers/gpu/drm/nouveau/nouveau_uvmm.c:199 [inline]
> op_map drivers/gpu/drm/nouveau/nouveau_uvmm.c:849 [inline]
> nouveau_uvmm_sm.constprop.0+0x6ab/0x900
> drivers/gpu/drm/nouveau/nouveau_uvmm.c:903
> nouveau_uvmm_sm_unmap drivers/gpu/drm/nouveau/nouveau_uvmm.c:932
> [inline]
> nouveau_uvmm_bind_job_run+0xd6/0x250
> drivers/gpu/drm/nouveau/nouveau_uvmm.c:1532
> nouveau_job_run drivers/gpu/drm/nouveau/nouveau_sched.c:350 [inline]
> nouveau_sched_run_job+0x62/0xd0
> drivers/gpu/drm/nouveau/nouveau_sched.c:364
> drm_sched_run_job_work+0x356/0xa10
> drivers/gpu/drm/scheduler/sched_main.c:1061
> 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
> </TASK>
>
> Allocated by task 2224 on cpu 0 at 66.550027s:
> 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]
> __do_kmalloc_node mm/slub.c:5334 [inline]
> __kmalloc_noprof+0x304/0x7c0 mm/slub.c:5359
> _kmalloc_noprof include/linux/slab.h:992 [inline]
> dma_resv_list_alloc+0x27/0x90 drivers/dma-buf/dma-resv.c:106
> dma_resv_reserve_fences+0x60e/0xa30 drivers/dma-buf/dma-resv.c:205
> ttm_bo_alloc_resource+0x12c/0xbd0 drivers/gpu/drm/ttm/ttm_bo.c:721
> ttm_bo_validate+0x1bc/0x4a0 drivers/gpu/drm/ttm/ttm_bo.c:856
> ttm_bo_init_reserved+0x2c3/0x570 drivers/gpu/drm/ttm/ttm_bo.c:970
> nouveau_bo_init+0x159/0x2c0 drivers/gpu/drm/nouveau/nouveau_bo.c:359
> nouveau_gem_new+0x234/0x5f0
> drivers/gpu/drm/nouveau/nouveau_gem.c:272
> nouveau_gem_ioctl_new+0x1eb/0x420
> drivers/gpu/drm/nouveau/nouveau_gem.c:352
> 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 2223 on cpu 0 at 66.554063s:
> 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]
> __rcu_free_sheaf_prepare+0xb6/0x2e0 mm/slub.c:2928
> rcu_free_sheaf+0x1b/0x120 mm/slub.c:5978
> rcu_do_batch kernel/rcu/tree.c:2645 [inline]
> rcu_core+0x521/0x1490 kernel/rcu/tree.c:2897
> handle_softirqs+0x1b1/0x8a0 kernel/softirq.c:622
> __do_softirq kernel/softirq.c:656 [inline]
> invoke_softirq kernel/softirq.c:496 [inline]
> __irq_exit_rcu+0x137/0x1c0 kernel/softirq.c:735
> irq_exit_rcu+0x9/0x20 kernel/softirq.c:752
> instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062
> [inline]
> sysvec_apic_timer_interrupt+0x70/0x80
> arch/x86/kernel/apic/apic.c:1062
> asm_sysvec_apic_timer_interrupt+0x1a/0x20
> arch/x86/include/asm/idtentry.h:674
>
> The buggy address belongs to the object at ffff888127d3e380
> which belongs to the cache kmalloc-96 of size 96
> The buggy address is located 32 bytes inside of
> freed 96-byte region [ffff888127d3e380, ffff888127d3e3e0)
>
> The buggy address belongs to the physical page:
> page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0
> pfn:0x127d3e
> flags: 0x200000000000000(node=0|zone=2)
> page_type: f5(slab)
> raw: 0200000000000000 ffff888100041280 dead000000000122
> 0000000000000000
> raw: 0000000000000000 0000000000200020 00000000f5000000
> 0000000000000000
> page dumped because: kasan: bad access detected
>
> Memory state around the buggy address:
> ffff888127d3e280: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc
> ffff888127d3e300: 00 00 00 00 00 00 00 00 00 00 00 fc fc fc fc fc
> > ffff888127d3e380: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
> ^
> ffff888127d3e400: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
> ffff888127d3e480: 00 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc
>
> Fix by resetting the placement to the BO's valid domains before
> calling nouveau_bo_validate(), matching the handling in
> nouveau_uvmm_bo_validate(), so map jobs do not run for SYSTEM
> resources;
> Reject BO that cannot reside in VRAM or GART;
> Also skip op_map() when the GPUVA has been invalidated, matching the
> handling in the unmap and remap paths.
>
> Found when fuzzing the nouveau driver with a modified Syzkaller.
>
> Fixes: b88baab82871 ("drm/nouveau: implement new VM_BIND uAPI")
> Cc: [email protected]
> Signed-off-by: Peiyang He <[email protected]>
> Assisted-by: Codex:gpt-5.5
> ---
> drivers/gpu/drm/nouveau/nouveau_uvmm.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> index fc125fd44a9b..2026fe6b48c6 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> @@ -846,6 +846,9 @@ op_map(struct nouveau_uvma *uvma)
> {
> struct nouveau_bo *nvbo = nouveau_gem_object(uvma-
> >va.gem.obj);
>
> + if (drm_gpuva_invalidated(&uvma->va))
> + return;
> +
> nouveau_uvma_map(uvma, nouveau_mem(nvbo->bo.resource));
> }
>
> @@ -1232,6 +1235,7 @@ bind_lock_validate(struct nouveau_job *job,
> struct drm_exec *exec,
>
> drm_gpuva_for_each_op(va_op, op->ops) {
> struct drm_gem_object *obj =
> op_gem_obj(va_op);
> + struct nouveau_bo *nvbo;
>
> if (unlikely(!obj))
> continue;
> @@ -1246,8 +1250,13 @@ bind_lock_validate(struct nouveau_job *job,
> struct drm_exec *exec,
> if (va_op->op == DRM_GPUVA_OP_UNMAP)
> continue;
>
> - ret =
> nouveau_bo_validate(nouveau_gem_object(obj),
> - true, false);
> + nvbo = nouveau_gem_object(obj);
> + if (!(nvbo->valid_domains &
> + (NOUVEAU_GEM_DOMAIN_VRAM |
> NOUVEAU_GEM_DOMAIN_GART)))
> + return -EINVAL;
> +
> + nouveau_bo_placement_set(nvbo, nvbo-
> >valid_domains, 0);
> + ret = nouveau_bo_validate(nvbo, true,
> false);
> if (ret)
> return ret;
> }
> }
>