This is an automated email from the ASF dual-hosted git repository.

michallenc 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 f7bdfabeac1 fs/vfs: validate iovcnt bounds and iov_len overflow in 
readv/writev
f7bdfabeac1 is described below

commit f7bdfabeac142057cd4816bf292f4443996217e6
Author: Swatantra Yadav <[email protected]>
AuthorDate: Sun Nov 8 03:12:58 2026 +0530

    fs/vfs: validate iovcnt bounds and iov_len overflow in readv/writev
    
    Add POSIX-mandated validation to file_readv() and file_writev():
    
    1. Reject iovcnt <= 0 or iovcnt > IOV_MAX with -EINVAL.
    2. Validate total iov_len does not overflow SSIZE_MAX with -EINVAL.
    3. Validate non-NULL iov_base for non-zero iov_len entries with
       -EFAULT (read path preserves kernel build exception for
       CONFIG_ARCH_TEXT_VBASE == 0).
    4. Move per-entry overflow and NULL checks from compat loops to
       upfront validation in the main functions.
    
    Update include/sys/uio.h doc comments to remove "(Not implemented)"
    annotations and fix readv/writev copy-paste typo.
    
    Reference: POSIX.1-2017 (IEEE Std 1003.1-2017) readv/writev [EINVAL].
    
    Assisted-by: Google Gemini:gemini-3.8-flash
    Signed-off-by: Swatantra Yadav <[email protected]>
---
 fs/vfs/fs_read.c  | 44 +++++++++++++++++++-------------------------
 fs/vfs/fs_write.c | 41 +++++++++++++++++------------------------
 include/sys/uio.h |  9 +++------
 3 files changed, 39 insertions(+), 55 deletions(-)

diff --git a/fs/vfs/fs_read.c b/fs/vfs/fs_read.c
index 2384365c11a..eda813edeb8 100644
--- a/fs/vfs/fs_read.c
+++ b/fs/vfs/fs_read.c
@@ -90,18 +90,6 @@ static ssize_t file_readv_compat(FAR struct file *filep,
           continue;
         }
 
-      /* Sanity check to avoid total length overflow */
-
-      if (SSIZE_MAX - ntotal < iov[i].iov_len)
-        {
-          if (ntotal > 0)
-            {
-              break;
-            }
-
-          return -EINVAL;
-        }
-
       nread = inode->u.i_ops->read(filep, iov[i].iov_base,
                                    iov[i].iov_len);
 
@@ -160,16 +148,18 @@ ssize_t file_readv(FAR struct file *filep,
                    FAR const struct iovec *iov, int iovcnt)
 {
   FAR struct inode *inode;
-  ssize_t ret;
+  size_t len = 0;
+  ssize_t ret = -EBADF;
+  int i;
 
   DEBUGASSERT(filep);
   inode = filep->f_inode;
 
   /* Check buffer count and pointer for iovec */
 
-  if (iovcnt == 0)
+  if (iovcnt <= 0 || iovcnt > IOV_MAX)
     {
-      return 0;
+      return -EINVAL;
     }
 
   if (iov == NULL)
@@ -183,20 +173,24 @@ ssize_t file_readv(FAR struct file *filep,
    * kernel builds will fail to load elf binaries found at 0.
    */
 
-#if !defined(CONFIG_BUILD_KERNEL) || CONFIG_ARCH_TEXT_VBASE != 0
+  for (i = 0; i < iovcnt; i++)
+    {
+      if (SSIZE_MAX - len < iov[i].iov_len)
+        {
+          return -EINVAL;
+        }
 
-  /* Are all iov_base accessible? */
+      len += iov[i].iov_len;
 
-  for (ret = 0; ret < iovcnt; ret++)
-    {
-      if (iov[ret].iov_base == NULL)
+#if !defined(CONFIG_BUILD_KERNEL) || CONFIG_ARCH_TEXT_VBASE != 0
+      /* Are all iov_base accessible? */
+
+      if (iov[i].iov_base == NULL && iov[i].iov_len != 0)
         {
           return -EFAULT;
         }
-    }
 #endif
-
-  ret = -EBADF;
+    }
 
   /* Was this file opened for read access? */
 
@@ -204,7 +198,7 @@ ssize_t file_readv(FAR struct file *filep,
     {
       /* No.. File is not read-able */
 
-      ret = -EBADF;
+      return -EBADF;
     }
 
   /* Is a driver or mountpoint registered? If so, does it support the read
@@ -214,7 +208,7 @@ ssize_t file_readv(FAR struct file *filep,
    * signature and position in the operations vtable.
    */
 
-  else if (inode != NULL && inode->u.i_ops)
+  if (inode != NULL && inode->u.i_ops)
     {
       clock_t start_time;
 
diff --git a/fs/vfs/fs_write.c b/fs/vfs/fs_write.c
index c290c868d2a..60b14e22150 100644
--- a/fs/vfs/fs_write.c
+++ b/fs/vfs/fs_write.c
@@ -72,23 +72,6 @@ static ssize_t file_writev_compat(FAR struct file *filep,
           continue;
         }
 
-      if (iov[i].iov_base == NULL)
-        {
-          return -EINVAL;
-        }
-
-      /* Sanity check to avoid total length overflow */
-
-      if (SSIZE_MAX - ntotal < iov[i].iov_len)
-        {
-          if (ntotal > 0)
-            {
-              break;
-            }
-
-          return -EINVAL;
-        }
-
       nwritten = inode->u.i_ops->write(filep, iov[i].iov_base,
                                        iov[i].iov_len);
 
@@ -150,7 +133,9 @@ ssize_t file_writev(FAR struct file *filep,
                     FAR const struct iovec *iov, int iovcnt)
 {
   FAR struct inode *inode;
-  ssize_t ret;
+  size_t len = 0;
+  ssize_t ret = -EBADF;
+  int i;
 
   /* Was this file opened for write access? */
 
@@ -161,9 +146,9 @@ ssize_t file_writev(FAR struct file *filep,
 
   /* Check buffer count and pointer for iovec */
 
-  if (iovcnt == 0)
+  if (iovcnt <= 0 || iovcnt > IOV_MAX)
     {
-      return 0;
+      return -EINVAL;
     }
 
   if (iov == NULL)
@@ -171,11 +156,20 @@ ssize_t file_writev(FAR struct file *filep,
       return -EFAULT;
     }
 
-  /* Are all iov_base accessible? */
+  /* Verify each iovec entry and check for total length overflow */
 
-  for (ret = 0; ret < iovcnt; ret++)
+  for (i = 0; i < iovcnt; i++)
     {
-      if (iov[ret].iov_base == NULL && iov[ret].iov_len != 0)
+      if (SSIZE_MAX - len < iov[i].iov_len)
+        {
+          return -EINVAL;
+        }
+
+      len += iov[i].iov_len;
+
+      /* Are all iov_base accessible? */
+
+      if (iov[i].iov_base == NULL && iov[i].iov_len != 0)
         {
           return -EFAULT;
         }
@@ -185,7 +179,6 @@ ssize_t file_writev(FAR struct file *filep,
    * If yes, then let the driver perform the write.
    */
 
-  ret = -EBADF;
   inode = filep->f_inode;
   if (inode != NULL && inode->u.i_ops)
     {
diff --git a/include/sys/uio.h b/include/sys/uio.h
index c9d3bb8ed48..cfcbc95b49d 100644
--- a/include/sys/uio.h
+++ b/include/sys/uio.h
@@ -105,7 +105,7 @@ extern "C"
  *    EINVAL.
  *      The sum of the iov_len values in the iov array overflowed an ssize_t
  *      or The 'iovcnt' argument was less than or equal to 0, or greater than
- *      IOV_MAX (Not implemented).
+ *      IOV_MAX.
  *
  ****************************************************************************/
 
@@ -129,9 +129,6 @@ ssize_t readv(int fildes, FAR const struct iovec *iov, int 
iovcnt);
  *   the array pointed to by iov are 0, writev() will return 0 and have no
  *   other effect. For other file types, the behavior is unspecified.
  *
- *   TODO: If the sum of the iov_len values is greater than SSIZE_MAX, the
- *   operation will fail and no data will be transferred.
- *
  * Input Parameters:
  *   filedes - The open file descriptor for the file to be read
  *   iov     - Array of read buffer descriptors
@@ -142,12 +139,12 @@ ssize_t readv(int fildes, FAR const struct iovec *iov, 
int iovcnt);
  *   actually written. Otherwise, it shall return a value of -1, the file-
  *   pointer shall remain unchanged, and errno shall be set to indicate an
  *   error. See write for the list of returned errno values. In addition,
- *   the readv() function will fail if:
+ *   the writev() function will fail if:
  *
  *    EINVAL.
  *      The sum of the iov_len values in the iov array overflowed an ssize_t
  *      or The 'iovcnt' argument was less than or equal to 0, or greater than
- *      IOV_MAX (Not implemented).
+ *      IOV_MAX.
  *
  ****************************************************************************/
 

Reply via email to