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 50c78843b40f5bba5126c22cae13c742baa7f9fc Author: dongjiuzhu1 <[email protected]> AuthorDate: Tue Jul 29 10:26:50 2025 +0800 fs/inode: Fix fd_tag_san/fd_tag_fdcheck loss during parent-to-child inheritance When a child process inherits file descriptors from its parent via fdlist_copy(), the fd tags (fd_tag_fdsan and fd_tag_fdcheck) were not being copied. This caused assertion failures when the child process closed inherited file descriptors, because the fdcheck/fdsan subsystems expected the tags to match. Root Cause: ---------- The fdlist_install() function was not preserving the fd tags during fd duplication. When copying fds from parent to child in fdlist_copy(), the tags were lost, resulting in: - fd_tag_fdsan: Used for file descriptor ownership tracking (FDSAN) - fd_tag_fdcheck: Used for fd validity checking (FDCHECK) Both tags being reset to 0/NULL instead of copied from parent. Symptom: -------- Child processes would crash with assertion failure in fdcheck_restore() when closing inherited file descriptors: fdcheck_restore+0x69/0xa0 fdlist_get2+0x11/0x48 fdlist_close+0xd/0x94 close+0x15/0x30 This occurred because fdcheck_restore() validates that the fd_tag_fdcheck matches the expected value, and the mismatch triggered an assertion. Solution: --------- 1. Add a 'copy' parameter to fdlist_install() to distinguish between: - New fd allocation (copy=false): Initialize fresh tags - Fd duplication (copy=true): Preserve tags from source fd 2. Add fdp parameter to fdlist_install() to access source fd tags 3. In fdlist_copy(), pass copy=true to preserve parent's fd tags 4. In fdlist_dup3(), pass copy=false since dup operations should create independent fd tracking (not copy parent tags) Changes: -------- - fdlist_install(): Added 'fdp' and 'copy' parameters - When copy=true, preserve fd_tag_fdsan and fd_tag_fdcheck from source - fdlist_copy(): Pass copy=true to preserve parent tags - fdlist_dup3(): Pass copy=false for normal dup behavior Impact: ------- This fix ensures that file descriptor ownership tracking and validity checking work correctly across fork/clone operations, preventing crashes when child processes close inherited file descriptors. Without this fix, any program using fork() with inherited fds would crash if CONFIG_FDSAN or CONFIG_FDCHECK were enabled. Issue backtrace: backtrace_unwind+0x105/0x108 sched_backtrace+0x6f/0x80 sched_dumpstack+0x33/0x80 _assert+0x229/0x510 arm_syscall+0x81/0x98 up_assert+0xd/0x18 __assert+0x1d/0x24 fdcheck_restore+0x69/0xa0 fdlist_get2+0x11/0x48 fdlist_close+0xd/0x94 close+0x15/0x30 closefd+0x5/0x30 notify_parent_process+0x2b/0x40 run_helper_tcp4_echo_server+0x75/0x114 run_test_part+0x5f/0x68 uv_run_tests_main+0x35/0x60 run_test_part+0x5f/0x68 uv_run_tests_main+0x35/0x60 nxtask_startup+0x13/0x2c nxtask_start+0x4d/0x64 Signed-off-by: dongjiuzhu1 <[email protected]> --- fs/inode/fs_files.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/fs/inode/fs_files.c b/fs/inode/fs_files.c index 6e16601bcf0..2645a40fe31 100644 --- a/fs/inode/fs_files.c +++ b/fs/inode/fs_files.c @@ -203,10 +203,11 @@ static void fdlist_uninstall(FAR struct fdlist *list, FAR struct fd *fdp) } static void fdlist_install(FAR struct fdlist *list, int fd, - FAR struct file *filep, int oflags) + FAR struct file *filep, FAR struct fd *fdp, + int oflags, bool copy) { - FAR struct file *oldfilep; - FAR struct fd *fdp; + FAR struct file *filep1; + FAR struct fd *fdp1; irqstate_t flags; int l1; int l2; @@ -216,15 +217,24 @@ static void fdlist_install(FAR struct fdlist *list, int fd, flags = spin_lock_irqsave_notrace(&list->fl_lock); - fdp = &list->fl_fds[l1][l2]; - oldfilep = fdp->f_file; - fdp->f_file = filep; + fdp1 = &list->fl_fds[l1][l2]; + filep1 = fdp1->f_file; + fdp1->f_file = filep; file_ref(filep); - fdp->f_cloexec = !!(oflags & O_CLOEXEC); - FS_ADD_BACKTRACE(fdp); + fdp1->f_cloexec = !!(oflags & O_CLOEXEC); + FS_ADD_BACKTRACE(fdp1); + if (copy) + { +#ifdef CONFIG_FDSAN + fdp1->f_tag_fdsan = fdp->f_tag_fdsan; +#endif +#ifdef CONFIG_FDCHECK + fdp1->f_tag_fdcheck = fdp->f_tag_fdcheck; +#endif + } spin_unlock_irqrestore_notrace(&list->fl_lock, flags); - file_put(oldfilep); + file_put(filep1); } /**************************************************************************** @@ -295,6 +305,7 @@ static void task_fssync(FAR struct tcb_s *tcb, FAR void *arg) int fdlist_dup3(FAR struct fdlist *list, int fd1, int fd2, int flags) { FAR struct file *filep1; + FAR struct fd *fdp1; int ret; if (fd1 == fd2) @@ -323,13 +334,13 @@ int fdlist_dup3(FAR struct fdlist *list, int fd1, int fd2, int flags) } } - ret = fdlist_get(list, fd1, &filep1); + ret = fdlist_get2(list, fd1, &filep1, &fdp1); if (ret < 0) { return ret; } - fdlist_install(list, fd2, filep1, flags); + fdlist_install(list, fd2, filep1, fdp1, flags, false); file_put(filep1); #ifdef CONFIG_FDCHECK @@ -820,7 +831,7 @@ int fdlist_copy(FAR struct fdlist *plist, FAR struct fdlist *clist, /* Assign filep to the child's descriptor list. Omit the flags */ - fdlist_install(clist, fd, filep, 0); + fdlist_install(clist, fd, filep, fdp, 0, true); file_put(filep); } }
