drm/amdkfd: clamp v9 CRIU control stack checkpoint copy to BO size

CRIU checkpoint copies the MQD control stack using cp_hqd_cntl_stack_size
from hardware without bounding it to the allocated BO region. If the HW
field is larger than the queue's control stack allocation, memcpy reads
past the BO into adjacent GTT memory and can leak kernel data to userspace.

Store the page-aligned control stack BO size in mqd_manager and clamp
checkpoint copies and reported checkpoint sizes to
min(cp_hqd_cntl_stack_size, mm->ctl_stack_size). Apply the same bound
for multi-XCC v9.4.3 checkpoint layout.

Signed-off-by: Yongqiang Sun <Yongqiang.Sun@amd.com>
Reviewed-by: David Francis <David.Francis@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
(cherry picked from commit 6c2abd0ec09e86c6323010673766f76050e28aa3)
Cc: stable@vger.kernel.org
This commit is contained in:
Yongqiang Sun 2026-06-02 09:47:19 -04:00 committed by Alex Deucher
parent ea772a440d
commit 426ffae6ec
2 changed files with 23 additions and 3 deletions

View File

@ -127,6 +127,7 @@ struct mqd_manager {
struct mutex mqd_mutex;
struct kfd_node *dev;
uint32_t mqd_size;
uint32_t ctl_stack_size;
};
struct mqd_user_context_save_area_header {

View File

@ -27,6 +27,7 @@
#include <linux/uaccess.h>
#include "kfd_priv.h"
#include "kfd_mqd_manager.h"
#include "kfd_topology.h"
#include "v9_structs.h"
#include "gc/gc_9_0_offset.h"
#include "gc/gc_9_0_sh_mask.h"
@ -411,8 +412,11 @@ static int get_wave_state(struct mqd_manager *mm, void *mqd,
static int get_checkpoint_info(struct mqd_manager *mm, void *mqd, u32 *ctl_stack_size)
{
struct v9_mqd *m = get_mqd(mqd);
u32 per_xcc_size;
if (check_mul_overflow(m->cp_hqd_cntl_stack_size, NUM_XCC(mm->dev->xcc_mask), ctl_stack_size))
per_xcc_size = min_t(u32, m->cp_hqd_cntl_stack_size, mm->ctl_stack_size);
if (check_mul_overflow(per_xcc_size, NUM_XCC(mm->dev->xcc_mask), ctl_stack_size))
return -EINVAL;
return 0;
@ -421,13 +425,15 @@ static int get_checkpoint_info(struct mqd_manager *mm, void *mqd, u32 *ctl_stack
static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, void *ctl_stack_dst)
{
struct v9_mqd *m;
u32 ctl_stack_copy_size;
/* Control stack is located one page after MQD. */
void *ctl_stack = (void *)((uintptr_t)mqd + AMDGPU_GPU_PAGE_SIZE);
m = get_mqd(mqd);
ctl_stack_copy_size = min_t(u32, m->cp_hqd_cntl_stack_size, mm->ctl_stack_size);
memcpy(mqd_dst, m, sizeof(struct v9_mqd));
memcpy(ctl_stack_dst, ctl_stack, m->cp_hqd_cntl_stack_size);
memcpy(ctl_stack_dst, ctl_stack, ctl_stack_copy_size);
}
static void checkpoint_mqd_v9_4_3(struct mqd_manager *mm,
@ -436,15 +442,19 @@ static void checkpoint_mqd_v9_4_3(struct mqd_manager *mm,
void *ctl_stack_dst)
{
struct v9_mqd *m;
u32 ctl_stack_stride;
int xcc;
uint64_t size = get_mqd(mqd)->cp_mqd_stride_size;
ctl_stack_stride = min_t(u32, get_mqd(mqd)->cp_hqd_cntl_stack_size,
mm->ctl_stack_size);
for (xcc = 0; xcc < NUM_XCC(mm->dev->xcc_mask); xcc++) {
m = get_mqd(mqd + size * xcc);
checkpoint_mqd(mm, m,
(uint8_t *)mqd_dst + sizeof(*m) * xcc,
(uint8_t *)ctl_stack_dst + m->cp_hqd_cntl_stack_size * xcc);
(uint8_t *)ctl_stack_dst + ctl_stack_stride * xcc);
}
}
@ -998,6 +1008,15 @@ struct mqd_manager *mqd_manager_init_v9(enum KFD_MQD_TYPE type,
mqd->is_occupied = kfd_is_occupied_cp;
mqd->get_checkpoint_info = get_checkpoint_info;
mqd->mqd_size = sizeof(struct v9_mqd);
if (dev->kfd->cwsr_enabled) {
struct kfd_topology_device *topo_dev;
topo_dev = kfd_topology_device_by_id(dev->id);
if (topo_dev)
mqd->ctl_stack_size =
ALIGN(topo_dev->node_props.ctl_stack_size,
AMDGPU_GPU_PAGE_SIZE);
}
mqd->mqd_stride = mqd_stride_v9;
#if defined(CONFIG_DEBUG_FS)
mqd->debugfs_show_mqd = debugfs_show_mqd;