mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 0/2] fs: __read/__write: fix EOF checks for negative file sizes
@ 2026-09-03 16:29 Stefan Kerkmann
  2026-09-03 16:29 ` [PATCH 1/2] fs: __read: fix EOF count clamping " Stefan Kerkmann
  2026-09-03 16:29 ` [PATCH 2/2] fs: __write: fix EOF growth checks " Stefan Kerkmann
  0 siblings, 2 replies; 4+ messages in thread
From: Stefan Kerkmann @ 2026-09-03 16:29 UTC (permalink / raw)
  To: Sascha Hauer, open list:BAREBOX; +Cc: Stefan Kerkmann

Signed-off-by: Stefan Kerkmann <s.kerkmann@pengutronix.de>
---
Stefan Kerkmann (2):
      fs: __read: fix EOF count clamping for negative file sizes
      fs: __write: fix EOF growth checks for negative file sizes

 fs/fs.c | 54 +++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 41 insertions(+), 13 deletions(-)
---
base-commit: a59cb95e1133c7f4844c68398c570694d1c7d9ed
change-id: 20260903-fix-fs_read_write_eof_checks-3393cd6d42bd

Best regards,
--  
Stefan Kerkmann <s.kerkmann@pengutronix.de>




^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/2] fs: __read: fix EOF count clamping for negative file sizes
  2026-09-03 16:29 [PATCH 0/2] fs: __read/__write: fix EOF checks for negative file sizes Stefan Kerkmann
@ 2026-09-03 16:29 ` Stefan Kerkmann
  2026-09-04  9:20   ` Sascha Hauer
  2026-09-03 16:29 ` [PATCH 2/2] fs: __write: fix EOF growth checks " Stefan Kerkmann
  1 sibling, 1 reply; 4+ messages in thread
From: Stefan Kerkmann @ 2026-09-03 16:29 UTC (permalink / raw)
  To: Sascha Hauer, open list:BAREBOX; +Cc: Stefan Kerkmann

__read() clamps count to the bytes remaining until EOF, but compared
the signed 64-bit f->f_pos/f->f_size (loff_t) against count (size_t),
which is 32-bit on 32-bit arches and 64-bit on 64-bit arches.

This made the comparison type targent dependent, breaking it for
negative file sizes except for the FILE_SIZE_STREAM sentinel.

- On 32-bit arches count is converted to the signed 64-bit type of
  f->f_pos, so for e.g. f->f_size = -512 the comparison f->f_pos + count
  > f->f_size evaluated true. The clamp then assigned the negative
  difference of f->f_size - f->f_pos to the unsigned count, wrapping it
  to a value near 2^32 and turning a small read into a huge out of
  bounds read request.

- On 64-bit arches size_t cannot be represented by signed 64-bit, so the
  arithmetic C conversions turned the whole comparison unsigned:
  f->f_size = -512 was reinterpreted as a value near 2^64, the
  comparison stayed false and the clamp never ran, leaving count
  unclamped and the bogus size undetected.

__read() now rejects negative file sizes (except for the
FILE_SIZE_STREAM sentinel) with -EINVAL. Reads at or past the end of the
file (reachable via pread() with an offset beyond EOF) now return 0.
Remaining reads are clamped to the bytes left until EOF.

Signed-off-by: Stefan Kerkmann <s.kerkmann@pengutronix.de>
---
 fs/fs.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/fs/fs.c b/fs/fs.c
index dc6c30802d..a8f2b78294 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -427,8 +427,16 @@ static ssize_t __read(struct file *f, void *buf, size_t count)
 	if (fsdrv != ramfs_driver)
 		assert_command_context();
 
-	if (f->f_size != FILE_SIZE_STREAM && f->f_pos + count > f->f_size)
-		count = f->f_size - f->f_pos;
+	if (f->f_size != FILE_SIZE_STREAM) {
+		if (f->f_size < 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+		if (f->f_pos > f->f_size)
+			count = 0;
+		else
+			count = min_t(u64, (u64)f->f_size - (u64)f->f_pos, count);
+	}
 
 	if (!count)
 		return 0;

-- 
2.47.3




^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/2] fs: __write: fix EOF growth checks for negative file sizes
  2026-09-03 16:29 [PATCH 0/2] fs: __read/__write: fix EOF checks for negative file sizes Stefan Kerkmann
  2026-09-03 16:29 ` [PATCH 1/2] fs: __read: fix EOF count clamping " Stefan Kerkmann
@ 2026-09-03 16:29 ` Stefan Kerkmann
  1 sibling, 0 replies; 4+ messages in thread
From: Stefan Kerkmann @ 2026-09-03 16:29 UTC (permalink / raw)
  To: Sascha Hauer, open list:BAREBOX; +Cc: Stefan Kerkmann

__write() grows the file when the write end would pass EOF, but based
the decision on the same target dependent mixed sign comparison that
__read() had: f->f_pos + count > f->f_size compares the signed 64-bit
position and size (loff_t) against count (size_t), which is 32-bit on
32-bit arches and 64-bit on 64-bit arches. For negative file sizes
except for the FILE_SIZE_STREAM sentinel this is broken:

- On 32-bit arches count is converted to the signed 64-bit type of
  f->f_pos, so for e.g. f->f_size = -512 the comparison evaluated true.
  fsdev_truncate() was then called for the corrupted file size,
  attempting to grow the file to f->f_pos + count.

- On 64-bit arches size_t cannot be represented by signed 64-bit, so the
  usual arithmetic conversions turned the whole comparison unsigned:
  f->f_size = -512 was reinterpreted as a value near 2^64, the
  comparison stayed false, the file was never grown and the write
  proceeded unclamped against the bogus size.

Additionally, when writing at a position past the end of the file
(reachable via pwrite() with a large offset) and fsdev_truncate() failed
with -ENOSPC, the fallback count f->f_size - f->f_pos was negative and
wrapped to a huge value in the unsigned count.

__write() now rejects negative file sizes with -EINVAL, like __read()
does. The write end f->f_pos + count is computed in u64, making the
growth check target independent, and extending the file beyond
MAX_LFS_FILESIZE is rejected with -EFBIG. On -ENOSPC the write is now
limited to the bytes remaining until EOF, or aborted when f->f_pos is at
or past the end of the file, instead of wrapping the negative remainder.

Signed-off-by: Stefan Kerkmann <s.kerkmann@pengutronix.de>
---
 fs/fs.c | 42 +++++++++++++++++++++++++++++++-----------
 1 file changed, 31 insertions(+), 11 deletions(-)

diff --git a/fs/fs.c b/fs/fs.c
index a8f2b78294..3803decc2a 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -482,7 +482,10 @@ EXPORT_SYMBOL(read);
 
 static ssize_t __write(struct file *f, const void *buf, size_t count)
 {
+	u64 size = (u64)f->f_size;
+	u64 pos = (u64)f->f_pos;
 	struct fs_driver *fsdrv;
+	u64 end;
 	int ret;
 
 	fsdrv = f->fsdev->driver;
@@ -495,18 +498,35 @@ static ssize_t __write(struct file *f, const void *buf, size_t count)
 	if (fsdrv != ramfs_driver)
 		assert_command_context();
 
-	if (f->f_size != FILE_SIZE_STREAM && f->f_pos + count > f->f_size) {
-		ret = fsdev_truncate(f, f->f_pos + count);
-		if (ret) {
-			if (ret == -EPERM)
-				ret = -ENOSPC;
-			if (ret != -ENOSPC)
+	if (f->f_size != FILE_SIZE_STREAM) {
+		if (f->f_size < 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		/* Writing past the end of the file requires growing it first */
+		end = pos + count;
+		if (end > size) {
+			/* New file size must be representable as loff_t */
+			if (end > (u64)MAX_LFS_FILESIZE ||
+			    (f->f_pos >= 0 && end < pos)) {
+				ret = -EFBIG;
 				goto out;
-			count = f->f_size - f->f_pos;
-			if (!count)
-				goto out;
-		} else {
-			f->f_size = f->f_pos + count;
+			}
+
+			ret = fsdev_truncate(f, end);
+			if (ret) {
+				if (ret == -EPERM)
+					ret = -ENOSPC;
+				if (ret != -ENOSPC)
+					goto out;
+				/* Truncate failed; write what fits into the file */
+				count = pos < size ? size - pos : 0;
+				if (!count)
+					goto out;
+			} else {
+				f->f_size = end;
+			}
 		}
 	}
 

-- 
2.47.3




^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/2] fs: __read: fix EOF count clamping for negative file sizes
  2026-09-03 16:29 ` [PATCH 1/2] fs: __read: fix EOF count clamping " Stefan Kerkmann
@ 2026-09-04  9:20   ` Sascha Hauer
  0 siblings, 0 replies; 4+ messages in thread
From: Sascha Hauer @ 2026-09-04  9:20 UTC (permalink / raw)
  To: Stefan Kerkmann; +Cc: open list:BAREBOX, Stefan Kerkmann

Hi Stefan,

On 2026-09-03 18:29, Stefan Kerkmann wrote:
> __read() clamps count to the bytes remaining until EOF, but compared
> the signed 64-bit f->f_pos/f->f_size (loff_t) against count (size_t),
> which is 32-bit on 32-bit arches and 64-bit on 64-bit arches.
> 
> This made the comparison type targent dependent, breaking it for
> negative file sizes except for the FILE_SIZE_STREAM sentinel.
> 
> - On 32-bit arches count is converted to the signed 64-bit type of
>   f->f_pos, so for e.g. f->f_size = -512 the comparison f->f_pos + count
>   > f->f_size evaluated true. The clamp then assigned the negative
>   difference of f->f_size - f->f_pos to the unsigned count, wrapping it
>   to a value near 2^32 and turning a small read into a huge out of
>   bounds read request.
> 
> - On 64-bit arches size_t cannot be represented by signed 64-bit, so the
>   arithmetic C conversions turned the whole comparison unsigned:
>   f->f_size = -512 was reinterpreted as a value near 2^64, the
>   comparison stayed false and the clamp never ran, leaving count
>   unclamped and the bogus size undetected.
> 
> __read() now rejects negative file sizes (except for the
> FILE_SIZE_STREAM sentinel) with -EINVAL. Reads at or past the end of the
> file (reachable via pread() with an offset beyond EOF) now return 0.
> Remaining reads are clamped to the bytes left until EOF.
> 
> Signed-off-by: Stefan Kerkmann <s.kerkmann@pengutronix.de>
> ---
>  fs/fs.c | 12 ++++++++++--
>  1 file changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/fs.c b/fs/fs.c
> index dc6c30802d..a8f2b78294 100644
> --- a/fs/fs.c
> +++ b/fs/fs.c
> @@ -427,8 +427,16 @@ static ssize_t __read(struct file *f, void *buf, size_t count)
>  	if (fsdrv != ramfs_driver)
>  		assert_command_context();
>  
> -	if (f->f_size != FILE_SIZE_STREAM && f->f_pos + count > f->f_size)
> -		count = f->f_size - f->f_pos;
> +	if (f->f_size != FILE_SIZE_STREAM) {
> +		if (f->f_size < 0) {
> +			ret = -EINVAL;
> +			goto out;
> +		}

I think we should start by rejecting negative file sizes at open time as
done in the patch I just sent. This is likely not the full solution to
the problem as f_pos + count could still become negative, but we
shouldn't allow to even open a file with negative file size.

Sascha

--
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-04  9:21 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-03 16:29 [PATCH 0/2] fs: __read/__write: fix EOF checks for negative file sizes Stefan Kerkmann
2026-09-03 16:29 ` [PATCH 1/2] fs: __read: fix EOF count clamping " Stefan Kerkmann
2026-09-04  9:20   ` Sascha Hauer
2026-09-03 16:29 ` [PATCH 2/2] fs: __write: fix EOF growth checks " Stefan Kerkmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox