[PATCH 0/2] Fix domain assignment TOCTOU races
Huamao Wu
huamao.wu at linux.spacemit.com
Wed Aug 19 00:51:36 PDT 2026
The per-hart domain assignment is represented by two fields that must
stay consistent at all times:
1. a per-hart pointer stored in sbi_scratch (hartindex_to_domain)
2. a per-domain bitmap (assigned_harts)
A concurrent reader is correct only if it observes both fields in
agreement. Today these fields are updated independently, creating
TOCTOU windows where a hart transiently belongs to no domain or to
two domains.
These races were identified through code review. On our internal
platform we observed symptoms consistent with the cross-call TOCTOU
described in Race 2 (a HART_START ecall seeing an assignment that a
preceding HART_GET_STATUS did not), though the root cause of those
specific symptoms turned out to be an unrelated cache-coherency issue.
The code paths nonetheless present TOCTOU windows worth addressing
regardless of the observed root cause.
Race 1: switch_to_next_domain_context() non-atomic reassignment
---------------------------------------------------------------
The context-switch path updates the two fields in three separate
locked steps. A concurrent HART_START that checks ownership between
steps sees an inconsistent state:
1. Hart A: clear hart N from old_dom->assigned_harts
2. Hart A: update hartindex_to_domain[hart N] = new_dom
-- hart N is in no domain's bitmap --
3. Hart B: check assigned_harts for hart N -> not found
-- hart appears unowned, HART_START rejected --
4. Hart A: set hart N in new_dom->assigned_harts
Between steps 1 and 4 the hart is in no domain. A concurrent
HART_START that validates ownership at step 3 will incorrectly
reject the hart.
Race 2: sbi_hsm_hart_start() check-vs-transition gap
-----------------------------------------------------
The HART_START handler validates domain ownership, acquires the start
ticket, and atomically transitions the hart to START_PENDING in three
unprotected steps. A concurrent domain assignment can reassign the
hart between the check and the commit:
1. Hart A: check dom->assigned_harts for hart N -> OK
2. Hart B: assign_hart(hart N, other_dom)
-- hart N now belongs to other_dom --
3. Hart A: acquire start ticket
4. Hart A: cmpxchg state STOPPED -> START_PENDING
-- hart N starts in a domain that no longer owns it --
The ownership check and the state transition are not serialized
against concurrent assignment changes, so HART_START can commit for
a domain that lost ownership between check and commit.
Patch 1 introduces the locking infrastructure and converts the
assignment path in sbi_domain_register().
Patch 2 converts the two remaining callers (sbi_hsm_hart_start and
switch_to_next_domain_context) to the atomic API.
The patches are also available on GitHub:
https://github.com/kasperis7/opensbi branch domain-assignment-race-fix
Huamao Wu (2):
lib: sbi_domain: introduce atomic hart assignment helper
lib: sbi: serialize HSM hart_start ownership check with assignment
lock
include/sbi/sbi_domain.h | 19 +++++++++
lib/sbi/sbi_domain.c | 81 ++++++++++++++++++++++++++++++++++------
lib/sbi/sbi_domain_context.c | 13 +------
lib/sbi/sbi_hsm.c | 15 +++++--
4 files changed, 108 insertions(+), 20 deletions(-)
--
2.43.0
More information about the opensbi
mailing list