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 d12c3c47cdacdc72cdf638954edfea804599499c
Author: Marco Casaroli <[email protected]>
AuthorDate: Mon Oct 19 23:32:58 2026 +0200

    fs/vfs: Fill a local stat buffer and copy it out at the end.
    
    stat(), lstat() and fstat() give the stat method of a mount point the
    caller's buffer as it is, and a file system sets only some of the fields:
    binfs_stat() sets six of them, and no file system sets st_dev.  The other
    fields keep what the buffer held before the call.
    
    These functions, statfs() and fstatfs() also write the caller's buffer
    field by field while they hold an inode reference, the temporary path
    buffer of inode_search_setup() or a file reference, and they change the
    buffer when the call fails.
    
    Fill a buffer on the kernel stack instead, and copy it to the caller's
    buffer with one memcpy() after the references and the path buffer are
    released.  The local buffer is cleared first, so the fields that a file
    system does not set are 0, and old kernel stack data does not reach the
    caller.  The buffer of the caller is changed only on success.  This costs
    one struct stat or struct statfs on the stack of these five functions.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 fs/vfs/fs_fstat.c   | 16 ++++++++++++++--
 fs/vfs/fs_fstatfs.c | 17 +++++++++++------
 fs/vfs/fs_stat.c    | 20 +++++++++++++++++++-
 fs/vfs/fs_statfs.c  | 12 ++++++++----
 4 files changed, 52 insertions(+), 13 deletions(-)

diff --git a/fs/vfs/fs_fstat.c b/fs/vfs/fs_fstat.c
index 4688a585932..11f242338c5 100644
--- a/fs/vfs/fs_fstat.c
+++ b/fs/vfs/fs_fstat.c
@@ -27,6 +27,7 @@
 #include <nuttx/config.h>
 
 #include <sys/stat.h>
+#include <string.h>
 #include <unistd.h>
 #include <sched.h>
 #include <assert.h>
@@ -243,6 +244,7 @@ int file_fstat(FAR struct file *filep, FAR struct stat *buf)
 int nx_fstat(int fd, FAR struct stat *buf)
 {
   FAR struct file *filep;
+  struct stat kbuf;
   int ret;
 
   /* First, get the file structure.  Note that on failure,
@@ -252,10 +254,20 @@ int nx_fstat(int fd, FAR struct stat *buf)
   ret = file_get(fd, &filep);
   if (ret >= 0)
     {
-      /* Perform the fstat operation */
+      /* Perform the fstat operation on a local buffer.  Copy it to the
+       * caller's buffer after file_put(), so that a bad buffer faults with
+       * no file reference held.
+       */
+
+      /* Clear it first: a file system fills only some of the fields */
 
-      ret = file_fstat(filep, buf);
+      memset(&kbuf, 0, sizeof(kbuf));
+      ret = file_fstat(filep, &kbuf);
       file_put(filep);
+      if (ret >= 0)
+        {
+          memcpy(buf, &kbuf, sizeof(kbuf));
+        }
     }
 
   return ret;
diff --git a/fs/vfs/fs_fstatfs.c b/fs/vfs/fs_fstatfs.c
index d6879efb933..345e6a0b1e9 100644
--- a/fs/vfs/fs_fstatfs.c
+++ b/fs/vfs/fs_fstatfs.c
@@ -62,6 +62,7 @@ int fstatfs(int fd, FAR struct statfs *buf)
 #ifndef CONFIG_DISABLE_MOUNTPOINT
   FAR struct inode *inode;
 #endif
+  struct statfs kbuf;
   int ret;
 
   DEBUGASSERT(buf != NULL);
@@ -96,8 +97,8 @@ int fstatfs(int fd, FAR struct statfs *buf)
         {
           /* Perform the statfs() operation */
 
-          memset(buf, 0, sizeof(struct statfs));
-          ret = inode->u.i_mops->statfs(inode, buf);
+          memset(&kbuf, 0, sizeof(kbuf));
+          ret = inode->u.i_mops->statfs(inode, &kbuf);
         }
     }
   else
@@ -105,19 +106,23 @@ int fstatfs(int fd, FAR struct statfs *buf)
     {
       /* The node is part of the root pseudo file system */
 
-      memset(buf, 0, sizeof(struct statfs));
-      buf->f_type    = PROC_SUPER_MAGIC;
-      buf->f_namelen = NAME_MAX;
+      memset(&kbuf, 0, sizeof(kbuf));
+      kbuf.f_type    = PROC_SUPER_MAGIC;
+      kbuf.f_namelen = NAME_MAX;
       ret            = OK;
     }
 
-  /* Check if the fstat operation was successful */
+  /* Check if the fstat operation was successful.  Copy the result to the
+   * caller's buffer after file_put(), so that a bad buffer faults with no
+   * file reference held.
+   */
 
   file_put(filep);
   if (ret >= 0)
     {
       /* Successfully statfs'ed the file */
 
+      memcpy(buf, &kbuf, sizeof(kbuf));
       return OK;
     }
 
diff --git a/fs/vfs/fs_stat.c b/fs/vfs/fs_stat.c
index d231073bebc..249bba6216e 100644
--- a/fs/vfs/fs_stat.c
+++ b/fs/vfs/fs_stat.c
@@ -188,6 +188,9 @@ errout_with_search:
 
 int nx_stat(FAR const char *path, FAR struct stat *buf, int resolve)
 {
+  struct stat kbuf;
+  int ret;
+
   /* Sanity checks */
 
   if (path == NULL  || buf == NULL)
@@ -202,9 +205,24 @@ int nx_stat(FAR const char *path, FAR struct stat *buf, 
int resolve)
 
   /* The perform the stat() operation on the path.  This is potentially
    * recursive if soft link support is enabled.
+   *
+   * Fill a local buffer and copy it to the caller's buffer at the end, when
+   * the inode and the path buffer are released.  A bad buffer then faults
+   * with nothing held.
+   */
+
+  /* Clear it first: a file system fills only some of the fields, and the
+   * others must not give the caller what the kernel stack held.
    */
 
-  return stat_recursive(path, buf, resolve);
+  memset(&kbuf, 0, sizeof(kbuf));
+  ret = stat_recursive(path, &kbuf, resolve);
+  if (ret >= 0)
+    {
+      memcpy(buf, &kbuf, sizeof(kbuf));
+    }
+
+  return ret;
 }
 
 /****************************************************************************
diff --git a/fs/vfs/fs_statfs.c b/fs/vfs/fs_statfs.c
index 29d597f8a4a..c0e416f098c 100644
--- a/fs/vfs/fs_statfs.c
+++ b/fs/vfs/fs_statfs.c
@@ -75,6 +75,7 @@ int statfs(FAR const char *path, FAR struct statfs *buf)
 {
   struct inode_search_s desc;
   FAR struct inode *inode;
+  struct statfs kbuf;
   int ret = OK;
 
   /* Sanity checks */
@@ -121,10 +122,12 @@ int statfs(FAR const char *path, FAR struct statfs *buf)
     }
 
   /* The way we handle the statfs depends on the type of inode that we
-   * are dealing with.
+   * are dealing with.  Fill a local buffer: it is copied to the caller's
+   * buffer when the inode and the path buffer are released, so that a bad
+   * buffer faults with nothing held.
    */
 
-  memset(buf, 0, sizeof(struct statfs));
+  memset(&kbuf, 0, sizeof(kbuf));
 #ifndef CONFIG_DISABLE_MOUNTPOINT
   if (INODE_IS_MOUNTPT(inode))
     {
@@ -136,7 +139,7 @@ int statfs(FAR const char *path, FAR struct statfs *buf)
         {
           /* Perform the statfs() operation */
 
-          ret = inode->u.i_mops->statfs(inode, buf);
+          ret = inode->u.i_mops->statfs(inode, &kbuf);
         }
     }
   else
@@ -144,7 +147,7 @@ int statfs(FAR const char *path, FAR struct statfs *buf)
     {
       /* The node is part of the root pseudo file system */
 
-      ret = statpseudofs(inode, buf);
+      ret = statpseudofs(inode, &kbuf);
     }
 
   /* Check if the statfs operation was successful */
@@ -158,6 +161,7 @@ int statfs(FAR const char *path, FAR struct statfs *buf)
 
   inode_release(inode);
   inode_search_release(&desc);
+  memcpy(buf, &kbuf, sizeof(kbuf));
   return OK;
 
   /* Failure conditions always set the errno appropriately */

Reply via email to