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
