[PATCH 2/2] misc: fastrpc: consume in-flight reference exactly once

From: bo cao

Date: Fri Oct 30 2026 - 04:21:31 EST


The response path is supposed to drop the in-flight reference taken
by fastrpc_invoke_send() exactly once: fastrpc_rpmsg_callback() calls
complete() and schedules fastrpc_context_put_wq(), whose worker drops
the reference. Nothing enforces this: a second delivery of the same
COMPLETE response (identical ctxid and sequence, so it passes the
sequence validation) re-queues put_work after the first worker has
run and drops the in-flight reference a second time. The context is
then freed while the invoking thread is still processing the first
completion, and the second fastrpc_context_free() also drops the
fastrpc_user and channel context references a second time.

Track the first delivery with a plain bool checked under cctx->lock,
together with the sequence validation, and drop duplicates.

Fixes: c68cfb718c8f ("misc: fastrpc: Add support for context Invoke method")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: bo cao <treebacker@xxxxxxxxxxx>
---
drivers/misc/fastrpc.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index c77048076aee..f56ba5e44e1f 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -271,6 +271,8 @@ struct fastrpc_invoke_ctx {
bool is_work_done;
/* process updates poll memory instead of glink response */
bool is_polled;
+ /* first response already consumed the in-flight reference */
+ bool responded;
struct kref refcount;
struct list_head node; /* list of ctxs */
struct completion work;
@@ -2725,6 +2727,7 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
unsigned long flags;
unsigned long ctxid;
bool stale = false;
+ bool dup = false;

if (len < sizeof(*rsp))
return -EINVAL;
@@ -2749,8 +2752,13 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
ctx = NULL;
}
if (ctx && FIELD_GET(FASTRPC_CTXID_SEQ_MASK, rsp->ctx) !=
- FIELD_GET(FASTRPC_CTXID_SEQ_MASK, ctx->ctxid))
+ FIELD_GET(FASTRPC_CTXID_SEQ_MASK, ctx->ctxid)) {
stale = true;
+ } else if (ctx && ctx->responded) {
+ dup = true;
+ } else if (ctx) {
+ ctx->responded = true;
+ }
spin_unlock_irqrestore(&cctx->lock, flags);

if (!ctx) {
@@ -2773,6 +2781,14 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
return 0;
}

+ if (dup) {
+ dev_dbg(&rpdev->dev,
+ "Duplicate glink response ctx 0x%llx, dropping\n",
+ rsp->ctx);
+ fastrpc_context_put(ctx);
+ return 0;
+ }
+
ctx->retval = rsp->retval;
ctx->is_work_done = true;
complete(&ctx->work);
--
2.39.5 (Apple Git-154)