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 bc026c971299ed8f1ea662799be5130ac9b74bb7 Author: Marco Casaroli <[email protected]> AuthorDate: Fri Oct 30 10:01:30 2026 +0100 sched/mqueue: Wake up the waiters of a removed System V message queue. msgctl(IPC_RMID) freed the queue, but the tasks blocked in msgrcv() or msgsnd() on it stayed blocked forever. Their TCB still pointed to the freed queue, so a later signal, cancellation or task exit wrote to freed memory. A task that msgsnd() or msgrcv() had woken up, but that did not run before the removal, also used the freed queue when it ran. POSIX requires that msgrcv() and msgsnd() fail with EIDRM when the queue is removed. nxmsg_free() now takes the queue out of the table first, then wakes up all tasks in its two wait lists with EIDRM, and then frees the messages and the queue. After a wake-up, msgrcv() and msgsnd() look up the queue identifier again and fail with EIDRM when the queue is gone, before they use the queue. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- include/sys/msg.h | 2 ++ sched/mqueue/msg.h | 2 +- sched/mqueue/msginternal.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- sched/mqueue/msgrcv.c | 9 +++++++++ sched/mqueue/msgsnd.c | 9 +++++++++ 5 files changed, 65 insertions(+), 3 deletions(-) diff --git a/include/sys/msg.h b/include/sys/msg.h index ba63a9c3f93..0bf0ba048f2 100644 --- a/include/sys/msg.h +++ b/include/sys/msg.h @@ -170,6 +170,7 @@ int msgget(key_t key, int msgflg); * less than 1, or msgsz is greater than the maximum message * size of the queue. * EINTR The call was interrupted by a signal handler. + * EIDRM The queue was removed while the caller waited. * ****************************************************************************/ @@ -203,6 +204,7 @@ int msgsnd(int msqid, FAR const void *msgp, size_t msgsz, int msgflg); * ENOMSG IPC_NOWAIT is set and the queue has no message of the * requested type. * EINTR The call was interrupted by a signal handler. + * EIDRM The queue was removed while the caller waited. * ****************************************************************************/ diff --git a/sched/mqueue/msg.h b/sched/mqueue/msg.h index 41124fad318..68e66c83ae4 100644 --- a/sched/mqueue/msg.h +++ b/sched/mqueue/msg.h @@ -96,7 +96,7 @@ int nxmsg_alloc(FAR struct msgq_s **pmsgq); * Name: nxmsg_free * * Description: - * Free the message queue instance + * Remove the message queue, wake up its waiters with EIDRM and free it * ****************************************************************************/ diff --git a/sched/mqueue/msginternal.c b/sched/mqueue/msginternal.c index 97dbacfeae8..7034072614e 100644 --- a/sched/mqueue/msginternal.c +++ b/sched/mqueue/msginternal.c @@ -26,6 +26,7 @@ #include <nuttx/kmalloc.h> #include "mqueue/msg.h" +#include "sched/sched.h" /**************************************************************************** * Pre-processor Definitions @@ -97,6 +98,41 @@ static FAR struct msgq_s *nxmsg_alloc_internal(void) return msgq; } +/**************************************************************************** + * Name: nxmsg_wakeup + * + * Description: + * Wake up all tasks that wait on a removed queue. Their msgrcv() or + * msgsnd() fails with EIDRM. + * + ****************************************************************************/ + +static void nxmsg_wakeup(FAR struct msgq_s *msgq) +{ + FAR struct tcb_s *rtcb = this_task(); + FAR struct tcb_s *wtcb; + bool need_switch = false; + + while ((wtcb = (FAR struct tcb_s *) + dq_remfirst(MQ_WNELIST(msgq->cmn))) != NULL || + (wtcb = (FAR struct tcb_s *) + dq_remfirst(MQ_WNFLIST(msgq->cmn))) != NULL) + { + wtcb->waitobj = NULL; + wtcb->errcode = EIDRM; + + if (nxsched_add_readytorun(wtcb)) + { + need_switch = true; + } + } + + if (need_switch) + { + up_switch_context(this_task(), rtcb); + } +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -143,6 +179,13 @@ void nxmsg_free(FAR struct msgq_s *msgq) index = msgq->key - 1; + /* Remove the queue from the table first, so that no task finds it after + * the wake-up or while kmm_free() waits for the heap lock. + */ + + g_msgqs[index] = NULL; + nxmsg_wakeup(msgq); + list_for_every_entry_safe(&msgq->msglist, entry, tmp, struct msgbuf_s, node) { @@ -150,8 +193,7 @@ void nxmsg_free(FAR struct msgq_s *msgq) list_add_tail(&g_msgfreelist, &entry->node); } - kmm_free(g_msgqs[index]); - g_msgqs[index] = NULL; + kmm_free(msgq); } /**************************************************************************** diff --git a/sched/mqueue/msgrcv.c b/sched/mqueue/msgrcv.c index a3c58355d2e..2402991e301 100644 --- a/sched/mqueue/msgrcv.c +++ b/sched/mqueue/msgrcv.c @@ -45,6 +45,7 @@ static int msgrcv_wait(FAR struct msgq_s *msgq, FAR struct msgbuf_s **rcvmsg, FAR struct msgbuf_s *newmsg = NULL; FAR struct msgbuf_s *tmp; FAR struct tcb_s *rtcb; + key_t key = msgq->key; #ifdef CONFIG_CANCELLATION_POINTS /* msgrcv_wait() is not a cancellation point, but it may be called @@ -160,6 +161,13 @@ static int msgrcv_wait(FAR struct msgq_s *msgq, FAR struct msgbuf_s **rcvmsg, { return -rtcb->errcode; } + + /* msgctl(IPC_RMID) may have freed the queue before this task ran */ + + if (nxmsg_lookup(key) != msgq) + { + return -EIDRM; + } } found: @@ -200,6 +208,7 @@ found: * ENOMSG IPC_NOWAIT is set and the queue has no message of the * requested type. * EINTR The call was interrupted by a signal handler. + * EIDRM The queue was removed while the caller waited. * ****************************************************************************/ diff --git a/sched/mqueue/msgsnd.c b/sched/mqueue/msgsnd.c index 1f0324392b4..b4f5ad911e1 100644 --- a/sched/mqueue/msgsnd.c +++ b/sched/mqueue/msgsnd.c @@ -42,6 +42,7 @@ static int msgsnd_wait(FAR struct msgq_s *msgq, int msgflg) { FAR struct tcb_s *rtcb; + key_t key = msgq->key; #ifdef CONFIG_CANCELLATION_POINTS /* msgsnd_wait() is not a cancellation point, but may be called via @@ -117,6 +118,13 @@ static int msgsnd_wait(FAR struct msgq_s *msgq, int msgflg) { return -rtcb->errcode; } + + /* msgctl(IPC_RMID) may have freed the queue before this task ran */ + + if (nxmsg_lookup(key) != msgq) + { + return -EIDRM; + } } return OK; @@ -152,6 +160,7 @@ static int msgsnd_wait(FAR struct msgq_s *msgq, int msgflg) * less than 1, or msgsz is greater than the maximum message * size of the queue. * EINTR The call was interrupted by a signal handler. + * EIDRM The queue was removed while the caller waited. * ****************************************************************************/
