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

commit a9e9607d95bc820d1bc75ff6e9852ec4d903208c
Author: raiden00pl <[email protected]>
AuthorDate: Mon Apr 20 20:07:07 2026 +0200

    arch/x86_64/intel64: fix signal delivery race in x86_64_sigdeliver
    
    copy logic from arm64 to fix SMP bugs in the signal trampoline
    caused signals to be lost or mis-delivered in ostest signest_test
    on qemu-intel64/jumbo.
    
    Also update comments so this logic is easier to follow
    
    Signed-off-by: raiden00pl <[email protected]>
---
 arch/x86_64/src/intel64/intel64_sigdeliver.c | 52 +++++++++++++++++++---------
 1 file changed, 35 insertions(+), 17 deletions(-)

diff --git a/arch/x86_64/src/intel64/intel64_sigdeliver.c 
b/arch/x86_64/src/intel64/intel64_sigdeliver.c
index 1e01a41a7a4..7b11acc7845 100644
--- a/arch/x86_64/src/intel64/intel64_sigdeliver.c
+++ b/arch/x86_64/src/intel64/intel64_sigdeliver.c
@@ -86,10 +86,10 @@ void x86_64_sigdeliver(void)
 
   x86_64_copystate(regs, rtcb->xcp.regs);
 
+retry:
 #ifdef CONFIG_SMP
-  /* In the SMP case, up_schedule_sigaction(0) will have incremented
-   * 'irqcount' in order to force us into a critical section.  Save the
-   * pre-incremented irqcount.
+  /* Save the pre-incremented irqcount so we can restore it before resuming
+   * the interrupted thread.
    */
 
   saved_irqcount = rtcb->irqcount;
@@ -125,11 +125,39 @@ void x86_64_sigdeliver(void)
   sinfo("Resuming\n");
 
 #ifdef CONFIG_SMP
-  enter_critical_section();
-#else
+  /* Restore the saved 'irqcount' plus one extra to enforce the critical
+   * section while we check for any newly queued signal actions and modify
+   * the saved regs. The extra is dropped below with a final
+   * leave_critical_section(), yielding net irqcount == saved_irqcount.
+   */
+
+  DEBUGASSERT(rtcb->irqcount == 0);
+  while (rtcb->irqcount < saved_irqcount + 1)
+    {
+      enter_critical_section();
+    }
+#endif
+
+#ifndef CONFIG_SUPPRESS_INTERRUPTS
   up_irq_save();
 #endif
 
+  /* A new signal action may have been queued while we were returning from
+   * nxsig_deliver() (after its empty-queue check but before we re-entered
+   * the critical section). If so, re-run the delivery loop so the new
+   * sigq is not orphaned. Nested signal handling is not supported, so
+   * skip this if we are already delivering.
+   */
+
+  if (!sq_empty(&rtcb->sigpendactionq) &&
+      (rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
+    {
+#ifdef CONFIG_SMP
+      leave_critical_section(up_irq_save());
+#endif
+      goto retry;
+    }
+
   /* Modify the saved return state with the actual saved values in the
    * TCB.  This depends on the fact that nested signal handling is
    * not supported.  Therefore, these values will persist throughout the
@@ -146,19 +174,9 @@ void x86_64_sigdeliver(void)
   rtcb->sigdeliver = NULL;  /* Allows next handler to be scheduled */
 
 #ifdef CONFIG_SMP
-  /* Restore the saved 'irqcount' and recover the critical section
-   * spinlocks.
-   *
-   * REVISIT:  irqcount should be one from the above call to
-   * enter_critical_section().  Could the saved_irqcount be zero?  That
-   * would be a problem.
-   */
+  /* We need to keep the IRQ lock until task switching */
 
-  DEBUGASSERT(rtcb->irqcount == 1);
-  while (rtcb->irqcount < saved_irqcount)
-    {
-      enter_critical_section();
-    }
+  leave_critical_section(up_irq_save());
 #endif
 
   /* Then restore the correct state for this thread of execution. */

Reply via email to