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

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

commit 2f06af7d5d7e9eb6c0ca34379bbb3bbdad075f51
Author: Marco Casaroli <[email protected]>
AuthorDate: Mon Oct 12 10:20:39 2026 +0200

    sched/addrenv: Drop the old address environment after the lock.
    
    addrenv_switch() dropped the reference to the old address environment
    while it held g_addrenv_lock.  When that was the last reference,
    addrenv_drop() queued the destruction on the work queue, and
    work_queue() enters the critical section.  With SMP, another CPU could
    hold the critical section during a context switch and wait for
    g_addrenv_lock in addrenv_switch(): both CPUs then spun for ever.
    
    Drop the reference after the lock is released.  The old address
    environment is no longer installed on this CPU at that point.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 sched/addrenv/addrenv.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/sched/addrenv/addrenv.c b/sched/addrenv/addrenv.c
index 8c768336ea7..66afa48dcda 100644
--- a/sched/addrenv/addrenv.c
+++ b/sched/addrenv/addrenv.c
@@ -125,6 +125,7 @@ static void addrenv_destroy(FAR void *arg)
 
 int addrenv_switch(FAR struct tcb_s *tcb)
 {
+  FAR struct addrenv_s *drop = NULL;
   FAR struct addrenv_s *curr;
   FAR struct addrenv_s *next;
   irqstate_t flags;
@@ -180,12 +181,9 @@ int addrenv_switch(FAR struct tcb_s *tcb)
           berr("ERROR: up_addrenv_select failed: %d\n", ret);
         }
 
-      /* This is a safe spot to drop the current address environment */
+      /* The current address environment is dropped below */
 
-      if (curr)
-        {
-          addrenv_drop(curr, true);
-        }
+      drop = curr;
 
       /* Save the new, current address environment group */
 
@@ -193,6 +191,13 @@ int addrenv_switch(FAR struct tcb_s *tcb)
     }
 
   spin_unlock_irqrestore_nopreempt(&g_addrenv_lock, flags);
+
+  /* Drop the old address environment after the lock is released:
+   * addrenv_drop() may queue work, which enters the critical section.
+   * Another CPU can hold the critical section and wait for this lock.
+   */
+
+  addrenv_drop(drop, true);
   return OK;
 }
 

Reply via email to