msm:adsprpc: Fix UAF of ctx->perf in async invoke perf counter

In async invoke path, fastrpc_update_invoke_count is called at
invoke_end with perf_counter pointing into ctx->perf.
After fastrpc_invoke_send returns, the async response thread may
call context_free(ctx), freeing ctx->perf before invoke_end.
This causes a use-after-free write that corrupts the SLUB
freelist and may trigger a kernel panic on next allocation.
Fix by storing submission timestamp in ctx->invoke_start_time
before send, removing unsafe call from invoke_end, and moving
fastrpc_update_invoke_count to fastrpc_wait_on_async_queue.
There, ctx is guaranteed valid before context_free. This also
aligns async perf->invoke with sync, measuring full end-to-end
latency instead of submission-only latency.

Acked-by: Sharad Kumar <sharku@qti.qualcomm.com>
Change-Id: I91f2a2479b508fde2fe7c26783ee56dc9391eb17
Signed-off-by: Santosh Sakore <ssakore@qti.qualcomm.com>
This commit is contained in:
Santosh Sakore 2026-05-18 16:15:14 +05:30 • committed by Saikiran Muppidi
commit c2adf7dc3f

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
/* Uncomment this block to log an error on every VERIFY failure */
@ -444,6 +444,7 @@ struct smq_invoke_ctx {
uint32_t sc_interrupted;
struct fastrpc_file *fl_interrupted;
uint32_t handle_interrupted;
struct timespec64 invoke_start_time; /* submission timestamp for async perf */
};
struct fastrpc_ctx_lst {
@ -3362,6 +3363,15 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
inv_args(ctx);
PERF_END);
/*
* Store submission timestamp in ctx before sending to DSP.
* For async invokes, perf->invoke will be updated in the collector
* thread (fastrpc_wait_on_async_queue) where ctx is still valid,
* measuring full end-to-end latency consistent with the sync path.
*/
if (fl->profile && isasyncinvoke)
ctx->invoke_start_time = invoket;
PERF(fl->profile, GET_COUNTER(perf_counter, PERF_LINK),
VERIFY(err, 0 == (err = fastrpc_invoke_send(ctx,
kernel, invoke->handle)));
@ -3423,9 +3433,6 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
err = -ECONNRESET;
invoke_end:
if (fl->profile && !interrupted && isasyncinvoke)
fastrpc_update_invoke_count(invoke->handle, perf_counter,
&invoket);
return err;
}
@ -3495,6 +3502,15 @@ bail:
async_res->result = ierr;
if (ctx) {
if (fl->profile && ctx->perf && ctx->handle > FASTRPC_STATIC_HANDLE_MAX) {
/*
* Update invoke/count perf counters here where ctx->perf is
* guaranteed valid. This measures full end-to-end async latency
* (submit → DSP → collect), consistent with the sync path.
* invoke_start_time was stored in ctx before fastrpc_invoke_send
* in fastrpc_internal_invoke.
*/
fastrpc_update_invoke_count(ctx->handle, perf_counter,
&ctx->invoke_start_time);
trace_fastrpc_perf_counters(ctx->handle, ctx->sc,
ctx->perf->count, ctx->perf->flush, ctx->perf->map,
ctx->perf->copy, ctx->perf->link, ctx->perf->getargs,