dri-devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] accel/amdxdna: Bound the page count of a user supplied buffer
@ 2026-08-26 19:57 Taimuraz Kaitmazov
  2026-08-26 20:09 ` sashiko-bot
  2026-08-26 21:20 ` Lizhi Hou
  0 siblings, 2 replies; 6+ messages in thread
From: Taimuraz Kaitmazov @ 2026-08-26 19:57 UTC (permalink / raw)
  To: Lizhi Hou, Min Ma, Oded Gabbay; +Cc: taimuraz, dri-devel, linux-kernel

amdxdna_get_ubuf() puts a per-entry page count derived from a __u64
va_ent[i].len into a u32, then passes it to pin_user_pages_fast(), whose
nr_pages is an int. An entry of 2^44 bytes truncates npages to zero, so
nothing is pinned, the ret != npages test still passes, and ubuf->pages
keeps whatever kvmalloc_objs() returned while ubuf->nr_pages describes the
untruncated count. amdxdna_ubuf_release() then walks all of it. Reaching
that needs CAP_IPC_LOCK and a multi-gigabyte kvmalloc() to succeed.

Reject a total that does not fit in an int. The lengths are page aligned
and summed with check_add_overflow(), so the total is at least as large as
any one entry and bounds the pin call, the offset accumulator and
sg_alloc_table_from_pages().

Fixes: bd72d4acda10 ("accel/amdxdna: Support user space allocated buffer")
Signed-off-by: Taimuraz Kaitmazov <taimuraz@kaitmazov.com>
---
 drivers/accel/amdxdna/amdxdna_ubuf.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c
index 0e0cd69cd1fb..da8e32566ae0 100644
--- a/drivers/accel/amdxdna/amdxdna_ubuf.c
+++ b/drivers/accel/amdxdna/amdxdna_ubuf.c
@@ -125,6 +125,12 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
 	}
 
 	ubuf->nr_pages = exp_info.size >> PAGE_SHIFT;
+	if (ubuf->nr_pages > INT_MAX) {
+		XDNA_ERR(xdna, "Too many pages %lld", ubuf->nr_pages);
+		ret = -EINVAL;
+		goto free_ent;
+	}
+
 	lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
 	new_pinned = atomic64_add_return(ubuf->nr_pages, &ubuf->mm->pinned_vm);
 	if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) {
-- 
2.55.0


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

* Re: [PATCH] accel/amdxdna: Bound the page count of a user supplied buffer
  2026-08-26 19:57 [PATCH] accel/amdxdna: Bound the page count of a user supplied buffer Taimuraz Kaitmazov
@ 2026-08-26 20:09 ` sashiko-bot
  2026-08-26 21:20 ` Lizhi Hou
  1 sibling, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-08-26 20:09 UTC (permalink / raw)
  To: Taimuraz Kaitmazov; +Cc: dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Unhandled zero-size user buffer bypasses constraints and leads to a fatal kernel panic due to ZERO_SIZE_PTR dereference.
--

commit 0ce5a2a6bc3b9a95a5dd641e22f058018b729ead
Author: Taimuraz Kaitmazov <taimuraz@kaitmazov.com>

accel/amdxdna: Bound the page count of a user supplied buffer

amdxdna_get_ubuf() puts a per-entry page count derived from a __u64
va_ent[i].len into a u32, then passes it to pin_user_pages_fast().
This patch rejects a total that does not fit in an int to prevent
truncation and unbound memory operations.

> diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c
> index 0e0cd69cd1fbf..da8e32566ae0a 100644
> --- a/drivers/accel/amdxdna/amdxdna_ubuf.c
> +++ b/drivers/accel/amdxdna/amdxdna_ubuf.c
> @@ -125,6 +125,12 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
>  	}
>  
>  	ubuf->nr_pages = exp_info.size >> PAGE_SHIFT;
> +	if (ubuf->nr_pages > INT_MAX) {
> +		XDNA_ERR(xdna, "Too many pages %lld", ubuf->nr_pages);
> +		ret = -EINVAL;
> +		goto free_ent;
> +	}
> +

[Severity: Critical]
This isn't a bug introduced by this patch, but is it possible for
ubuf->nr_pages to be 0 here, leading to a kernel panic later?

If a user calls the AMDXDNA_DRM_CREATE_BO ioctl with a virtual address
table containing at least one entry but with lengths set to 0,
exp_info.size becomes 0 and ubuf->nr_pages becomes 0. This bypasses the
bounds check above.

Then kvmalloc_objs() allocates ubuf->pages with size 0, which returns
ZERO_SIZE_PTR (0x10) instead of NULL. Later, pin_user_pages_fast() handles
npages = 0 by returning 0, allowing the zero-length dma-buf to be
exported.

When the buffer is eventually mapped in amdxdna_ubuf_map() in
amdxdna_ubuf.c:

ret = sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->nr_pages, 0,
                                ubuf->nr_pages << PAGE_SHIFT, GFP_KERNEL);

sg_alloc_table_from_pages() delegates to sg_alloc_append_table_from_pages(),
which computes chunks = 1 and evaluates pages[0] in its first iteration
since the nested loop bounds check is skipped when n_pages == 0.

Dereferencing ZERO_SIZE_PTR (0x10) will cause a fatal kernel page fault.

Could we add a check for ubuf->nr_pages == 0 here as well?

>  	lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
>  	new_pinned = atomic64_add_return(ubuf->nr_pages, &ubuf->mm->pinned_vm);
>  	if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) {

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826195736.358579-1-taimuraz@kaitmazov.com?part=1

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

* Re: [PATCH] accel/amdxdna: Bound the page count of a user supplied buffer
  2026-08-26 19:57 [PATCH] accel/amdxdna: Bound the page count of a user supplied buffer Taimuraz Kaitmazov
  2026-08-26 20:09 ` sashiko-bot
@ 2026-08-26 21:20 ` Lizhi Hou
  2026-08-26 21:28   ` [PATCH v2] " Taimuraz Kaitmazov
  1 sibling, 1 reply; 6+ messages in thread
From: Lizhi Hou @ 2026-08-26 21:20 UTC (permalink / raw)
  To: Taimuraz Kaitmazov, Min Ma, Oded Gabbay; +Cc: dri-devel, linux-kernel


On 8/26/26 12:57, Taimuraz Kaitmazov wrote:
> amdxdna_get_ubuf() puts a per-entry page count derived from a __u64
> va_ent[i].len into a u32, then passes it to pin_user_pages_fast(), whose
> nr_pages is an int. An entry of 2^44 bytes truncates npages to zero, so
> nothing is pinned, the ret != npages test still passes, and ubuf->pages
> keeps whatever kvmalloc_objs() returned while ubuf->nr_pages describes the
> untruncated count. amdxdna_ubuf_release() then walks all of it. Reaching
> that needs CAP_IPC_LOCK and a multi-gigabyte kvmalloc() to succeed.
>
> Reject a total that does not fit in an int. The lengths are page aligned
> and summed with check_add_overflow(), so the total is at least as large as
> any one entry and bounds the pin call, the offset accumulator and
> sg_alloc_table_from_pages().
>
> Fixes: bd72d4acda10 ("accel/amdxdna: Support user space allocated buffer")
> Signed-off-by: Taimuraz Kaitmazov <taimuraz@kaitmazov.com>
> ---
>   drivers/accel/amdxdna/amdxdna_ubuf.c | 6 ++++++
>   1 file changed, 6 insertions(+)
>
> diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c
> index 0e0cd69cd1fb..da8e32566ae0 100644
> --- a/drivers/accel/amdxdna/amdxdna_ubuf.c
> +++ b/drivers/accel/amdxdna/amdxdna_ubuf.c
> @@ -125,6 +125,12 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
>   	}
>   
>   	ubuf->nr_pages = exp_info.size >> PAGE_SHIFT;
> +	if (ubuf->nr_pages > INT_MAX) {
> +		XDNA_ERR(xdna, "Too many pages %lld", ubuf->nr_pages);

XDNA_DBG(.... %llu", )

Could you also add the boundary check sashiko suggested? Maybe something 
like:

         for (i = 0, exp_info.size = 0; i < num_entries; i++) {
                 if (!IS_ALIGNED(va_ent[i].vaddr, PAGE_SIZE) ||
-                   !IS_ALIGNED(va_ent[i].len, PAGE_SIZE)) {
-                       XDNA_ERR(xdna, "Invalid address or len %llx, %llx",
+                   !IS_ALIGNED(va_ent[i].len, PAGE_SIZE) ||
+                   !va_ent[i].len) {
+                       XDNA_DBG(xdna, "Invalid address or len %llx, %llx",
                                  va_ent[i].vaddr, va_ent[i].len);

Thanks,

Lizhi


> +		ret = -EINVAL;
> +		goto free_ent;
> +	}
> +
>   	lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
>   	new_pinned = atomic64_add_return(ubuf->nr_pages, &ubuf->mm->pinned_vm);
>   	if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) {

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

* [PATCH v2] accel/amdxdna: Bound the page count of a user supplied buffer
  2026-08-26 21:20 ` Lizhi Hou
@ 2026-08-26 21:28   ` Taimuraz Kaitmazov
  2026-08-26 21:43     ` sashiko-bot
  2026-08-26 23:04     ` Lizhi Hou
  0 siblings, 2 replies; 6+ messages in thread
From: Taimuraz Kaitmazov @ 2026-08-26 21:28 UTC (permalink / raw)
  To: Lizhi Hou, Min Ma, Oded Gabbay; +Cc: taimuraz, dri-devel, linux-kernel

amdxdna_get_ubuf() puts a per-entry page count derived from a __u64
va_ent[i].len into a u32, then passes it to pin_user_pages_fast(), whose
nr_pages is an int. An entry of 2^44 bytes truncates npages to zero, so
nothing is pinned, the ret != npages test still passes, and ubuf->pages
keeps whatever kvmalloc_objs() returned while ubuf->nr_pages describes the
untruncated count. amdxdna_ubuf_release() then walks all of it. Reaching
that needs CAP_IPC_LOCK and a multi-gigabyte kvmalloc() to succeed.

Reject a total that does not fit in an int. The lengths are page aligned
and summed with check_add_overflow(), so the total is at least as large as
any one entry and bounds the pin call, the offset accumulator and
sg_alloc_table_from_pages().

Reject a zero length entry as well: it contributes nothing to the mapping
and a table of them leaves nr_pages at zero.

Fixes: bd72d4acda10 ("accel/amdxdna: Support user space allocated buffer")
Signed-off-by: Taimuraz Kaitmazov <taimuraz@kaitmazov.com>
---
v2:
- XDNA_DBG and %llu, per your comment.
- Reject a zero length entry in the validation loop, and lower that log to
  XDNA_DBG too, as you suggested.

 drivers/accel/amdxdna/amdxdna_ubuf.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c
index 0e0cd69cd1fb..bf1e4dd7bbc3 100644
--- a/drivers/accel/amdxdna/amdxdna_ubuf.c
+++ b/drivers/accel/amdxdna/amdxdna_ubuf.c
@@ -111,8 +111,9 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
 
 	for (i = 0, exp_info.size = 0; i < num_entries; i++) {
 		if (!IS_ALIGNED(va_ent[i].vaddr, PAGE_SIZE) ||
-		    !IS_ALIGNED(va_ent[i].len, PAGE_SIZE)) {
-			XDNA_ERR(xdna, "Invalid address or len %llx, %llx",
+		    !IS_ALIGNED(va_ent[i].len, PAGE_SIZE) ||
+		    !va_ent[i].len) {
+			XDNA_DBG(xdna, "Invalid address or len %llx, %llx",
 				 va_ent[i].vaddr, va_ent[i].len);
 			ret = -EINVAL;
 			goto free_ent;
@@ -125,6 +126,12 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
 	}
 
 	ubuf->nr_pages = exp_info.size >> PAGE_SHIFT;
+	if (ubuf->nr_pages > INT_MAX) {
+		XDNA_DBG(xdna, "Too many pages %llu", ubuf->nr_pages);
+		ret = -EINVAL;
+		goto free_ent;
+	}
+
 	lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
 	new_pinned = atomic64_add_return(ubuf->nr_pages, &ubuf->mm->pinned_vm);
 	if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) {
-- 
2.55.0


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

* Re: [PATCH v2] accel/amdxdna: Bound the page count of a user supplied buffer
  2026-08-26 21:28   ` [PATCH v2] " Taimuraz Kaitmazov
@ 2026-08-26 21:43     ` sashiko-bot
  2026-08-26 23:04     ` Lizhi Hou
  1 sibling, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-08-26 21:43 UTC (permalink / raw)
  To: Taimuraz Kaitmazov; +Cc: dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Unrestricted userspace-controlled allocation size for `va_ent` can trigger the OOM killer.
--

commit 9b9a5c5cd1330d0aae0aea71c45dc018b59ee7e1
Author: Taimuraz Kaitmazov <taimuraz@kaitmazov.com>

accel/amdxdna: Bound the page count of a user supplied buffer

This patch fixes an issue where amdxdna_get_ubuf() could truncate a per-entry
page count derived from a 64-bit length into a 32-bit integer. It adds
validation to ensure the total page count fits within an integer, preventing
invalid memory accesses. It also explicitly rejects zero-length buffer
entries.

> diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c
> index 0e0cd69cd1fbf..bf1e4dd7bbc30 100644
> --- a/drivers/accel/amdxdna/amdxdna_ubuf.c
> +++ b/drivers/accel/amdxdna/amdxdna_ubuf.c
> @@ -111,8 +111,9 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
>  
>  	for (i = 0, exp_info.size = 0; i < num_entries; i++) {

[Severity: High]
This is a pre-existing issue, but is it possible for num_entries to be a
massive value here?

Looking at amdxdna_gem_create_ubuf_object() in
drivers/accel/amdxdna/amdxdna_gem.c, it appears that va_tbl.num_entries
from userspace is passed directly to amdxdna_get_ubuf() without any bounds
checking.

Earlier in amdxdna_get_ubuf(), we allocate memory based on this value:

	va_ent = kvzalloc_objs(*va_ent, num_entries);

If an unprivileged user with a non-zero RLIMIT_MEMLOCK sets num_entries to a
huge value like U32_MAX, can this allocate an unrestricted amount of memory
and trigger the OOM killer before we even reach the bounds checks on nr_pages
added in this patch?

>  		if (!IS_ALIGNED(va_ent[i].vaddr, PAGE_SIZE) ||
> -		    !IS_ALIGNED(va_ent[i].len, PAGE_SIZE)) {
> -			XDNA_ERR(xdna, "Invalid address or len %llx, %llx",
> +		    !IS_ALIGNED(va_ent[i].len, PAGE_SIZE) ||
> +		    !va_ent[i].len) {
> +			XDNA_DBG(xdna, "Invalid address or len %llx, %llx",
>  				 va_ent[i].vaddr, va_ent[i].len);
>  			ret = -EINVAL;
>  			goto free_ent;

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826212825.408846-1-taimuraz@kaitmazov.com?part=1

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

* Re: [PATCH v2] accel/amdxdna: Bound the page count of a user supplied buffer
  2026-08-26 21:28   ` [PATCH v2] " Taimuraz Kaitmazov
  2026-08-26 21:43     ` sashiko-bot
@ 2026-08-26 23:04     ` Lizhi Hou
  1 sibling, 0 replies; 6+ messages in thread
From: Lizhi Hou @ 2026-08-26 23:04 UTC (permalink / raw)
  To: Taimuraz Kaitmazov, Min Ma, Oded Gabbay; +Cc: dri-devel, linux-kernel


On 8/26/26 14:28, Taimuraz Kaitmazov wrote:
> amdxdna_get_ubuf() puts a per-entry page count derived from a __u64
> va_ent[i].len into a u32, then passes it to pin_user_pages_fast(), whose
> nr_pages is an int. An entry of 2^44 bytes truncates npages to zero, so
> nothing is pinned, the ret != npages test still passes, and ubuf->pages
> keeps whatever kvmalloc_objs() returned while ubuf->nr_pages describes the
> untruncated count. amdxdna_ubuf_release() then walks all of it. Reaching
> that needs CAP_IPC_LOCK and a multi-gigabyte kvmalloc() to succeed.
>
> Reject a total that does not fit in an int. The lengths are page aligned
> and summed with check_add_overflow(), so the total is at least as large as
> any one entry and bounds the pin call, the offset accumulator and
> sg_alloc_table_from_pages().
>
> Reject a zero length entry as well: it contributes nothing to the mapping
> and a table of them leaves nr_pages at zero.
>
> Fixes: bd72d4acda10 ("accel/amdxdna: Support user space allocated buffer")
> Signed-off-by: Taimuraz Kaitmazov <taimuraz@kaitmazov.com>
> ---
> v2:
> - XDNA_DBG and %llu, per your comment.
> - Reject a zero length entry in the validation loop, and lower that log to
>    XDNA_DBG too, as you suggested.
>
>   drivers/accel/amdxdna/amdxdna_ubuf.c | 11 +++++++++--
>   1 file changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c
> index 0e0cd69cd1fb..bf1e4dd7bbc3 100644
> --- a/drivers/accel/amdxdna/amdxdna_ubuf.c
> +++ b/drivers/accel/amdxdna/amdxdna_ubuf.c
> @@ -111,8 +111,9 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
>   
>   	for (i = 0, exp_info.size = 0; i < num_entries; i++) {
>   		if (!IS_ALIGNED(va_ent[i].vaddr, PAGE_SIZE) ||
> -		    !IS_ALIGNED(va_ent[i].len, PAGE_SIZE)) {
> -			XDNA_ERR(xdna, "Invalid address or len %llx, %llx",
> +		    !IS_ALIGNED(va_ent[i].len, PAGE_SIZE) ||
> +		    !va_ent[i].len) {
> +			XDNA_DBG(xdna, "Invalid address or len %llx, %llx",
>   				 va_ent[i].vaddr, va_ent[i].len);
>   			ret = -EINVAL;
>   			goto free_ent;
> @@ -125,6 +126,12 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
>   	}
>   
>   	ubuf->nr_pages = exp_info.size >> PAGE_SHIFT;
> +	if (ubuf->nr_pages > INT_MAX) {
> +		XDNA_DBG(xdna, "Too many pages %llu", ubuf->nr_pages);
> +		ret = -EINVAL;
> +		goto free_ent;
> +	}
> +
Reviewed-by: Lizhi Hou <lizhi.hou@amd.com>
>   	lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
>   	new_pinned = atomic64_add_return(ubuf->nr_pages, &ubuf->mm->pinned_vm);
>   	if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) {

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

end of thread, other threads:[~2026-08-26 23:04 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26 19:57 [PATCH] accel/amdxdna: Bound the page count of a user supplied buffer Taimuraz Kaitmazov
2026-08-26 20:09 ` sashiko-bot
2026-08-26 21:20 ` Lizhi Hou
2026-08-26 21:28   ` [PATCH v2] " Taimuraz Kaitmazov
2026-08-26 21:43     ` sashiko-bot
2026-08-26 23:04     ` Lizhi Hou

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