This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 09e49aa5864 fs/inode: Fix descriptor-block leak on concurrent 
fdlist_extend()
09e49aa5864 is described below

commit 09e49aa5864dec8799233adbe803a6895d166ef6
Author: chao an <[email protected]>
AuthorDate: Wed Nov 4 16:52:27 2026 +0800

    fs/inode: Fix descriptor-block leak on concurrent fdlist_extend()
    
    When fdlist_extend() grows the file-descriptor list, it allocates the
    new row array and per-row descriptor blocks while the list lock is
    dropped.  If another thread also grows the list in the meantime
    (range (orig_rows, row)), the old code ran memcpy() with the already
    published row pointers, overwriting - and permanently leaking - the
    descriptor blocks this thread just allocated for those rows.
    
    Merge the list row by row instead: release the buffers that the
    published list already supplies before copying in its pointers, and
    drop the redundant single request when fl_rows >= row.
    
    Signed-off-by: chao an <[email protected]>
---
 fs/inode/fs_files.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/fs/inode/fs_files.c b/fs/inode/fs_files.c
index 3dc715ce31e..be522834f71 100644
--- a/fs/inode/fs_files.c
+++ b/fs/inode/fs_files.c
@@ -117,6 +117,7 @@ static int fdlist_extend(FAR struct fdlist *list, size_t 
row)
   int i;
   int j;
 
+retry:
   orig_rows = list->fl_rows;
   if (row <= orig_rows)
     {
@@ -156,12 +157,16 @@ static int fdlist_extend(FAR struct fdlist *list, size_t 
row)
 
   flags = spin_lock_irqsave_notrace(&list->fl_lock);
 
-  /* To avoid race condition, if the file list is updated by other threads
-   * and list rows is greater or equal than temp list,
-   * release the obsolete buffers
+  /* Another thread may have extended the list while we were allocating the
+   * buffers above.  We must not release the temporary buffers while holding
+   * fl_lock, because fs_heap_free() -> mm_lock() -> nxmutex_lock() may
+   * sleep, which spin_lock_irqsave() forbids.  Drop the lock, release the
+   * temporary
+   * buffers and retry instead.  For the rows the winning thread already
+   * published, retry re-reads orig_rows and returns 0 from the top.
    */
 
-  if (orig_rows != list->fl_rows && list->fl_rows >= row)
+  if (list->fl_rows != orig_rows)
     {
       spin_unlock_irqrestore_notrace(&list->fl_lock, flags);
 
@@ -172,7 +177,7 @@ static int fdlist_extend(FAR struct fdlist *list, size_t 
row)
 
       fs_heap_free(fds);
 
-      return OK;
+      goto retry;
     }
 
   if (list->fl_fds != NULL)

Reply via email to