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);
         }
     }

Reply via email to