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 d0b504e54c6e3f62f6bd5dc2b2ef3c521e67ea8d Author: Marco Casaroli <[email protected]> AuthorDate: Thu Oct 22 12:41:44 2026 +0200 sched/signal: Release the signals pending for a thread when it exits. A signal sent to one thread (pthread_kill(), tgkill()) that the thread cannot take at once (it blocks the signal, or it waits in a system call of a protected or kernel build) waits in the pending list of the group, marked with the TCB of the thread. When the thread exited, nxsig_cleanup() released only the lists in the TCB: the entry stayed in the list of the group with a pointer to the freed TCB. It was never delivered, its memory was lost, and a new thread at the same TCB address got it when it unblocked the signal, out of turn. Add nxsig_discard_threadsignals(), which releases the entries of a thread from the pending list of its group. nxtask_exithook() calls it before the thread leaves its group. The exit sets TCB_FLAG_EXIT_PROCESSING in a critical section before that. nxsig_tcbdispatch() now checks the flag again in the critical section where it queues the signal (it can leave the critical section to allocate before), so no entry comes after the discard, also with SMP. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- sched/signal/sig_cleanup.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ sched/signal/sig_dispatch.c | 11 +++++++++ sched/signal/signal.h | 1 + sched/task/task_exithook.c | 8 +++++++ 4 files changed, 78 insertions(+) diff --git a/sched/signal/sig_cleanup.c b/sched/signal/sig_cleanup.c index 0947c0e63bb..f2cb6c0510a 100644 --- a/sched/signal/sig_cleanup.c +++ b/sched/signal/sig_cleanup.c @@ -70,6 +70,64 @@ void nxsig_cleanup(FAR struct tcb_s *stcb) sigemptyset(&stcb->sigwaitmask); } +/**************************************************************************** + * Name: nxsig_discard_threadsignals + * + * Description: + * Release the signals pending for one thread (sent with pthread_kill() + * or tgkill() and not delivered yet). Called when the thread exits, + * before it leaves its group. + * + ****************************************************************************/ + +void nxsig_discard_threadsignals(FAR struct tcb_s *stcb) +{ + FAR struct task_group_s *group = stcb->group; + FAR sigpendq_t *sigpend; + FAR sigpendq_t *prev; + FAR sigpendq_t *next; + sq_queue_t discard; + irqstate_t flags; + + if (group == NULL) + { + return; + } + + sq_init(&discard); + flags = spin_lock_irqsave(&group->tg_lock); + + for (prev = NULL, sigpend = (FAR sigpendq_t *)group->tg_sigpendingq.head; + sigpend != NULL; sigpend = next) + { + next = sigpend->flink; + if (sigpend->tcb == stcb) + { + if (prev != NULL) + { + sq_remafter((FAR sq_entry_t *)prev, &group->tg_sigpendingq); + } + else + { + sq_remfirst(&group->tg_sigpendingq); + } + + sq_addlast((FAR sq_entry_t *)sigpend, &discard); + } + else + { + prev = sigpend; + } + } + + spin_unlock_irqrestore(&group->tg_lock, flags); + + while ((sigpend = (FAR sigpendq_t *)sq_remfirst(&discard)) != NULL) + { + nxsig_release_pendingsignal(sigpend); + } +} + /**************************************************************************** * Name: nxsig_release * diff --git a/sched/signal/sig_dispatch.c b/sched/signal/sig_dispatch.c index 93265434d13..4b3585f02bb 100644 --- a/sched/signal/sig_dispatch.c +++ b/sched/signal/sig_dispatch.c @@ -605,6 +605,17 @@ int nxsig_tcbdispatch(FAR struct tcb_s *stcb, FAR siginfo_t *info, return ret; } + /* Check the exit again, in the critical section that queues the signal: + * the exit sets the flag in a critical section and then discards the + * signals pending for the thread. + */ + + if ((stcb->flags & TCB_FLAG_EXIT_PROCESSING) != 0) + { + leave_critical_section(flags); + return -ESRCH; + } + masked = nxsig_ismember(&stcb->sigprocmask, info->si_signo); #ifdef CONFIG_ENABLE_ALL_SIGNALS diff --git a/sched/signal/signal.h b/sched/signal/signal.h index c1d3eb85b24..b783c0ab10d 100644 --- a/sched/signal/signal.h +++ b/sched/signal/signal.h @@ -209,6 +209,7 @@ int nxsig_notification_source(pid_t pid, /* sig_cleanup.c */ void nxsig_cleanup(FAR struct tcb_s *stcb); +void nxsig_discard_threadsignals(FAR struct tcb_s *stcb); void nxsig_release(FAR struct task_group_s *group); /* sig_timedwait.c */ diff --git a/sched/task/task_exithook.c b/sched/task/task_exithook.c index 0ff68926c8c..c40dbbf32df 100644 --- a/sched/task/task_exithook.c +++ b/sched/task/task_exithook.c @@ -461,6 +461,14 @@ void nxtask_exithook(FAR struct tcb_s *tcb, int status) sched_unlock(); +#ifndef CONFIG_DISABLE_ALL_SIGNALS + /* Release the signals that wait for this thread in the pending list of + * the group. No new ones come: the thread is in exit processing. + */ + + nxsig_discard_threadsignals(tcb); +#endif + /* Leave the task group. Perhaps discarding any un-reaped child * status (no zombies here!) */
