Skip to content

{bp-19880} spinlock: fix ticket lock corruption in trylock and unlock - #20072

Merged
xiaoxiang781216 merged 1 commit into
apache:releases/13.0from
jerpelea:bp-19880
Sep 7, 2026
Merged

{bp-19880} spinlock: fix ticket lock corruption in trylock and unlock#20072
xiaoxiang781216 merged 1 commit into
apache:releases/13.0from
jerpelea:bp-19880

Conversation

@jerpelea

@jerpelea jerpelea commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Two defects in the CONFIG_TICKET_SPINLOCK paths of spinlock.h:

  1. spin_trylock_notrace() passed &lock->owner as the "expected" pointer of atomic_cmpxchg(). A failed compare-exchange writes the current value of the target object back through that pointer, so a losing trylock stores lock->next into lock->owner. owner then equals next, which is the unlocked state: a lock still held by another CPU reports itself as free, spin_is_locked() returns false and the lock can be taken again. Every later unlock keeps incrementing owner past next, so the ticket of a real waiter never matches and the lock stays locked forever. Keep the expected value in a local variable.

  2. spin_unlock() was wrapped in #ifdef __SP_UNLOCK_FUNCTION, a macro that is never defined anywhere in the tree. The function body was therefore dead code and spin_unlock() always expanded to "do { *(l) = SP_UNLOCKED; } while (0)", which zeroes both ticket counters instead of releasing one ticket with atomic_fetch_add(&lock->owner, 1). That drops queued waiters, lets a newcomer draw ticket 0 and enter the critical section, and also skips the UP_DMB/UP_DSB/UP_SEV release barriers and the sched_note_spinlock_unlock() note. Drop the dead #ifdef so spin_unlock() is always the function.

Both were reproduced on qemu-armv7a:smp (cortex-a7 x4) with CONFIG_TICKET_SPINLOCK=y, where the compare-exchange lowers to native ldrex/strex. This confirms the root cause is the C-level aliasing of the expected pointer, not the atomic implementation.

Refs: #19808

Impact

RELEASE

Testing

CI

Two defects in the CONFIG_TICKET_SPINLOCK paths of spinlock.h:

1. spin_trylock_notrace() passed &lock->owner as the "expected" pointer
   of atomic_cmpxchg().  A failed compare-exchange writes the current
   value of the target object back through that pointer, so a losing
   trylock stores lock->next into lock->owner.  owner then equals next,
   which is the unlocked state: a lock still held by another CPU reports
   itself as free, spin_is_locked() returns false and the lock can be
   taken again.  Every later unlock keeps incrementing owner past next,
   so the ticket of a real waiter never matches and the lock stays
   locked forever.  Keep the expected value in a local variable.

2. spin_unlock() was wrapped in #ifdef __SP_UNLOCK_FUNCTION, a macro
   that is never defined anywhere in the tree.  The function body was
   therefore dead code and spin_unlock() always expanded to
   "do { *(l) = SP_UNLOCKED; } while (0)", which zeroes both ticket
   counters instead of releasing one ticket with
   atomic_fetch_add(&lock->owner, 1).  That drops queued waiters, lets a
   newcomer draw ticket 0 and enter the critical section, and also skips
   the UP_DMB/UP_DSB/UP_SEV release barriers and the
   sched_note_spinlock_unlock() note.  Drop the dead #ifdef so
   spin_unlock() is always the function.

Both were reproduced on qemu-armv7a:smp (cortex-a7 x4) with
CONFIG_TICKET_SPINLOCK=y, where the compare-exchange lowers to native
ldrex/strex.  This confirms the root cause is the C-level aliasing of
the expected pointer, not the atomic implementation.

Refs: apache#19808

Signed-off-by: hujun5 <hujun5@xiaomi.com>
@github-actions github-actions Bot added Area: OS Components OS Components issues Size: S The size of the change in this PR is small labels Sep 7, 2026
@jerpelea

jerpelea commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

CI fix
apache/nuttx-apps#3775

@xiaoxiang781216
xiaoxiang781216 merged commit b7b7a40 into apache:releases/13.0 Sep 7, 2026
24 of 41 checks passed
@jerpelea
jerpelea deleted the bp-19880 branch September 7, 2026 11:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: OS Components OS Components issues Size: S The size of the change in this PR is small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants