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
prev 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