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 <mahkumar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3239873
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Vinod Gopalakrishnakurup <vinodg@nvidia.com>
(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 <svcacv@nvidia.com>
This commit is contained in:
Mahesh Kumar
2024-10-30 05:05:22 +00:00
committed by mobile promotions
parent 322d0bafa1
commit 472d0dfdf3

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only // 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 <dce.h> #include <dce.h>
@@ -89,22 +89,17 @@ static int dce_client_ipc_handle_alloc(u32 *handle)
return ret; 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 *d;
struct tegra_dce_client_ipc *cl;
if (!is_client_handle_valid(handle)) if ((cl == NULL) || (cl->valid == false))
return -EINVAL;
cl = &client_handles[client_handle_to_index(handle)];
if (cl->valid == false)
return -EINVAL; return -EINVAL;
d = cl->d; d = cl->d;
d->d_clients[cl->type] = NULL; d->d_clients[cl->type] = NULL;
memset(cl, 0, sizeof(struct tegra_dce_client_ipc)); memset(cl, 0, sizeof(struct tegra_dce_client_ipc));
cl->valid = false;
return 0; return 0;
} }
@@ -129,7 +124,7 @@ int tegra_dce_register_ipc_client(u32 type,
int ret; int ret;
uint32_t int_type; uint32_t int_type;
struct tegra_dce *d = NULL; 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; u32 handle = DCE_CLIENT_IPC_HANDLE_INVALID;
if (handlep == NULL) { if (handlep == NULL) {
@@ -185,11 +180,12 @@ int tegra_dce_register_ipc_client(u32 type,
goto out; goto out;
} }
cl->valid = true;
d->d_clients[type] = cl; d->d_clients[type] = cl;
out: out:
if (ret != 0) { if (ret != 0) {
dce_client_ipc_handle_free(handle); (void)dce_client_ipc_handle_free(cl);
handle = DCE_CLIENT_IPC_HANDLE_INVALID; handle = DCE_CLIENT_IPC_HANDLE_INVALID;
} }
@@ -209,8 +205,9 @@ int tegra_dce_unregister_ipc_client(u32 handle)
} }
dce_cond_destroy(&cl->recv_wait); 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); 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]; 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]", dce_err(d, "Failed to retrieve client info for ch_type: [%d]",
ch_type); ch_type);
return; return;