> + if (ctl_stack_size > qp->ctl_stack_size) {
This looks wrong to me. Tracking both of these values back to their origin,
ctl_stack_size comes from the CRIU private queue data, and qp->ctl_stack_size
is filled in by set_queue_properties_from_criu, also from the CRIU private
queue data.
So this check is useless.
David Francis
________________________________________
From: amd-gfx <[email protected]> on behalf of Yongqiang
Sun <[email protected]>
Sent: Tuesday, June 23, 2026 3:39 PM
To: [email protected]
Cc: Sun, Yongqiang
Subject: [PATCH] drm/amdkfd: bound v9 CRIU control stack restore to allocated
MQD region
During CRIU restore, restore_mqd() copies the MQD control stack using a
size derived from the untrusted, user-supplied q_data->ctl_stack_size. On
multi-XCC GFX9.4.3 parts the divisor used to size the MQD allocation can
differ from the one used for the copy, so the memcpy at the fixed
+AMDGPU_GPU_PAGE_SIZE offset can write attacker-controlled bytes past the
TTM/BO-backed MQD buffer into adjacent kernel memory, allowing local
privilege escalation (requires CAP_CHECKPOINT_RESTORE and /dev/kfd).
Clamp the control stack copy in restore_mqd() to qp->ctl_stack_size, the
page-aligned region allocate_mqd() actually reserves, and reject oversized
q_data->ctl_stack_size early in kfd_criu_restore_queue() by bounding the
per-XCC size to the node's advertised control stack size.
Signed-off-by: Yongqiang Sun <[email protected]>
---
.../gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c | 16 +++++++++++-
.../amd/amdkfd/kfd_process_queue_manager.c | 26 +++++++++++++++++++
2 files changed, 41 insertions(+), 1 deletion(-)
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 ce379ab17916..9117fbae0a1a 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
@@ -453,8 +453,22 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
if (gart_addr)
*gart_addr = addr;
- /* Control stack is located one page after MQD. */
+ /*
+ * Control stack is located one page after the MQD. allocate_mqd()
+ * sized this region from qp->ctl_stack_size (page aligned). On the
+ * CRIU restore path ctl_stack_size is derived from the untrusted,
+ * user-supplied q_data->ctl_stack_size and, on multi-XCC parts, from
+ * a divisor that may not match the one used to size the allocation.
+ * Bound the copy to the allocated region so a malicious checkpoint
+ * cannot write past the MQD buffer object into adjacent kernel
+ * (TTM/GTT) memory.
+ */
ctl_stack = (void *)((uintptr_t)*mqd + AMDGPU_GPU_PAGE_SIZE);
+ if (ctl_stack_size > qp->ctl_stack_size) {
+ pr_err_ratelimited("ctl_stack_size 0x%x exceeds allocated 0x%x,
clamping\n",
+ ctl_stack_size, qp->ctl_stack_size);
+ ctl_stack_size = qp->ctl_stack_size;
+ }
memcpy(ctl_stack, ctl_stack_src, ctl_stack_size);
m->cp_hqd_pq_doorbell_control =
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
index 071f956f183c..7c4f89cf283c 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
@@ -27,6 +27,7 @@
#include "kfd_device_queue_manager.h"
#include "kfd_priv.h"
#include "kfd_kernel_queue.h"
+#include "kfd_topology.h"
#include "amdgpu_amdkfd.h"
#include "amdgpu_reset.h"
@@ -984,7 +985,9 @@ int kfd_criu_restore_queue(struct kfd_process *p,
{
uint8_t *mqd, *ctl_stack, *q_extra_data = NULL;
struct kfd_criu_queue_priv_data *q_data;
+ struct kfd_topology_device *topo_dev;
struct kfd_process_device *pdd;
+ uint32_t max_ctl_stack_size, num_xcc;
uint64_t q_extra_data_size;
struct queue_properties qp;
unsigned int queue_id;
@@ -1032,6 +1035,29 @@ int kfd_criu_restore_queue(struct kfd_process *p,
goto exit;
}
+ /*
+ * q_data->ctl_stack_size is user-supplied and is consumed across all
+ * XCCs of the node when restoring the MQD control stack. Reject sizes
+ * that could not have come from a valid checkpoint before sizing any
+ * allocation or copy, so a bogus value can never drive an
+ * out-of-bounds control stack write during MQD restore.
+ */
+ num_xcc = NUM_XCC(pdd->dev->xcc_mask);
+ topo_dev = kfd_topology_device_by_id(pdd->dev->id);
+ if (!num_xcc || !topo_dev) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ max_ctl_stack_size = ALIGN(topo_dev->node_props.ctl_stack_size,
PAGE_SIZE);
+ if (q_data->type == KFD_QUEUE_TYPE_COMPUTE &&
+ q_data->ctl_stack_size > (uint64_t)max_ctl_stack_size * num_xcc) {
+ pr_err("CRIU restore ctl_stack_size 0x%x exceeds max 0x%llx\n",
+ q_data->ctl_stack_size,
+ (uint64_t)max_ctl_stack_size * num_xcc);
+ ret = -EINVAL;
+ goto exit;
+ }
+
/*
* data stored in this order:
* mqd[xcc0], mqd[xcc1],..., ctl_stack[xcc0], ctl_stack[xcc1]...
--
2.43.0