This is an automated email from the ASF dual-hosted git repository. anchao pushed a commit to branch fix/fdlist-concurrent-leak in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit be28936547917cc5eb9ec80fdb439788163ff962 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 | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/fs/inode/fs_files.c b/fs/inode/fs_files.c index 3dc715ce31e..6f17691330d 100644 --- a/fs/inode/fs_files.c +++ b/fs/inode/fs_files.c @@ -156,13 +156,18 @@ 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. Instead of blindly memcpy()ing the whole list (which + * would overwrite - and leak - the buffers we allocated for those rows), + * merge row by row: */ - if (orig_rows != list->fl_rows && list->fl_rows >= row) + if (list->fl_rows >= row) { + /* Another thread has already published a list with at least `row` + * rows -- keep it and release every buffer we allocated. + */ + spin_unlock_irqrestore_notrace(&list->fl_lock, flags); for (j = orig_rows; j < i; j++) @@ -175,6 +180,15 @@ static int fdlist_extend(FAR struct fdlist *list, size_t row) return OK; } + /* Release the buffers we allocated for any rows that the current list + * already supplies (its pointers will be copied in below). + */ + + for (j = orig_rows; j < i && j < list->fl_rows; j++) + { + fs_heap_free(fds[j]); + } + if (list->fl_fds != NULL) { memcpy(fds, list->fl_fds, list->fl_rows * sizeof(FAR struct fd *));
