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


The following commit(s) were added to refs/heads/master by this push:
     new 27ff1e74e10 sched/signal: Unblock sigtimedwait through kernel memory.
27ff1e74e10 is described below

commit 27ff1e74e1025fd24d1460a99f446d16771c63a5
Author: Justin Hammond <[email protected]>
AuthorDate: Fri Aug 7 12:04:36 2026 +0800

    sched/signal: Unblock sigtimedwait through kernel memory.
    
    nxsig_timedwait parked a pointer to the caller's siginfo buffer in the
    TCB, for whoever eventually posts the signal to fill in.  But the
    poster fills it in from its own context (another task, a kernel
    thread, an interrupt), and in a kernel build the caller's buffer is
    an address in the caller's private address space, which the poster
    does not share.  The write lands wherever the currently active
    mappings put it: the waiter wakes to find garbage where the signal
    number should be, and some other process is left with a corrupted
    page.  Flat builds share one address space, which is why this never
    showed there.
    
    Park the stack local in the TCB instead.  That is kernel memory,
    mapped in every context, and it is copied out to the caller's buffer
    after waking, in the caller's own context, exactly where the
    pending-signal path already does the same thing.
    
    Found on the EIC7700X port by an RTC alarm: the alarm signal, posted
    from the low-priority work queue, woke a sigwaitinfo caller into an
    assertion on the unblocking signal number while the init process,
    whose address space had received the stray write, died of a jump to
    address zero.  With this change the same test arms, waits and wakes
    cleanly, repeatedly.
    
    Assisted-by: Claude:claude-opus-5
    Signed-off-by: Justin Hammond <[email protected]>
---
 sched/signal/sig_timedwait.c | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/sched/signal/sig_timedwait.c b/sched/signal/sig_timedwait.c
index 19412b87474..727120da1df 100644
--- a/sched/signal/sig_timedwait.c
+++ b/sched/signal/sig_timedwait.c
@@ -157,7 +157,15 @@ int nxsig_timedwait(FAR const sigset_t *set, FAR struct 
siginfo *info,
 
   else
     {
-      rtcb->sigunbinfo = (info == NULL) ? &unbinfo : info;
+      /* Always unblock into the stack local, never into the caller's
+       * buffer.  The poster writes it from its own context (another
+       * task, a kernel thread, an interrupt), and in a kernel build the
+       * caller's buffer belongs to an address space the poster does not
+       * share.  The stack local is kernel memory, mapped everywhere, and
+       * is copied out below in the caller's own context.
+       */
+
+      rtcb->sigunbinfo = &unbinfo;
 
       /* Save the set of pending signals to wait for */
 
@@ -182,19 +190,19 @@ int nxsig_timedwait(FAR const sigset_t *set, FAR struct 
siginfo *info,
        * or timeout) that awakened us.
        */
 
-      if (GOOD_SIGNO(rtcb->sigunbinfo->si_signo))
+      if (GOOD_SIGNO(unbinfo.si_signo))
         {
           /* We were awakened by a signal... but is it one of the signals
            * that we were waiting for?
            */
 
-          if (nxsig_ismember(set, rtcb->sigunbinfo->si_signo) == 1)
+          if (nxsig_ismember(set, unbinfo.si_signo) == 1)
             {
               /* Yes.. the return value is the number of the signal that
                * awakened us.
                */
 
-              ret = rtcb->sigunbinfo->si_signo;
+              ret = unbinfo.si_signo;
             }
           else
             {
@@ -210,11 +218,11 @@ int nxsig_timedwait(FAR const sigset_t *set, FAR struct 
siginfo *info,
            */
 
 #ifdef CONFIG_CANCELLATION_POINTS
-          if (rtcb->sigunbinfo->si_signo == SIG_CANCEL_TIMEOUT)
+          if (unbinfo.si_signo == SIG_CANCEL_TIMEOUT)
             {
               /* The wait was canceled */
 
-              ret = -rtcb->sigunbinfo->si_errno;
+              ret = -unbinfo.si_errno;
               DEBUGASSERT(ret < 0);
             }
           else
@@ -224,12 +232,17 @@ int nxsig_timedwait(FAR const sigset_t *set, FAR struct 
siginfo *info,
                * error.
                */
 
-              DEBUGASSERT(rtcb->sigunbinfo->si_signo == SIG_WAIT_TIMEOUT);
+              DEBUGASSERT(unbinfo.si_signo == SIG_WAIT_TIMEOUT);
               ret = -EAGAIN;
             }
         }
 
       rtcb->sigunbinfo = NULL;
+
+      if (info != NULL)
+        {
+          memcpy(info, &unbinfo, sizeof(struct siginfo));
+        }
     }
 
   leave_critical_section(flags);

Reply via email to