accel/rocket: Validate task regcmd address and count on submission

The regcmd fields in drm_rocket_task come from userspace and are
programmed into the PC unit without any validation.

Bits 31:4 of PC_BASE_ADDRESS hold the register command DMA address
and bit 0 selects slave mode, so a misaligned regcmd silently drops
its low bits, and an odd address flips the PC unit into slave mode.
Similarly, pc_data_amount is a 16-bit field holding
(regcmd_count + 1) / 2 - 1, so a larger regcmd_count is silently
truncated by the register encoding.

Reject unaligned regcmd addresses and out-of-range regcmd_count with
-EINVAL at submission time. Existing userspace is not affected: Mesa
places regcmd buffers at 64-byte aligned offsets, and its regcmd
counts stay far below the limit.

Signed-off-by: Sidong Yang <sidong.yang@furiosa.ai>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Link: https://patch.msgid.link/20261004164727.466164-4-sidong.yang@furiosa.ai
This commit is contained in:
Sidong Yang 2026-10-05 01:47:27 +09:00 committed by Heiko Stuebner
parent f31a7b1f27
commit 725953d4da

View File

@ -7,6 +7,7 @@
#include <drm/drm_file.h>
#include <drm/drm_gem.h>
#include <drm/rocket_accel.h>
#include <linux/align.h>
#include <linux/interrupt.h>
#include <linux/overflow.h>
#include <linux/iommu.h>
@ -21,6 +22,15 @@
#define JOB_TIMEOUT_MS 500
/*
* The PC unit fetches two 64-bit register commands per pc_data_amount unit,
* and the field holds (regcmd_count + 1) / 2 - 1.
*/
#define ROCKET_MAX_REGCMDS ((PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT__MASK + 1) * 2U)
/* Bits 3:0 of PC_BASE_ADDRESS hold the mode selection bit and reserved bits */
#define ROCKET_REGCMD_ALIGN 16
static struct rocket_job *
to_rocket_job(struct drm_sched_job *sched_job)
{
@ -95,6 +105,20 @@ rocket_copy_tasks(struct drm_device *dev,
goto fail;
}
if (task.regcmd_count > ROCKET_MAX_REGCMDS) {
drm_dbg(dev, "regcmd_count field in drm_rocket_task should be <= %u.\n",
ROCKET_MAX_REGCMDS);
ret = -EINVAL;
goto fail;
}
if (!IS_ALIGNED(task.regcmd, ROCKET_REGCMD_ALIGN)) {
drm_dbg(dev, "regcmd field in drm_rocket_task should be aligned to %u bytes.\n",
ROCKET_REGCMD_ALIGN);
ret = -EINVAL;
goto fail;
}
rjob->tasks[i].regcmd = task.regcmd;
rjob->tasks[i].regcmd_count = task.regcmd_count;
}