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 a6d583afb4118e504bead6f9a390f24ce1899b2f
Author: Marco Casaroli <[email protected]>
AuthorDate: Sun Oct 18 20:06:19 2026 +0200

    fs/inode: Enforce RLIMIT_NOFILE for new file descriptors.
    
    A process could open files up to OPEN_MAX, whatever its RLIMIT_NOFILE
    soft limit was.
    
    A new file descriptor must now be below the soft limit of the process,
    as POSIX says: open(), dup() and fcntl(F_DUPFD) fail with EMFILE when
    the lowest free descriptor is at or above it (F_DUPFD with EINVAL when
    the lowest descriptor that it may use is already at or above it), and
    dup2() and dup3() fail with EBADF for a target at or above it.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 fs/inode/fs_files.c | 36 +++++++++++++++++++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/fs/inode/fs_files.c b/fs/inode/fs_files.c
index 6407f0eb9bd..3dc715ce31e 100644
--- a/fs/inode/fs_files.c
+++ b/fs/inode/fs_files.c
@@ -26,6 +26,7 @@
 
 #include <nuttx/config.h>
 
+#include <sys/param.h>
 #include <sys/types.h>
 #include <string.h>
 #include <assert.h>
@@ -45,6 +46,7 @@
 #include <nuttx/spawn.h>
 #include <nuttx/spinlock.h>
 #include <nuttx/lib/lib.h>
+#include <nuttx/nuttx.h>
 
 #ifdef CONFIG_FDCHECK
 #  include <nuttx/fdcheck.h>
@@ -58,6 +60,23 @@
  * Private Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: fdlist_limit
+ *
+ * Description:
+ *   Return the RLIMIT_NOFILE soft limit of the task group that owns the
+ *   list: a new file descriptor must be below it.
+ *
+ ****************************************************************************/
+
+static inline int fdlist_limit(FAR struct fdlist *list)
+{
+  FAR struct task_group_s *group =
+    container_of(list, struct task_group_s, tg_fdlist);
+
+  return MIN(group->tg_rlimit_nofile.rlim_cur, OPEN_MAX);
+}
+
 /****************************************************************************
  * Name: fdlist_get_by_index
  ****************************************************************************/
@@ -319,7 +338,7 @@ int fdlist_dup3(FAR struct fdlist *list, int fd1, int fd2, 
int flags)
 
   /* Get the file descriptor list.  It should not be NULL in this context. */
 
-  if (fd2 < 0)
+  if (fd2 < 0 || fd2 >= fdlist_limit(list))
     {
       return -EBADF;
     }
@@ -568,6 +587,7 @@ int fdlist_dupfile(FAR struct fdlist *list, int oflags, int 
minfd,
 {
   FAR struct fd *fdp;
   irqstate_t flags;
+  int limit;
   int ret;
   int i;
   int j;
@@ -578,6 +598,12 @@ int fdlist_dupfile(FAR struct fdlist *list, int oflags, 
int minfd,
   minfd = fdcheck_restore(minfd);
 #endif
 
+  limit = fdlist_limit(list);
+  if (minfd >= limit)
+    {
+      return -EINVAL;
+    }
+
   i = minfd / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK;
   j = minfd % CONFIG_NFILE_DESCRIPTORS_PER_BLOCK;
 
@@ -605,6 +631,14 @@ int fdlist_dupfile(FAR struct fdlist *list, int oflags, 
int minfd,
           fdp = &list->fl_fds[i][j];
           if (fdp->f_file == NULL)
             {
+              /* The lowest free descriptor is beyond RLIMIT_NOFILE */
+
+              if (i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + j >= limit)
+                {
+                  spin_unlock_irqrestore_notrace(&list->fl_lock, flags);
+                  return -EMFILE;
+                }
+
               atomic_add(&filep->f_refs, 1);
               fdp->f_file        = filep;
               fdp->f_cloexec     = !!(oflags & O_CLOEXEC);

Reply via email to