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)