]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
iomap: guard io_size EOF trim against concurrent truncate underflow
authorMorduan Zang <zhangdandan@uniontech.com>
Wed, 24 Jun 2026 06:26:22 +0000 (14:26 +0800)
committerChristian Brauner <brauner@kernel.org>
Wed, 1 Jul 2026 13:26:27 +0000 (15:26 +0200)
iomap: fix zero padding data issue in concurrent append writes
changed ioend accounting so that io_size tracks only valid data
within EOF.  This trims io_size when a writeback range extends
past end_pos:

    ioend->io_size += map_len;
    if (ioend->io_offset + ioend->io_size > end_pos)
        ioend->io_size = end_pos - ioend->io_offset;

However, if end_pos ends up below ioend->io_offset, the subtraction
becomes negative and is stored in size_t io_size, causing an unsigned
wrap to a huge value.  This can happen when writeback continues past
byte-level EOF up to a block-aligned range, or when a concurrent
truncate shrinks the file after end_pos was sampled in
iomap_writeback_handle_eof().

A wrapped io_size can mislead append detection and corrupt
completion-time size handling, since filesystem end_io paths consume
io_size for decisions such as on-disk EOF updates and unwritten/COW
completion ranges.

Fix this by clamping io_size to zero when EOF has moved to or before
the ioend start offset.  This preserves the original intent of trimming
io_size to valid in-EOF data while avoiding the underflow.

Fixes: 51d20d1dacbe ("iomap: fix zero padding data issue in concurrent append writes")
Suggested-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Morduan Zang <zhangdandan@uniontech.com>
Link: https://patch.msgid.link/9E38E2659B47DC2A+20260624062622.337469-1-zhangdandan@uniontech.com
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
fs/iomap/ioend.c

index f7c3e0c70fd72b8110010888bf8bc55938d9ec42..0565328764c175308a0d62f9d8eb6cf3b57b7cb8 100644 (file)
@@ -298,8 +298,12 @@ new_ioend:
         * appending writes.
         */
        ioend->io_size += map_len;
-       if (ioend->io_offset + ioend->io_size > end_pos)
-               ioend->io_size = end_pos - ioend->io_offset;
+       if (ioend->io_offset + ioend->io_size > end_pos) {
+               if (ioend->io_offset >= end_pos)
+                       ioend->io_size = 0;
+               else
+                       ioend->io_size = end_pos - ioend->io_offset;
+       }
 
        wbc_account_cgroup_owner(wpc->wbc, folio, map_len);
        return map_len;