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 <[email protected]> --- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h | 1 + .../gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h index 06ca6235ff1b..63ea70e5c0e6 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h @@ -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 { diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c index a04102fd2fb7..741de3ded293 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c @@ -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; -- 2.43.0
