accel/rocket: fix NULL dereference and integer overflow in rocket_job_push()

rocket_job_push() allocates a temporary array to hold all input and
output GEM object pointers:

    bos = kvmalloc_array(job->in_bo_count + job->out_bo_count,
                         sizeof(void *), GFP_KERNEL);
    memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *));
    memcpy(&bos[job->in_bo_count], job->out_bos, ...);

Two bugs exist:

1. Missing NULL check: if kvmalloc_array() fails, bos is NULL and
   the subsequent memcpy() dereferences it, causing a kernel NULL
   pointer dereference.

2. Integer overflow: in_bo_count and out_bo_count are both u32, set
   directly from userspace-supplied in_bo_handle_count and
   out_bo_handle_count with no prior validation. Their sum is computed
   in u32 arithmetic and can wrap to a smaller value, causing the
   allocation count passed to kvmalloc_array() to be smaller than
   intended. Subsequent uses still operate on the original counts when
   copying and locking objects, which may lead to out-of-bounds accesses
   on the temporary array.

Fix by using check_add_overflow() to detect count overflow before the
allocation, and adding a NULL check on the allocation result.

Fixes: 0810d5ad88 ("accel/rocket: Add job submission IOCTL")
Cc: stable@vger.kernel.org
Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
Link: https://lore.kernel.org/r/20260524155716.90955-1-meatuni001@gmail.com
Signed-off-by: Tomeu Vizoso <tomeu@tomeuvizoso.net>
This commit is contained in:
Muhammad Bilal 2026-05-24 15:57:16 +00:00 committed by Tomeu Vizoso
parent 70e6a33d68
commit a85402bff2

View File

@ -8,6 +8,7 @@
#include <drm/drm_gem.h>
#include <drm/rocket_accel.h>
#include <linux/interrupt.h>
#include <linux/overflow.h>
#include <linux/iommu.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@ -189,14 +190,19 @@ static int rocket_job_push(struct rocket_job *job)
struct rocket_device *rdev = job->rdev;
struct drm_gem_object **bos;
struct ww_acquire_ctx acquire_ctx;
u32 bo_count;
int ret = 0;
bos = kvmalloc_array(job->in_bo_count + job->out_bo_count, sizeof(void *),
GFP_KERNEL);
if (check_add_overflow(job->in_bo_count, job->out_bo_count, &bo_count))
return -EINVAL;
bos = kvmalloc_array(bo_count, sizeof(*bos), GFP_KERNEL);
if (!bos)
return -ENOMEM;
memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *));
memcpy(&bos[job->in_bo_count], job->out_bos, job->out_bo_count * sizeof(void *));
ret = drm_gem_lock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
ret = drm_gem_lock_reservations(bos, bo_count, &acquire_ctx);
if (ret)
goto err;
@ -221,7 +227,7 @@ static int rocket_job_push(struct rocket_job *job)
rocket_attach_object_fences(job->out_bos, job->out_bo_count, job->inference_done_fence);
err_unlock:
drm_gem_unlock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
drm_gem_unlock_reservations(bos, bo_count, &acquire_ctx);
err:
kvfree(bos);