When allocating memory for a BO list array, the multiplication
bo_number * info_size may overflow on 32-bit systems if userspace
supplies large values. This could lead to allocating a smaller buffer
than expected, followed by a memset or copy_from_user that writes
beyond the allocated memory, potentially causing memory corruption or
information disclosure.

Add an overflow check using check_mul_overflow to detect such cases.
Also ensure the resulting allocation size does not exceed INT_MAX,
as the subsequent user copy operations may rely on this limit.
Return -EINVAL if either condition fails.

A crash log illustrating the issue:

[ 2943.053706] RIP: 0010:__kvmalloc_node_noprof+0x5be/0x8a0
...
[ 2943.053725] Call Trace:
[ 2943.053728] amdgpu_bo_create_list_entry_array+0x42/0x130 [amdgpu]
[ 2943.053947] amdgpu_bo_list_ioctl+0x51/0x300 [amdgpu]
[ 2943.054277] drm_ioctl+0x2cb/0x5a0 [drm]
[ 2943.054379] __x64_sys_ioctl+0x9e/0xf0

The overflow occurs in the allocation inside
amdgpu_bo_create_list_entry_array, leading to a crash in
vmemdup_user (via __kvmalloc_node_noprof).

Signed-off-by: Jesse.Zhang <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
index 87ec46c56a6e..efab39ba7f51 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
@@ -29,6 +29,7 @@
  */
 
 #include <linux/sort.h>
+#include <linux/overflow.h>
 #include <linux/uaccess.h>
 
 #include "amdgpu.h"
@@ -187,6 +188,11 @@ int amdgpu_bo_create_list_entry_array(struct 
drm_amdgpu_bo_list_in *in,
        const uint32_t bo_info_size = in->bo_info_size;
        const uint32_t bo_number = in->bo_number;
        struct drm_amdgpu_bo_list_entry *info;
+       size_t alloc_size;
+
+       if (check_mul_overflow((size_t)bo_number, (size_t)info_size,
+                              &alloc_size) || alloc_size > INT_MAX)
+               return -EINVAL;
 
        /* copy the handle array from userspace to a kernel buffer */
        if (likely(info_size == bo_info_size)) {
@@ -201,7 +207,7 @@ int amdgpu_bo_create_list_entry_array(struct 
drm_amdgpu_bo_list_in *in,
                if (!info)
                        return -ENOMEM;
 
-               memset(info, 0, bo_number * info_size);
+               memset(info, 0, alloc_size);
                for (i = 0; i < bo_number; ++i, uptr += bo_info_size) {
                        if (copy_from_user(&info[i], uptr, bytes)) {
                                kvfree(info);
-- 
2.49.0

Reply via email to