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.
  *
  ****************************************************************************/
 

Reply via email to