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 4852e5fd4b4572457a4ad470b48d9ec510a462fc
Author: Marco Casaroli <[email protected]>
AuthorDate: Thu Oct 29 12:26:49 2026 +0100

    sched/task: Give the fork() child a copy of the task information.
    
    nxtask_setup_fork() gave the child of fork() the new task information
    that group_allocate() made for the group of the child.  This task
    information was empty: no fork handlers, no exit functions, no streams
    other than new stdin, stdout and stderr, no keys and no loaded modules.
    Thus in the child:
    
    - The child handlers of pthread_atfork() did not run.  A mutex that a
      prepare handler locked stayed locked, and the next lock of it in the
      child did not return.  For example, each fork() of a Perl that uses
      threads stopped in the child.
    - exit() did not call the functions of atexit(), and exit() and
      fflush(NULL) did not flush the streams that the parent opened.
    - pthread_key_create() could give a key that the parent used.  The value
      of that key was the value of the parent in the copy of the TLS.
    
    POSIX says that the child has a copy of the address space of the parent.
    The copy has the task information of the parent at the same address,
    with all of the above.  The child now uses that copy.
    
    nxtask_setup_fork() frees the new task information before the copy,
    while the address environment of the parent is current.  Thus the block
    is in neither heap.  This replaces the free in nxtask_start_fork().  The
    child does not need env_dup(), because the copy has the environment.
    
    vfork() does not change.  Its child shares the memory of the parent and
    has its own task information.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 sched/task/task_fork.c | 57 ++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 44 insertions(+), 13 deletions(-)

diff --git a/sched/task/task_fork.c b/sched/task/task_fork.c
index 8bd6c59776e..50ecf10ab6c 100644
--- a/sched/task/task_fork.c
+++ b/sched/task/task_fork.c
@@ -124,6 +124,38 @@ static void fork_inherit_tls(FAR struct tcb_s *child)
   info->tl_tid  = child->pid;
 }
 
+/****************************************************************************
+ * Name: fork_inherit_info
+ *
+ * Description:
+ *   Give the fork() child the task information of its parent.
+ *
+ *   group_allocate() made new task information for the group of the child.
+ *   It is in the user heap of the parent, because the child has no address
+ *   environment yet.  It has no fork handlers, exit functions, streams,
+ *   keys or auxiliary vector.  The copy of the address environment has the
+ *   task information of the parent at the same address, with all of these.
+ *   The child uses that copy, as it uses the copy of each other object of
+ *   the parent.
+ *
+ *   Call this before the copy, with the address environment of the parent
+ *   current: then the new block is in neither heap.
+ *
+ * Input Parameters:
+ *   parent - The parent task's TCB
+ *   child  - The child task's TCB
+ *
+ ****************************************************************************/
+
+static void fork_inherit_info(FAR struct tcb_s *parent,
+                              FAR struct tcb_s *child)
+{
+#ifdef CONFIG_MM_KERNEL_HEAP
+  group_free(child->group, child->group->tg_info);
+  child->group->tg_info = parent->group->tg_info;
+#endif
+}
+
 /****************************************************************************
  * Name: fork_restore_parent_env
  *
@@ -259,9 +291,11 @@ FAR struct tcb_s *nxtask_setup_fork(start_t retaddr, bool 
vfork)
            * the parent's contents -- including its stack -- at the parent's
            * virtual addresses, which is what lets the child go on to inherit
            * the stack address rather than be given a relocated copy.  See
-           * fork_inherit_stack().
+           * fork_inherit_stack().  The same applies to the task
+           * information:  see fork_inherit_info().
            */
 
+          fork_inherit_info(parent, child);
           ret = addrenv_fork(parent, child);
           if (ret >= 0)
             {
@@ -316,12 +350,17 @@ FAR struct tcb_s *nxtask_setup_fork(start_t retaddr, bool 
vfork)
   DEBUGASSERT(vfork);
 #endif
 
-  /* Duplicate the parent tasks environment */
+  /* Duplicate the parent tasks environment.  A child that has the copy of
+   * the task information of its parent has the environment in it.
+   */
 
-  ret = env_dup(child->group->tg_info, environ);
-  if (ret < 0)
+  if (child->group->tg_info != parent->group->tg_info)
     {
-      goto errout_with_tcb;
+      ret = env_dup(child->group->tg_info, environ);
+      if (ret < 0)
+        {
+          goto errout_with_tcb;
+        }
     }
 
   /* Associate file descriptors with the new task */
@@ -512,14 +551,6 @@ pid_t nxtask_start_fork(FAR struct tcb_s *child, bool 
vfork)
       (child->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_KERNEL)
     {
       fork_restore_parent_env();
-
-#ifdef CONFIG_MM_KERNEL_HEAP
-      /* group_allocate() put the child's task_info in the parent's heap
-       * before the copy.  The child uses its own copy: free this one.
-       */
-
-      group_free(child->group, child->group->tg_info);
-#endif
     }
 #endif
 

Reply via email to