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 */
