This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new b7c8430de69 spinlock: fix ticket lock corruption in trylock and unlock
b7c8430de69 is described below

commit b7c8430de698e43386683e12f10680c48e90ba26
Author: hujun5 <[email protected]>
AuthorDate: Mon Aug 17 13:46:09 2026 +0800

    spinlock: fix ticket lock corruption in trylock and unlock
    
    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: https://github.com/apache/nuttx/issues/19808
    
    Signed-off-by: hujun5 <[email protected]>
---
 include/nuttx/spinlock.h | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/include/nuttx/spinlock.h b/include/nuttx/spinlock.h
index 208686b5b55..63884365547 100644
--- a/include/nuttx/spinlock.h
+++ b/include/nuttx/spinlock.h
@@ -276,8 +276,18 @@ static inline_function bool
 spin_trylock_notrace(FAR volatile spinlock_t *lock)
 {
 #ifdef CONFIG_TICKET_SPINLOCK
-  if (!atomic_cmpxchg(&lock->next, &lock->owner,
-                      atomic_read(&lock->next) + 1))
+  /* The expected value must live in a local.  A failed compare-exchange
+   * writes the current value of the target object back through the
+   * expected pointer, so passing &lock->owner here would clobber the
+   * owner counter and make a lock held by another CPU appear unlocked.
+   *
+   * The exchange succeeds only when next == owner, which is the unlocked
+   * state of a ticket lock.
+   */
+
+  uint32_t expected = atomic_read(&lock->owner);
+
+  if (!atomic_cmpxchg(&lock->next, &expected, expected + 1))
 #else /* CONFIG_TICKET_SPINLOCK */
   if (up_testset(lock) == SP_LOCKED)
 #endif /* CONFIG_TICKET_SPINLOCK */
@@ -394,7 +404,6 @@ spin_unlock_notrace(FAR volatile spinlock_t *lock)
  ****************************************************************************/
 
 #ifdef CONFIG_SPINLOCK
-#  ifdef __SP_UNLOCK_FUNCTION
 static inline_function void spin_unlock(FAR volatile spinlock_t *lock)
 {
   /* Unlock without trace note */
@@ -405,9 +414,6 @@ static inline_function void spin_unlock(FAR volatile 
spinlock_t *lock)
 
   sched_note_spinlock_unlock(lock);
 }
-#  else
-#    define spin_unlock(l)  do { *(l) = SP_UNLOCKED; } while (0)
-#  endif
 #else
 #  define spin_unlock(lock)
 #endif /* CONFIG_SPINLOCK */

Reply via email to