> +       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

Reply via email to