mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Stefan Kerkmann <s.kerkmann@pengutronix.de>
To: Sascha Hauer <s.hauer@pengutronix.de>,
	 "open list:BAREBOX" <barebox@lists.infradead.org>
Cc: Stefan Kerkmann <s.kerkmann@pengutronix.de>
Subject: [PATCH 2/2] fs: __write: fix EOF growth checks for negative file sizes
Date: Thu, 03 Sep 2026 18:29:14 +0200	[thread overview]
Message-ID: <20260903-fix-fs_read_write_eof_checks-v1-2-7afd44db1793@pengutronix.de> (raw)
In-Reply-To: <20260903-fix-fs_read_write_eof_checks-v1-0-7afd44db1793@pengutronix.de>

__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




      parent reply	other threads:[~2026-09-03 16:31 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-03 16:29 [PATCH 0/2] fs: __read/__write: fix EOF " 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 ` Stefan Kerkmann [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260903-fix-fs_read_write_eof_checks-v1-2-7afd44db1793@pengutronix.de \
    --to=s.kerkmann@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    --cc=s.hauer@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox