From a0efb1fefeb1b5360e15912b92fa817dbab00a98 Mon Sep 17 00:00:00 2001 From: Jukka Laitinen Date: Mon, 5 May 2025 15:39:35 +0300 Subject: [PATCH] sched/signal: Move dynamic allocation of pending signals to a safe place The kmm_alloc can break the critical section, if it sleeps on the heap mutex. If we run out of pending signal structures, allocate more right after entering the critical section but before checking if the signal needs to be added to the pending queue. Signed-off-by: Jukka Laitinen --- sched/signal/sig_allocpendingsigaction.c | 16 --- sched/signal/sig_dispatch.c | 130 ++++++++++++++++------- 2 files changed, 90 insertions(+), 56 deletions(-) diff --git a/sched/signal/sig_allocpendingsigaction.c b/sched/signal/sig_allocpendingsigaction.c index 01b5a9ddf7..58c9be41fb 100644 --- a/sched/signal/sig_allocpendingsigaction.c +++ b/sched/signal/sig_allocpendingsigaction.c @@ -80,22 +80,6 @@ FAR sigq_t *nxsig_alloc_pendingsigaction(void) flags = enter_critical_section(); sigq = (FAR sigq_t *)sq_remfirst(&g_sigpendingaction); leave_critical_section(flags); - - /* Check if we got one. */ - - if (!sigq) - { - /* No...Try the resource pool */ - - sigq = kmm_malloc(sizeof(sigq_t)); - - /* Check if we got an allocated message */ - - if (sigq) - { - sigq->type = SIG_ALLOC_DYN; - } - } } return sigq; diff --git a/sched/signal/sig_dispatch.c b/sched/signal/sig_dispatch.c index 9746f0135d..3ec5d9eff9 100644 --- a/sched/signal/sig_dispatch.c +++ b/sched/signal/sig_dispatch.c @@ -214,49 +214,17 @@ static FAR sigpendq_t *nxsig_alloc_pendingsignal(void) { FAR sigpendq_t *sigpend; - /* Check if we were called from an interrupt handler. */ + /* Try to get the pending signal structure from the free list */ - if (up_interrupt_context()) + sigpend = (FAR sigpendq_t *)sq_remfirst(&g_sigpendingsignal); + if (!sigpend && up_interrupt_context()) { - /* Try to get the pending signal structure from the free list */ + /* If no pending signal structure is available in the free list, + * then try the special list of structures reserved for + * interrupt handlers + */ - sigpend = (FAR sigpendq_t *)sq_remfirst(&g_sigpendingsignal); - if (!sigpend) - { - /* If no pending signal structure is available in the free list, - * then try the special list of structures reserved for - * interrupt handlers - */ - - sigpend = (FAR sigpendq_t *)sq_remfirst(&g_sigpendingirqsignal); - } - } - - /* If we were not called from an interrupt handler, then we are - * free to allocate pending action structures if necessary. - */ - - else - { - /* Try to get the pending signal structure from the free list */ - - sigpend = (FAR sigpendq_t *)sq_remfirst(&g_sigpendingsignal); - - /* Check if we got one. */ - - if (!sigpend) - { - /* No... Allocate the pending signal */ - - sigpend = kmm_malloc(sizeof(sigpendq_t)); - - /* Check if we got an allocated message */ - - if (sigpend) - { - sigpend->type = SIG_ALLOC_DYN; - } - } + sigpend = (FAR sigpendq_t *)sq_remfirst(&g_sigpendingirqsignal); } return sigpend; @@ -371,6 +339,80 @@ static FAR sigpendq_t *nxsig_add_pendingsignal(FAR struct tcb_s *stcb, return sigpend; } +/**************************************************************************** + * Name: nxsig_alloc_dyn_pending + * + * Description: + * Dynamically allocate more pending signal and pending sigaction + * structures, if there are no more left. Note that this leaves the + * the critical section for the allocation. During that time it is + * it is possible that structures are freed, or another signalling thread + * allocates more structures. This is not an issue, any extra pending + * structures are freed after they get used. + * + * Assumptions: + * Called with g_sigpendingsignal and g_sigpendingaction locked by + * critical section. + * + ****************************************************************************/ + +static irqstate_t nxsig_alloc_dyn_pending(irqstate_t flags) +{ + if (!up_interrupt_context()) + { + bool alloc_signal = sq_empty(&g_sigpendingsignal); + bool alloc_sigact = sq_empty(&g_sigpendingaction); + + /* Signals are not dispatched from the idle task */ + + DEBUGASSERT(!sched_idletask()); + + if (alloc_signal || alloc_sigact) + { + FAR sigpendq_t *sigpend = NULL; + FAR sigq_t *sigq = NULL; + + /* Leave critical section for the duration of heap operations */ + + leave_critical_section(flags); + + /* Allocate more pending signals if there are no more */ + + if (alloc_signal) + { + sigpend = kmm_malloc(sizeof(sigpendq_t)); + } + + /* Allocate more pending signal actions if there are no more */ + + if (alloc_sigact) + { + sigq = kmm_malloc(sizeof(sigq_t)); + } + + /* Restore critical section and add the allocated structures to + * the free pending queues + */ + + flags = enter_critical_section(); + + if (sigpend) + { + sigpend->type = SIG_ALLOC_DYN; + sq_addfirst((sq_entry_t *)sigpend, &g_sigpendingsignal); + } + + if (sigq) + { + sigq->type = SIG_ALLOC_DYN; + sq_addfirst((sq_entry_t *)sigq, &g_sigpendingaction); + } + } + } + + return flags; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -435,6 +477,14 @@ int nxsig_tcbdispatch(FAR struct tcb_s *stcb, siginfo_t *info) flags = enter_critical_section(); + /* Make sure that there is always at least one sigpednq and sigq structure + * available, in case one needs to be queued later. Note that this breaks + * the critical section if it needs to allocate any new structures. So it + * needs to be done here before using the task state or sigprocmask. + */ + + flags = nxsig_alloc_dyn_pending(flags); + masked = nxsig_ismember(&stcb->sigprocmask, info->si_signo); #ifdef CONFIG_LIB_SYSCALL