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 2fe328517ba649ed7b6f3c44a29a566c685cd037 Author: Marco Casaroli <[email protected]> AuthorDate: Tue Oct 20 10:40:35 2026 +0200 sched/signal: Clear GROUP_FLAG_NOCLDWAIT and honour SIG_IGN for SIGCHLD. sigaction(SIGCHLD) with SA_NOCLDWAIT set GROUP_FLAG_NOCLDWAIT, but no call cleared it. A process that set SA_NOCLDWAIT once, for example to start one child, never kept the exit status of a child again. XSI also gives SIGCHLD set to SIG_IGN the effect of SA_NOCLDWAIT. NuttX kept the status in this case. Now sigaction(SIGCHLD) sets the flag for SA_NOCLDWAIT or SIG_IGN, and clears it for all other actions. SIG_IGN counts only with CONFIG_SIG_DEFAULT: without it, SIG_IGN and SIG_DFL are the same value. The old action has SA_NOCLDWAIT when the flag is set, so a restore of the old action keeps the flag. The flag changes only after the new action is in place, so a sigaction() that fails with ENOMEM changes nothing. nxsig_dupactions() gives the child of fork() or vfork() the signal actions of its parent, but not the flag: the child ignored SIGCHLD and still kept the status of its own children. It now copies the flag too. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- sched/signal/sig_action.c | 100 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 30 deletions(-) diff --git a/sched/signal/sig_action.c b/sched/signal/sig_action.c index a0f55f22330..4c6d345c02a 100644 --- a/sched/signal/sig_action.c +++ b/sched/signal/sig_action.c @@ -204,7 +204,8 @@ static FAR sigactq_t *nxsig_alloc_action(void) * - All sa_flags in struct sigaction of act input are ignored (all * treated like SA_SIGINFO). The one exception is if * CONFIG_SCHED_CHILD_STATUS is defined; then SA_NOCLDWAIT is supported but - * only for SIGCHLD + * only for SIGCHLD. SIGCHLD set to SIG_IGN has the same effect if + * CONFIG_SIG_DEFAULT is defined. * ****************************************************************************/ @@ -287,6 +288,15 @@ int nxsig_action(int signo, FAR const struct sigaction *act, oact->sa_flags = 0; sigemptyset(&oact->sa_mask); } + +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) + /* Report SA_NOCLDWAIT, so that a restore of oact keeps it */ + + if (signo == SIGCHLD && (group->tg_flags & GROUP_FLAG_NOCLDWAIT) != 0) + { + oact->sa_flags |= SA_NOCLDWAIT; + } +#endif } /* If the argument act is a null pointer, signal handling is unchanged; @@ -299,35 +309,6 @@ int nxsig_action(int signo, FAR const struct sigaction *act, return OK; } -#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) - /* Handle a special case. Retention of child status can be suppressed - * if signo == SIGCHLD and sa_flags == SA_NOCLDWAIT. - * - * POSIX.1 leaves it unspecified whether a SIGCHLD signal is generated - * when a child process terminates. In NuttX, a SIGCHLD signal is - * generated in this case; but in some other implementations, it may not - * be. - */ - - if (signo == SIGCHLD && (act->sa_flags & SA_NOCLDWAIT) != 0) - { - /* We do require a critical section to muck with the TCB values that - * can be modified by the child thread. - */ - - flags = enter_critical_section(); - - /* Mark that status should be not be retained */ - - rtcb->group->tg_flags |= GROUP_FLAG_NOCLDWAIT; - - /* Free all pending exit status */ - - group_remove_children(rtcb->group); - leave_critical_section(flags); - } -#endif - handler = act->sa_handler; #ifdef CONFIG_FDPIC @@ -422,6 +403,55 @@ int nxsig_action(int signo, FAR const struct sigaction *act, sigact->act.sa_user = act->sa_user; } +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) + /* Handle a special case. Retention of child status is suppressed if + * signo == SIGCHLD and sa_flags has SA_NOCLDWAIT, or if the action is + * SIG_IGN (XSI). Any other action for SIGCHLD retains it again. + * + * POSIX.1 leaves it unspecified whether a SIGCHLD signal is generated + * when a child process terminates. In NuttX, a SIGCHLD signal is + * generated in this case; but in some other implementations, it may not + * be. + */ + + if (signo == SIGCHLD) + { + bool nocldwait = (act->sa_flags & SA_NOCLDWAIT) != 0; + +#ifdef CONFIG_SIG_DEFAULT + /* Without CONFIG_SIG_DEFAULT, SIG_IGN is also SIG_DFL */ + + if (act->sa_handler == SIG_IGN) + { + nocldwait = true; + } +#endif + + /* We do require a critical section to muck with the TCB values that + * can be modified by the child thread. + */ + + flags = enter_critical_section(); + + if (nocldwait) + { + /* Mark that status should be not be retained */ + + group->tg_flags |= GROUP_FLAG_NOCLDWAIT; + + /* Free all pending exit status */ + + group_remove_children(group); + } + else + { + group->tg_flags &= ~GROUP_FLAG_NOCLDWAIT; + } + + leave_critical_section(flags); + } +#endif + return OK; } @@ -504,6 +534,16 @@ void nxsig_dupactions(FAR struct task_group_s *group, group->tg_sigdefault = parent->tg_sigdefault; spin_unlock_irqrestore(&parent->tg_lock, flags); #endif + +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) + /* SA_NOCLDWAIT (or SIGCHLD set to SIG_IGN) is part of the action of + * SIGCHLD: the child does not keep the status of its children either. + */ + + flags = enter_critical_section(); + group->tg_flags |= parent->tg_flags & GROUP_FLAG_NOCLDWAIT; + leave_critical_section(flags); +#endif } /****************************************************************************
