diff options
| author | Roland Dreier <rolanddreier@rivian.com> | 2026-09-09 04:32:27 +0000 |
|---|---|---|
| committer | Sudeep Holla <sudeep.holla@kernel.org> | 2026-09-10 14:01:49 +0100 |
| commit | 49fc5bc04eec2b7687d49ccceb7adf6ace640a3b (patch) | |
| tree | d67973d09f3449d1960563558b1a4259cd5a4a83 /tools/perf/scripts/python/stackcollapse.py | |
| parent | 7d5b79cd7d59dd114fb1eb5f60aaac92333edf0e (diff) | |
firmware: arm_scmi: Protect xfer->async_done with xfer->lock
Asynchronous SCMI commands are completed by a delayed response. The RX
path signals the response with complete(xfer->async_done). Unlike
xfer->done, xfer->async_done is a pointer to a completion owned by
whoever is waiting for the delayed response, and it stays valid only for
as long as that waiter is still waiting. In do_xfer_with_response() it
is a DECLARE_COMPLETION_ONSTACK() in the caller's stack frame.
Nothing serialises the RX path against a waiter that gives up on a
timeout. scmi_msg_response_validate() does read xfer->async_done
under xfer->lock, and documents that as a requirement, but the lock is
dropped again before scmi_handle_response() dereferences the pointer,
and neither the arming nor the disarming side takes it at all. So a
delayed response arriving just as the wait times out can be signalled
on a completion that is already gone:
waiter RX path (IRQ context)
------ ---------------------
do_xfer_with_response():
xfer->async_done = &async_response
do_xfer(xfer)
wait_for_completion_timeout(xfer->async_done, tmo)
/* returns 0, gives up */
/* response receive interrupt */
scmi_handle_response():
scmi_xfer_command_acquire()
lock xfer->lock
validate: async_done != NULL
unlock xfer->lock
xfer->async_done = NULL
return -ETIMEDOUT
/* async_response goes out of scope */
complete(xfer->async_done)
That last complete() has two possible bad outcomes: it either dereferences
the NULL just stored by the waiter or - if that store is not yet visible
on the RX CPU - it takes a lock and writes to a stack frame that the
waiter may already have returned from.
Fix this by making xfer->lock cover xfer->async_done end-to-end. Add
helpers to arm and disarm it under the lock, use them on both the regular
and the raw paths, and have the RX path read and signal the completion
under that same lock. A waiter that is timing out then either completes
its disarm before the RX path looks, in which case the delayed response is
dropped, or blocks in the disarm until the RX path is done with the
completion, in which case the completion is still alive.
Log the dropped case, so every path where a delayed response that was
matched to a pending xfer and then discarded produces an error. (This is
the same treatment scmi_msg_response_validate() already gives a
delayed response that arrives after the waiter has disarmed the xfer)
Fixes: 58ecdf03dbb9 ("firmware: arm_scmi: Add support for asynchronous commands and delayed response")
Signed-off-by: Roland Dreier <rolanddreier@rivian.com>
Reviewed-by: Cristian Marussi <cristian.marussi@arm.com>
Tested-by: Cristian Marussi <cristian.marussi@arm.com>
Link: https://patch.msgid.link/20260909-scmi-async-done-race-v2-1-cc5dec25c6be@rivian.com
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
Diffstat (limited to 'tools/perf/scripts/python/stackcollapse.py')
0 files changed, 0 insertions, 0 deletions
