From 472d0dfdf3e4fce869e714f8f2ce9f26e7af4f57 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Wed, 30 Oct 2024 05:05:22 +0000 Subject: [PATCH] platform: dce: fix ipc_client_unregister race cond This patch fixes a race condition between tegra_dce_unregister_ipc_client and dce_client_ipc_wakeup. If dce_client_ipc_wakeup is called just after unregistering it'll result in accessing already freed data structures and kernel crash. This patch adds a check to validate if the client_ipc struct is valid before accessing its members. Bug 4913921 Change-Id: Ie7f25379d7254d1f1ad4fb17baafee353f6d9eca Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3239873 GVS: buildbot_gerritrpt Reviewed-by: Vinod Gopalakrishnakurup (cherry picked from commit 91a3402525e79bc59c7367743740c17a91e8752e) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3256954 (cherry picked from commit 6bce903d9e8c8de4c2017abc33191a983343a0c2) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3261855 Reviewed-by: svcacv --- drivers/platform/tegra/dce/dce-client-ipc.c | 23 +++++++++------------ 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index b01eaad8..0c16cc33 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2019-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ #include @@ -89,22 +89,17 @@ static int dce_client_ipc_handle_alloc(u32 *handle) return ret; } -static int dce_client_ipc_handle_free(u32 handle) +static int dce_client_ipc_handle_free(struct tegra_dce_client_ipc *cl) { struct tegra_dce *d; - struct tegra_dce_client_ipc *cl; - if (!is_client_handle_valid(handle)) - return -EINVAL; - - cl = &client_handles[client_handle_to_index(handle)]; - - if (cl->valid == false) + if ((cl == NULL) || (cl->valid == false)) return -EINVAL; d = cl->d; d->d_clients[cl->type] = NULL; memset(cl, 0, sizeof(struct tegra_dce_client_ipc)); + cl->valid = false; return 0; } @@ -129,7 +124,7 @@ int tegra_dce_register_ipc_client(u32 type, int ret; uint32_t int_type; struct tegra_dce *d = NULL; - struct tegra_dce_client_ipc *cl; + struct tegra_dce_client_ipc *cl = NULL; u32 handle = DCE_CLIENT_IPC_HANDLE_INVALID; if (handlep == NULL) { @@ -185,11 +180,12 @@ int tegra_dce_register_ipc_client(u32 type, goto out; } + cl->valid = true; d->d_clients[type] = cl; out: if (ret != 0) { - dce_client_ipc_handle_free(handle); + (void)dce_client_ipc_handle_free(cl); handle = DCE_CLIENT_IPC_HANDLE_INVALID; } @@ -209,8 +205,9 @@ int tegra_dce_unregister_ipc_client(u32 handle) } dce_cond_destroy(&cl->recv_wait); + atomic_set(&cl->complete, 0); - return dce_client_ipc_handle_free(handle); + return dce_client_ipc_handle_free(cl); } EXPORT_SYMBOL(tegra_dce_unregister_ipc_client); @@ -368,7 +365,7 @@ void dce_client_ipc_wakeup(struct tegra_dce *d, u32 ch_type) } cl = d->d_clients[type]; - if ((cl == NULL) || (cl->int_type != ch_type)) { + if ((cl == NULL) || (cl->valid == false) || (cl->int_type != ch_type)) { dce_err(d, "Failed to retrieve client info for ch_type: [%d]", ch_type); return;