sst-linux/fs/btrfs
Filipe Manana 791d0082c1 btrfs: fix hole expansion when writing at an offset beyond EOF
commit da2dccd7451de62b175fb8f0808d644959e964c7 upstream.

At btrfs_write_check() if our file's i_size is not sector size aligned and
we have a write that starts at an offset larger than the i_size that falls
within the same page of the i_size, then we end up not zeroing the file
range [i_size, write_offset).

The code is this:

    start_pos = round_down(pos, fs_info->sectorsize);
    oldsize = i_size_read(inode);
    if (start_pos > oldsize) {
        /* Expand hole size to cover write data, preventing empty gap */
        loff_t end_pos = round_up(pos + count, fs_info->sectorsize);

        ret = btrfs_cont_expand(BTRFS_I(inode), oldsize, end_pos);
        if (ret)
            return ret;
    }

So if our file's i_size is 90269 bytes and a write at offset 90365 bytes
comes in, we get 'start_pos' set to 90112 bytes, which is less than the
i_size and therefore we don't zero out the range [90269, 90365) by
calling btrfs_cont_expand().

This is an old bug introduced in commit 9036c10208 ("Btrfs: update hole
handling v2"), from 2008, and the buggy code got moved around over the
years.

Fix this by discarding 'start_pos' and comparing against the write offset
('pos') without any alignment.

This bug was recently exposed by test case generic/363 which tests this
scenario by polluting ranges beyond EOF with an mmap write and than verify
that after a file increases we get zeroes for the range which is supposed
to be a hole and not what we wrote with the previous mmaped write.

We're only seeing this exposed now because generic/363 used to run only
on xfs until last Sunday's fstests update.

The test was failing like this:

   $ ./check generic/363
   FSTYP         -- btrfs
   PLATFORM      -- Linux/x86_64 debian0 6.13.0-rc7-btrfs-next-185+ #17 SMP PREEMPT_DYNAMIC Mon Feb  3 12:28:46 WET 2025
   MKFS_OPTIONS  -- /dev/sdc
   MOUNT_OPTIONS -- /dev/sdc /home/fdmanana/btrfs-tests/scratch_1

   generic/363 0s ... [failed, exit status 1]- output mismatch (see /home/fdmanana/git/hub/xfstests/results//generic/363.out.bad)
#      --- tests/generic/363.out	2025-02-05 15:31:14.013646509 +0000
#      +++ /home/fdmanana/git/hub/xfstests/results//generic/363.out.bad	2025-02-05 17:25:33.112630781 +0000
       @@ -1 +1,46 @@
        QA output created by 363
       +READ BAD DATA: offset = 0xdcad, size = 0xd921, fname = /home/fdmanana/btrfs-tests/dev/junk
       +OFFSET      GOOD    BAD     RANGE
       +0x1609d     0x0000  0x3104  0x0
       +operation# (mod 256) for the bad data may be 4
       +0x1609e     0x0000  0x0472  0x1
       +operation# (mod 256) for the bad data may be 4
       ...
       (Run 'diff -u /home/fdmanana/git/hub/xfstests/tests/generic/363.out /home/fdmanana/git/hub/xfstests/results//generic/363.out.bad'  to see the entire diff)
   Ran: generic/363
   Failures: generic/363
   Failed 1 of 1 tests

Fixes: 9036c10208 ("Btrfs: update hole handling v2")
CC: stable@vger.kernel.org
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2025-02-21 13:50:06 +01:00
..
tests btrfs: tests: allocate dummy fs_info and root in test_find_delalloc() 2024-08-29 17:30:37 +02:00
acl.c
async-thread.c
async-thread.h
backref.c
backref.h
block-group.c btrfs: zoned: fix zone unusable accounting for freed reserved extent 2024-11-01 01:56:06 +01:00
block-group.h
block-rsv.c btrfs: calculate the right space for delayed refs when updating global reserve 2024-09-30 16:23:55 +02:00
block-rsv.h btrfs: calculate the right space for delayed refs when updating global reserve 2024-09-30 16:23:55 +02:00
btrfs_inode.h
check-integrity.c
check-integrity.h
compression.c btrfs: fix extent map use-after-free when adding pages to compressed bio 2024-09-04 13:25:00 +02:00
compression.h
ctree.c btrfs: fix use-after-free when COWing tree bock and tracing is enabled 2025-01-09 13:30:03 +01:00
ctree.h btrfs: rename and export __btrfs_cow_block() 2025-01-09 13:30:03 +01:00
delalloc-space.c
delalloc-space.h
delayed-inode.c btrfs: change BUG_ON to assertion when checking for delayed_node root 2024-08-29 17:30:37 +02:00
delayed-inode.h
delayed-ref.c btrfs: reinitialize delayed ref list after deleting it from the list 2024-11-14 13:15:17 +01:00
delayed-ref.h btrfs: calculate the right space for delayed refs when updating global reserve 2024-09-30 16:23:55 +02:00
dev-replace.c
dev-replace.h
dir-item.c btrfs: fix passing 0 to ERR_PTR in btrfs_search_dir_index_item() 2024-11-01 01:56:06 +01:00
discard.c
discard.h
disk-io.c btrfs: flush delalloc workers queue before stopping cleaner kthread during unmount 2025-01-09 13:30:03 +01:00
disk-io.h
export.c
export.h
extent_io.c btrfs: replace sb::s_blocksize by fs_info::sectorsize 2024-08-29 17:30:42 +02:00
extent_io.h
extent_map.c
extent_map.h
extent-io-tree.c
extent-io-tree.h
extent-tree.c btrfs: don't BUG_ON on ENOMEM from btrfs_lookup_extent_info() in walk_down_proc() 2024-12-14 19:54:17 +01:00
file-item.c
file.c btrfs: fix hole expansion when writing at an offset beyond EOF 2025-02-21 13:50:06 +01:00
free-space-cache.c btrfs: zoned: properly take lock to read/update block group's zoned variables 2024-08-29 17:30:15 +02:00
free-space-cache.h
free-space-tree.c
free-space-tree.h
inode-item.c
inode-item.h
inode.c btrfs: avoid monopolizing a core when activating a swap file 2025-02-21 13:49:55 +01:00
ioctl.c btrfs: qgroup: fix qgroup prealloc rsv leak in subvolume operations 2024-12-14 19:53:56 +01:00
Kconfig
locking.c
locking.h
lzo.c
Makefile
misc.h
ordered-data.c
ordered-data.h
orphan.c
print-tree.c btrfs: avoid using fixed char array size for tree names 2024-08-14 13:52:59 +02:00
print-tree.h
props.c
props.h
qgroup.c btrfs: run delayed iputs when flushing delalloc 2024-09-04 13:24:55 +02:00
qgroup.h
raid56.c
raid56.h
rcu-string.h
ref-verify.c btrfs: ref-verify: fix use-after-free after invalid ref action 2024-12-14 19:54:10 +01:00
ref-verify.h
reflink.c btrfs: replace sb::s_blocksize by fs_info::sectorsize 2024-08-29 17:30:42 +02:00
reflink.h
relocation.c btrfs: convert BUG_ON in btrfs_reloc_cow_block() to proper error handling 2025-02-21 13:49:29 +01:00
root-tree.c btrfs: qgroup: fix qgroup prealloc rsv leak in subvolume operations 2024-12-14 19:53:56 +01:00
scrub.c
send.c btrfs: send: fix invalid clone operation for file that got its size decreased 2024-10-17 15:22:02 +02:00
send.h
space-info.c
space-info.h
struct-funcs.c
subpage.c
subpage.h
super.c btrfs: output the reason for open_ctree() failure 2025-02-21 13:49:28 +01:00
sysfs.c btrfs: sysfs: fix direct super block member reads 2025-01-02 10:30:55 +01:00
sysfs.h
transaction.c btrfs: fix use-after-free when attempting to join an aborted transaction 2025-02-21 13:49:29 +01:00
transaction.h btrfs: fix race between direct IO write and fsync when using same fd 2024-09-12 11:10:29 +02:00
tree-checker.c btrfs: tree-checker: reject inline extent items with 0 ref count 2024-12-27 13:52:59 +01:00
tree-checker.h
tree-defrag.c
tree-log.c btrfs: fix uninitialized pointer free on read_alloc_one_name() error 2024-10-22 15:56:39 +02:00
tree-log.h
tree-mod-log.c
tree-mod-log.h
ulist.c
ulist.h
uuid-tree.c
verity.c
volumes.c btrfs: do not clear read-only when adding sprout device 2024-12-14 19:54:37 +01:00
volumes.h
xattr.c
xattr.h
zlib.c
zoned.c btrfs: zoned: fix missing RCU locking in error message when loading zone info 2024-10-17 15:22:22 +02:00
zoned.h
zstd.c