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