]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/nouveau: fix a use-after-free in r535_gsp_rpc_push()
authorZhi Wang <zhiw@nvidia.com>
Tue, 27 May 2025 16:37:12 +0000 (16:37 +0000)
committerDanilo Krummrich <dakr@kernel.org>
Fri, 13 Jun 2025 14:38:06 +0000 (16:38 +0200)
The RPC container is released after being passed to r535_gsp_rpc_send().

When sending the initial fragment of a large RPC and passing the
caller's RPC container, the container will be freed prematurely. Subsequent
attempts to send remaining fragments will therefore result in a
use-after-free.

Allocate a temporary RPC container for holding the initial fragment of a
large RPC when sending. Free the caller's container when all fragments
are successfully sent.

Fixes: 176fdcbddfd2 ("drm/nouveau/gsp/r535: add support for booting GSP-RM")
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
Link: https://lore.kernel.org/r/20250527163712.3444-1-zhiw@nvidia.com
[ Rebase onto Blackwell changes. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c

index 5acb98d137bd8b3f2a8854e7f4011c592e4289ec..9d06ff722fea7c5f108ec218fb91c4074bb83360 100644 (file)
@@ -637,12 +637,18 @@ r535_gsp_rpc_push(struct nvkm_gsp *gsp, void *payload,
        if (payload_size > max_payload_size) {
                const u32 fn = rpc->function;
                u32 remain_payload_size = payload_size;
+               void *next;
 
-               /* Adjust length, and send initial RPC. */
-               rpc->length = sizeof(*rpc) + max_payload_size;
-               msg->checksum = rpc->length;
+               /* Send initial RPC. */
+               next = r535_gsp_rpc_get(gsp, fn, max_payload_size);
+               if (IS_ERR(next)) {
+                       repv = next;
+                       goto done;
+               }
 
-               repv = r535_gsp_rpc_send(gsp, payload, NVKM_GSP_RPC_REPLY_NOWAIT, 0);
+               memcpy(next, payload, max_payload_size);
+
+               repv = r535_gsp_rpc_send(gsp, next, NVKM_GSP_RPC_REPLY_NOWAIT, 0);
                if (IS_ERR(repv))
                        goto done;
 
@@ -653,7 +659,6 @@ r535_gsp_rpc_push(struct nvkm_gsp *gsp, void *payload,
                while (remain_payload_size) {
                        u32 size = min(remain_payload_size,
                                       max_payload_size);
-                       void *next;
 
                        next = r535_gsp_rpc_get(gsp, NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD, size);
                        if (IS_ERR(next)) {
@@ -674,6 +679,8 @@ r535_gsp_rpc_push(struct nvkm_gsp *gsp, void *payload,
                /* Wait for reply. */
                repv = r535_gsp_rpc_handle_reply(gsp, fn, policy, payload_size +
                                                 sizeof(*rpc));
+               if (!IS_ERR(repv))
+                       kvfree(msg);
        } else {
                repv = r535_gsp_rpc_send(gsp, payload, policy, gsp_rpc_len);
        }