mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/2] drm/nouveau: fix out-of-bounds VRAM access and VM_BIND page-size mismatch
@ 2026-08-17  6:50 Junrui Luo via B4 Relay
  2026-08-17  6:50 ` [PATCH 1/2] drm/nouveau/dmem: pin VRAM for the whole registered range Junrui Luo via B4 Relay
  2026-08-17  6:50 ` [PATCH 2/2] drm/nouveau/uvmm: reject replace across page sizes Junrui Luo via B4 Relay
  0 siblings, 2 replies; 8+ messages in thread
From: Junrui Luo via B4 Relay @ 2026-08-17  6:50 UTC (permalink / raw)
  To: Lyude Paul, Danilo Krummrich, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Andrew Morton,
	Balbir Singh, Mary Guillemard, Mohamed Ahmed, James Jones
  Cc: dri-devel, nouveau, linux-kernel, Junrui Luo, Yuhao Jiang, stable

Two unrelated fixes; they touch different files and can be applied
independently.

Patch 1 sizes the VRAM buffer object backing a device-private region to
the region it actually backs. The region grew to DMEM_CHUNK_SIZE *
NR_CHUNKS while the buffer object stayed at DMEM_CHUNK_SIZE, so
nouveau_dmem_page_addr() resolves every page past the first chunk to VRAM
outside the object.

Patch 2 rejects a VM_BIND that replaces a mapping with one using a
different page size. A new mapping takes over the page tables of the
mappings it replaces, which only holds while they all use the same page
size. Since select_page_shift() started deriving one per mapping it no
longer does, and the new mapping ends up mapping at a size its page
tables were never built for.

Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
---
Junrui Luo (2):
      drm/nouveau/dmem: pin VRAM for the whole registered range
      drm/nouveau/uvmm: reject replace across page sizes

 drivers/gpu/drm/nouveau/nouveau_dmem.c |  4 ++--
 drivers/gpu/drm/nouveau/nouveau_uvmm.c | 33 ++++++++++++++++++++++++++++++++-
 2 files changed, 34 insertions(+), 3 deletions(-)
---
base-commit: f5bbbfec59b4e2fb7520a91de3df8a6174325d6a
change-id: 20260817-nouveau-fixes-23877845c3ab

Best regards,
-- 
Junrui Luo <moonafterrain@outlook.com>



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/2] drm/nouveau/dmem: pin VRAM for the whole registered range
  2026-08-17  6:50 [PATCH 0/2] drm/nouveau: fix out-of-bounds VRAM access and VM_BIND page-size mismatch Junrui Luo via B4 Relay
@ 2026-08-17  6:50 ` Junrui Luo via B4 Relay
  2026-09-17 19:59   ` lyude
                     ` (2 more replies)
  2026-08-17  6:50 ` [PATCH 2/2] drm/nouveau/uvmm: reject replace across page sizes Junrui Luo via B4 Relay
  1 sibling, 3 replies; 8+ messages in thread
From: Junrui Luo via B4 Relay @ 2026-08-17  6:50 UTC (permalink / raw)
  To: Lyude Paul, Danilo Krummrich, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Andrew Morton,
	Balbir Singh, Mary Guillemard, Mohamed Ahmed, James Jones
  Cc: dri-devel, nouveau, linux-kernel, Junrui Luo, Yuhao Jiang, stable

From: Junrui Luo <moonafterrain@outlook.com>

Commit c32287471077 ("gpu/drm/nouveau: enable THP support for GPU memory
migration") grew the device-private region that
nouveau_dmem_chunk_alloc() registers from DMEM_CHUNK_SIZE to
DMEM_CHUNK_SIZE * NR_CHUNKS, but left the VRAM buffer object backing that
region at DMEM_CHUNK_SIZE.

nouveau_dmem_page_addr() returns chunk->bo->offset plus the page's offset
within the registered region, so every page past the first chunk resolves
to VRAM outside the buffer object.

Size the buffer object to the region it backs.

Fixes: c32287471077 ("gpu/drm/nouveau: enable THP support for GPU memory migration")
Reported-by: Yuhao Jiang <danisjiang@gmail.com>
Assisted-by: Claude:claude-opus-5
Cc: stable@vger.kernel.org
Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
---
 drivers/gpu/drm/nouveau/nouveau_dmem.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c
index 9442ec6e1f6c..356ff8f3c1b8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dmem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c
@@ -325,8 +325,8 @@ nouveau_dmem_chunk_alloc(struct nouveau_drm *drm, struct page **ppage,
 	chunk->pagemap.ops = &nouveau_dmem_pagemap_ops;
 	chunk->pagemap.owner = drm->dev;
 
-	ret = nouveau_bo_new_pin(&drm->client, NOUVEAU_GEM_DOMAIN_VRAM, DMEM_CHUNK_SIZE,
-				 &chunk->bo);
+	ret = nouveau_bo_new_pin(&drm->client, NOUVEAU_GEM_DOMAIN_VRAM,
+				 DMEM_CHUNK_SIZE * NR_CHUNKS, &chunk->bo);
 	if (ret)
 		goto out_release;
 

-- 
2.51.2



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/2] drm/nouveau/uvmm: reject replace across page sizes
  2026-08-17  6:50 [PATCH 0/2] drm/nouveau: fix out-of-bounds VRAM access and VM_BIND page-size mismatch Junrui Luo via B4 Relay
  2026-08-17  6:50 ` [PATCH 1/2] drm/nouveau/dmem: pin VRAM for the whole registered range Junrui Luo via B4 Relay
@ 2026-08-17  6:50 ` Junrui Luo via B4 Relay
  2026-09-17 19:56   ` lyude
  2026-09-17 22:44   ` Balbir Singh
  1 sibling, 2 replies; 8+ messages in thread
From: Junrui Luo via B4 Relay @ 2026-08-17  6:50 UTC (permalink / raw)
  To: Lyude Paul, Danilo Krummrich, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Andrew Morton,
	Balbir Singh, Mary Guillemard, Mohamed Ahmed, James Jones
  Cc: dri-devel, nouveau, linux-kernel, Junrui Luo, Yuhao Jiang, stable

From: Junrui Luo <moonafterrain@outlook.com>

A new mapping takes over the page tables of the mappings it replaces.
nouveau_uvmm_sm_prepare() only acquires page tables for the range no
existing mapping covers, and the map path frees the replaced mappings
without putting their references. That is only valid while all of them
use the same page size, which select_page_shift() no longer guarantees.

Rebinding a GART BO over a 2MiB VRAM BO therefore leaves the new mapping
owning page tables built for a different page size, and it then maps at a
size that was never referenced over that range. Since raw map does not
allocate, nvkm_vmm_iter() can walk down to a NULL leaf and dereference
it. The remainders of a split have the same problem: op_map_prepare()
recomputes a page size with select_page_shift() while the remainder keeps
the parent's page tables, so a parent that was itself downgraded can leave
a remainder that re-aligns to a larger size. This happens on the unmap
path too.

Reject the bind, and make split remainders inherit the page size of the
mapping they are split from.

Fixes: c488a94e7e14 ("drm/nouveau/uvmm: Allow larger pages")
Reported-by: Yuhao Jiang <danisjiang@gmail.com>
Assisted-by: Claude:claude-opus-5
Cc: stable@vger.kernel.org
Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
---
 drivers/gpu/drm/nouveau/nouveau_uvmm.c | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
index f5e4756b4de4..6404c54d097c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
@@ -85,6 +85,8 @@ struct uvmm_map_args {
 	u64 addr;
 	u64 range;
 	u8 kind;
+	/* Page size to give the new mapping, or 0 to derive it from the op. */
+	u8 page_shift;
 };
 
 static int
@@ -655,7 +657,8 @@ op_map_prepare(struct nouveau_uvmm *uvmm,
 
 	uvma->region = args->region;
 	uvma->kind = args->kind;
-	uvma->page_shift = select_page_shift(uvmm, op);
+	uvma->page_shift = args->page_shift ? args->page_shift :
+			   select_page_shift(uvmm, op);
 
 	drm_gpuva_map(&uvmm->base, &uvma->va, op);
 
@@ -684,8 +687,20 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm *uvmm,
 	struct drm_gpuva_op *op;
 	u64 vmm_get_start = args ? args->addr : 0;
 	u64 vmm_get_end = args ? args->addr + args->range : 0;
+	u8 map_page_shift = 0;
 	int ret;
 
+	/* A new mapping takes over the page tables of the mappings it replaces,
+	 * so every one of them has to be using its page size. The new mapping
+	 * is the last op drm_gpuvm_sm_map_ops_create() emits.
+	 */
+	if (args) {
+		struct drm_gpuva_op *last = drm_gpuva_last_op(ops);
+
+		if (last->op == DRM_GPUVA_OP_MAP)
+			map_page_shift = select_page_shift(uvmm, &last->map);
+	}
+
 	drm_gpuva_for_each_op(op, ops) {
 		switch (op->op) {
 		case DRM_GPUVA_OP_MAP: {
@@ -713,11 +728,22 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm *uvmm,
 			struct uvmm_map_args remap_args = {
 				.kind = uvma_from_va(va)->kind,
 				.region = uvma_from_va(va)->region,
+				/* The remainders of the split keep the page
+				 * tables of the mapping they are split from,
+				 * so they must keep its page size too.
+				 */
+				.page_shift = uvma_from_va(va)->page_shift,
 			};
 			u64 ustart = va->va.addr;
 			u64 urange = va->va.range;
 			u64 uend = ustart + urange;
 
+			if (map_page_shift &&
+			    uvma_from_va(va)->page_shift != map_page_shift) {
+				ret = -EINVAL;
+				goto unwind;
+			}
+
 			op_unmap_prepare(r->unmap);
 
 			if (r->prev) {
@@ -756,6 +782,11 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm *uvmm,
 			u64 uend = ustart + urange;
 			u8 page_shift = uvma_from_va(va)->page_shift;
 
+			if (map_page_shift && page_shift != map_page_shift) {
+				ret = -EINVAL;
+				goto unwind;
+			}
+
 			op_unmap_prepare(u);
 
 			if (!args)

-- 
2.51.2



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] drm/nouveau/uvmm: reject replace across page sizes
  2026-08-17  6:50 ` [PATCH 2/2] drm/nouveau/uvmm: reject replace across page sizes Junrui Luo via B4 Relay
@ 2026-09-17 19:56   ` lyude
  2026-09-17 22:44   ` Balbir Singh
  1 sibling, 0 replies; 8+ messages in thread
From: lyude @ 2026-09-17 19:56 UTC (permalink / raw)
  To: moonafterrain, Danilo Krummrich, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
	Andrew Morton, Balbir Singh, Mary Guillemard, Mohamed Ahmed,
	James Jones
  Cc: dri-devel, nouveau, linux-kernel, Yuhao Jiang, stable

Changes down below:

On Mon, 2026-08-17 at 14:50 +0800, Junrui Luo via B4 Relay wrote:
> From: Junrui Luo <moonafterrain@outlook.com>
> 
> A new mapping takes over the page tables of the mappings it replaces.
> nouveau_uvmm_sm_prepare() only acquires page tables for the range no
> existing mapping covers, and the map path frees the replaced mappings
> without putting their references. That is only valid while all of
> them
> use the same page size, which select_page_shift() no longer
> guarantees.
> 
> Rebinding a GART BO over a 2MiB VRAM BO therefore leaves the new
> mapping
> owning page tables built for a different page size, and it then maps
> at a
> size that was never referenced over that range. Since raw map does
> not
> allocate, nvkm_vmm_iter() can walk down to a NULL leaf and
> dereference
> it. The remainders of a split have the same problem: op_map_prepare()
> recomputes a page size with select_page_shift() while the remainder
> keeps
> the parent's page tables, so a parent that was itself downgraded can
> leave
> a remainder that re-aligns to a larger size. This happens on the
> unmap
> path too.
> 
> Reject the bind, and make split remainders inherit the page size of
> the
> mapping they are split from.
> 
> Fixes: c488a94e7e14 ("drm/nouveau/uvmm: Allow larger pages")
> Reported-by: Yuhao Jiang <danisjiang@gmail.com>
> Assisted-by: Claude:claude-opus-5
> Cc: stable@vger.kernel.org
> Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
> ---
>  drivers/gpu/drm/nouveau/nouveau_uvmm.c | 33
> ++++++++++++++++++++++++++++++++-
>  1 file changed, 32 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> index f5e4756b4de4..6404c54d097c 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> @@ -85,6 +85,8 @@ struct uvmm_map_args {
>  	u64 addr;
>  	u64 range;
>  	u8 kind;
> +	/* Page size to give the new mapping, or 0 to derive it from
> the op. */
> +	u8 page_shift;
>  };
>  
>  static int
> @@ -655,7 +657,8 @@ op_map_prepare(struct nouveau_uvmm *uvmm,
>  
>  	uvma->region = args->region;
>  	uvma->kind = args->kind;
> -	uvma->page_shift = select_page_shift(uvmm, op);
> +	uvma->page_shift = args->page_shift ? args->page_shift :
> +			   select_page_shift(uvmm, op);

This can just be:

   args->page_shift ?: select_page_shift(uvmm, op);

>  
>  	drm_gpuva_map(&uvmm->base, &uvma->va, op);
>  
> @@ -684,8 +687,20 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm
> *uvmm,
>  	struct drm_gpuva_op *op;
>  	u64 vmm_get_start = args ? args->addr : 0;
>  	u64 vmm_get_end = args ? args->addr + args->range : 0;
> +	u8 map_page_shift = 0;
>  	int ret;
>  
> +	/* A new mapping takes over the page tables of the mappings
> it replaces,
> +	 * so every one of them has to be using its page size. The
> new mapping
> +	 * is the last op drm_gpuvm_sm_map_ops_create() emits.
> +	 */
> +	if (args) {
> +		struct drm_gpuva_op *last = drm_gpuva_last_op(ops);
> +
> +		if (last->op == DRM_GPUVA_OP_MAP)
> +			map_page_shift = select_page_shift(uvmm,
> &last->map);
> +	}
> +
>  	drm_gpuva_for_each_op(op, ops) {
>  		switch (op->op) {
>  		case DRM_GPUVA_OP_MAP: {
> @@ -713,11 +728,22 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm
> *uvmm,
>  			struct uvmm_map_args remap_args = {
>  				.kind = uvma_from_va(va)->kind,
>  				.region = uvma_from_va(va)->region,
> +				/* The remainders of the split keep
> the page
> +				 * tables of the mapping they are
> split from,
> +				 * so they must keep its page size
> too.
> +				 */
> +				.page_shift = uvma_from_va(va)-
> >page_shift,
>  			};
>  			u64 ustart = va->va.addr;
>  			u64 urange = va->va.range;
>  			u64 uend = ustart + urange;
>  
> +			if (map_page_shift &&
> +			    uvma_from_va(va)->page_shift !=
> map_page_shift) {

Let's just take this value from remap_args instead of doing
uvma_from_va(va), it makes it a bit easier for people to understand
what's happening here.

With those nitpicks fixed:

Reviewed-by: Lyude Paul <lyude@redhat.com>

> +				ret = -EINVAL;
> +				goto unwind;
> +			}
> +
>  			op_unmap_prepare(r->unmap);
>  
>  			if (r->prev) {
> @@ -756,6 +782,11 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm
> *uvmm,
>  			u64 uend = ustart + urange;
>  			u8 page_shift = uvma_from_va(va)-
> >page_shift;
>  
> +			if (map_page_shift && page_shift !=
> map_page_shift) {
> +				ret = -EINVAL;
> +				goto unwind;
> +			}
> +
>  			op_unmap_prepare(u);
>  
>  			if (!args)


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] drm/nouveau/dmem: pin VRAM for the whole registered range
  2026-08-17  6:50 ` [PATCH 1/2] drm/nouveau/dmem: pin VRAM for the whole registered range Junrui Luo via B4 Relay
@ 2026-09-17 19:59   ` lyude
  2026-09-17 19:59   ` lyude
  2026-09-17 22:35   ` Balbir Singh
  2 siblings, 0 replies; 8+ messages in thread
From: lyude @ 2026-09-17 19:59 UTC (permalink / raw)
  To: moonafterrain, Danilo Krummrich, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
	Andrew Morton, Balbir Singh, Mary Guillemard, Mohamed Ahmed,
	James Jones
  Cc: dri-devel, nouveau, linux-kernel, Yuhao Jiang, stable

Reviewed-by: Lyude Paul <lyude@redhat.com>

On Mon, 2026-08-17 at 14:50 +0800, Junrui Luo via B4 Relay wrote:
> From: Junrui Luo <moonafterrain@outlook.com>
> 
> Commit c32287471077 ("gpu/drm/nouveau: enable THP support for GPU
> memory
> migration") grew the device-private region that
> nouveau_dmem_chunk_alloc() registers from DMEM_CHUNK_SIZE to
> DMEM_CHUNK_SIZE * NR_CHUNKS, but left the VRAM buffer object backing
> that
> region at DMEM_CHUNK_SIZE.
> 
> nouveau_dmem_page_addr() returns chunk->bo->offset plus the page's
> offset
> within the registered region, so every page past the first chunk
> resolves
> to VRAM outside the buffer object.
> 
> Size the buffer object to the region it backs.
> 
> Fixes: c32287471077 ("gpu/drm/nouveau: enable THP support for GPU
> memory migration")
> Reported-by: Yuhao Jiang <danisjiang@gmail.com>
> Assisted-by: Claude:claude-opus-5
> Cc: stable@vger.kernel.org
> Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
> ---
>  drivers/gpu/drm/nouveau/nouveau_dmem.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c
> b/drivers/gpu/drm/nouveau/nouveau_dmem.c
> index 9442ec6e1f6c..356ff8f3c1b8 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_dmem.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c
> @@ -325,8 +325,8 @@ nouveau_dmem_chunk_alloc(struct nouveau_drm *drm,
> struct page **ppage,
>  	chunk->pagemap.ops = &nouveau_dmem_pagemap_ops;
>  	chunk->pagemap.owner = drm->dev;
>  
> -	ret = nouveau_bo_new_pin(&drm->client,
> NOUVEAU_GEM_DOMAIN_VRAM, DMEM_CHUNK_SIZE,
> -				 &chunk->bo);
> +	ret = nouveau_bo_new_pin(&drm->client,
> NOUVEAU_GEM_DOMAIN_VRAM,
> +				 DMEM_CHUNK_SIZE * NR_CHUNKS,
> &chunk->bo);
>  	if (ret)
>  		goto out_release;
>  


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] drm/nouveau/dmem: pin VRAM for the whole registered range
  2026-08-17  6:50 ` [PATCH 1/2] drm/nouveau/dmem: pin VRAM for the whole registered range Junrui Luo via B4 Relay
  2026-09-17 19:59   ` lyude
@ 2026-09-17 19:59   ` lyude
  2026-09-17 22:35   ` Balbir Singh
  2 siblings, 0 replies; 8+ messages in thread
From: lyude @ 2026-09-17 19:59 UTC (permalink / raw)
  To: moonafterrain, Danilo Krummrich, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
	Andrew Morton, Balbir Singh, Mary Guillemard, Mohamed Ahmed,
	James Jones
  Cc: dri-devel, nouveau, linux-kernel, Yuhao Jiang, stable

Reviewed-by: Lyude Paul <lyude@redhat.com>

Will push patch 1 to drm-misc-fixes in just a moment

On Mon, 2026-08-17 at 14:50 +0800, Junrui Luo via B4 Relay wrote:
> From: Junrui Luo <moonafterrain@outlook.com>
> 
> Commit c32287471077 ("gpu/drm/nouveau: enable THP support for GPU
> memory
> migration") grew the device-private region that
> nouveau_dmem_chunk_alloc() registers from DMEM_CHUNK_SIZE to
> DMEM_CHUNK_SIZE * NR_CHUNKS, but left the VRAM buffer object backing
> that
> region at DMEM_CHUNK_SIZE.
> 
> nouveau_dmem_page_addr() returns chunk->bo->offset plus the page's
> offset
> within the registered region, so every page past the first chunk
> resolves
> to VRAM outside the buffer object.
> 
> Size the buffer object to the region it backs.
> 
> Fixes: c32287471077 ("gpu/drm/nouveau: enable THP support for GPU
> memory migration")
> Reported-by: Yuhao Jiang <danisjiang@gmail.com>
> Assisted-by: Claude:claude-opus-5
> Cc: stable@vger.kernel.org
> Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
> ---
>  drivers/gpu/drm/nouveau/nouveau_dmem.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c
> b/drivers/gpu/drm/nouveau/nouveau_dmem.c
> index 9442ec6e1f6c..356ff8f3c1b8 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_dmem.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c
> @@ -325,8 +325,8 @@ nouveau_dmem_chunk_alloc(struct nouveau_drm *drm,
> struct page **ppage,
>  	chunk->pagemap.ops = &nouveau_dmem_pagemap_ops;
>  	chunk->pagemap.owner = drm->dev;
>  
> -	ret = nouveau_bo_new_pin(&drm->client,
> NOUVEAU_GEM_DOMAIN_VRAM, DMEM_CHUNK_SIZE,
> -				 &chunk->bo);
> +	ret = nouveau_bo_new_pin(&drm->client,
> NOUVEAU_GEM_DOMAIN_VRAM,
> +				 DMEM_CHUNK_SIZE * NR_CHUNKS,
> &chunk->bo);
>  	if (ret)
>  		goto out_release;
>  


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] drm/nouveau/dmem: pin VRAM for the whole registered range
  2026-08-17  6:50 ` [PATCH 1/2] drm/nouveau/dmem: pin VRAM for the whole registered range Junrui Luo via B4 Relay
  2026-09-17 19:59   ` lyude
  2026-09-17 19:59   ` lyude
@ 2026-09-17 22:35   ` Balbir Singh
  2 siblings, 0 replies; 8+ messages in thread
From: Balbir Singh @ 2026-09-17 22:35 UTC (permalink / raw)
  To: moonafterrain, Lyude Paul, Danilo Krummrich, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
	Andrew Morton, Mary Guillemard, Mohamed Ahmed, James Jones
  Cc: dri-devel, nouveau, linux-kernel, Yuhao Jiang, stable

On 8/17/26 4:50 PM, Junrui Luo via B4 Relay wrote:
> From: Junrui Luo <moonafterrain@outlook.com>
> 
> Commit c32287471077 ("gpu/drm/nouveau: enable THP support for GPU memory
> migration") grew the device-private region that
> nouveau_dmem_chunk_alloc() registers from DMEM_CHUNK_SIZE to
> DMEM_CHUNK_SIZE * NR_CHUNKS, but left the VRAM buffer object backing that
> region at DMEM_CHUNK_SIZE.
> 
> nouveau_dmem_page_addr() returns chunk->bo->offset plus the page's offset
> within the registered region, so every page past the first chunk resolves
> to VRAM outside the buffer object.
> 
> Size the buffer object to the region it backs.
> 
> Fixes: c32287471077 ("gpu/drm/nouveau: enable THP support for GPU memory migration")
> Reported-by: Yuhao Jiang <danisjiang@gmail.com>
> Assisted-by: Claude:claude-opus-5
> Cc: stable@vger.kernel.org
> Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
> ---
>  drivers/gpu/drm/nouveau/nouveau_dmem.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c
> index 9442ec6e1f6c..356ff8f3c1b8 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_dmem.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c
> @@ -325,8 +325,8 @@ nouveau_dmem_chunk_alloc(struct nouveau_drm *drm, struct page **ppage,
>  	chunk->pagemap.ops = &nouveau_dmem_pagemap_ops;
>  	chunk->pagemap.owner = drm->dev;
>  
> -	ret = nouveau_bo_new_pin(&drm->client, NOUVEAU_GEM_DOMAIN_VRAM, DMEM_CHUNK_SIZE,
> -				 &chunk->bo);
> +	ret = nouveau_bo_new_pin(&drm->client, NOUVEAU_GEM_DOMAIN_VRAM,
> +				 DMEM_CHUNK_SIZE * NR_CHUNKS, &chunk->bo);
>  	if (ret)
>  		goto out_release;
>  
> 

Thanks for catching this!

Acked-by: Balbir Singh <balbirs@nvidia.com>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] drm/nouveau/uvmm: reject replace across page sizes
  2026-08-17  6:50 ` [PATCH 2/2] drm/nouveau/uvmm: reject replace across page sizes Junrui Luo via B4 Relay
  2026-09-17 19:56   ` lyude
@ 2026-09-17 22:44   ` Balbir Singh
  1 sibling, 0 replies; 8+ messages in thread
From: Balbir Singh @ 2026-09-17 22:44 UTC (permalink / raw)
  To: moonafterrain, Lyude Paul, Danilo Krummrich, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
	Andrew Morton, Mary Guillemard, Mohamed Ahmed, James Jones
  Cc: dri-devel, nouveau, linux-kernel, Yuhao Jiang, stable

On 8/17/26 4:50 PM, Junrui Luo via B4 Relay wrote:
> From: Junrui Luo <moonafterrain@outlook.com>
> 
> A new mapping takes over the page tables of the mappings it replaces.
> nouveau_uvmm_sm_prepare() only acquires page tables for the range no
> existing mapping covers, and the map path frees the replaced mappings
> without putting their references. That is only valid while all of them
> use the same page size, which select_page_shift() no longer guarantees.
> 
> Rebinding a GART BO over a 2MiB VRAM BO therefore leaves the new mapping
> owning page tables built for a different page size, and it then maps at a
> size that was never referenced over that range. Since raw map does not
> allocate, nvkm_vmm_iter() can walk down to a NULL leaf and dereference
> it. The remainders of a split have the same problem: op_map_prepare()
> recomputes a page size with select_page_shift() while the remainder keeps
> the parent's page tables, so a parent that was itself downgraded can leave
> a remainder that re-aligns to a larger size. This happens on the unmap
> path too.
> 
> Reject the bind, and make split remainders inherit the page size of the
> mapping they are split from.
> 
> Fixes: c488a94e7e14 ("drm/nouveau/uvmm: Allow larger pages")
> Reported-by: Yuhao Jiang <danisjiang@gmail.com>
> Assisted-by: Claude:claude-opus-5

We are moving to Assisted-by: LLM

> Cc: stable@vger.kernel.org
> Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
> ---
>  drivers/gpu/drm/nouveau/nouveau_uvmm.c | 33 ++++++++++++++++++++++++++++++++-
>  1 file changed, 32 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> index f5e4756b4de4..6404c54d097c 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
> @@ -85,6 +85,8 @@ struct uvmm_map_args {
>  	u64 addr;
>  	u64 range;
>  	u8 kind;
> +	/* Page size to give the new mapping, or 0 to derive it from the op. */
> +	u8 page_shift;
>  };
>  
>  static int
> @@ -655,7 +657,8 @@ op_map_prepare(struct nouveau_uvmm *uvmm,
>  
>  	uvma->region = args->region;
>  	uvma->kind = args->kind;
> -	uvma->page_shift = select_page_shift(uvmm, op);
> +	uvma->page_shift = args->page_shift ? args->page_shift :
> +			   select_page_shift(uvmm, op);
>  
>  	drm_gpuva_map(&uvmm->base, &uvma->va, op);
>  
> @@ -684,8 +687,20 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm *uvmm,
>  	struct drm_gpuva_op *op;
>  	u64 vmm_get_start = args ? args->addr : 0;
>  	u64 vmm_get_end = args ? args->addr + args->range : 0;
> +	u8 map_page_shift = 0;
>  	int ret;
>  
> +	/* A new mapping takes over the page tables of the mappings it replaces,
> +	 * so every one of them has to be using its page size. The new mapping
> +	 * is the last op drm_gpuvm_sm_map_ops_create() emits.
> +	 */
> +	if (args) {
> +		struct drm_gpuva_op *last = drm_gpuva_last_op(ops);
> +
> +		if (last->op == DRM_GPUVA_OP_MAP)
> +			map_page_shift = select_page_shift(uvmm, &last->map);
> +	}
> +
>  	drm_gpuva_for_each_op(op, ops) {
>  		switch (op->op) {
>  		case DRM_GPUVA_OP_MAP: {
> @@ -713,11 +728,22 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm *uvmm,
>  			struct uvmm_map_args remap_args = {
>  				.kind = uvma_from_va(va)->kind,
>  				.region = uvma_from_va(va)->region,
> +				/* The remainders of the split keep the page
> +				 * tables of the mapping they are split from,
> +				 * so they must keep its page size too.
> +				 */
> +				.page_shift = uvma_from_va(va)->page_shift,
>  			};
>  			u64 ustart = va->va.addr;
>  			u64 urange = va->va.range;
>  			u64 uend = ustart + urange;
>  
> +			if (map_page_shift &&
> +			    uvma_from_va(va)->page_shift != map_page_shift) {
> +				ret = -EINVAL;
> +				goto unwind;
> +			}
> +
>  			op_unmap_prepare(r->unmap);
>  
>  			if (r->prev) {
> @@ -756,6 +782,11 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm *uvmm,
>  			u64 uend = ustart + urange;
>  			u8 page_shift = uvma_from_va(va)->page_shift;
>  
> +			if (map_page_shift && page_shift != map_page_shift) {
> +				ret = -EINVAL;
> +				goto unwind;
> +			}
> +
>  			op_unmap_prepare(u);
>  
>  			if (!args)
> 

Just wondering how this was tested/caught?

Thanks,
Balbir

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-09-17 22:44 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-17  6:50 [PATCH 0/2] drm/nouveau: fix out-of-bounds VRAM access and VM_BIND page-size mismatch Junrui Luo via B4 Relay
2026-08-17  6:50 ` [PATCH 1/2] drm/nouveau/dmem: pin VRAM for the whole registered range Junrui Luo via B4 Relay
2026-09-17 19:59   ` lyude
2026-09-17 19:59   ` lyude
2026-09-17 22:35   ` Balbir Singh
2026-08-17  6:50 ` [PATCH 2/2] drm/nouveau/uvmm: reject replace across page sizes Junrui Luo via B4 Relay
2026-09-17 19:56   ` lyude
2026-09-17 22:44   ` Balbir Singh

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®