On Mon, Dec 19, 2022 at 8:28 PM Boris Burkov <boris@xxxxxx> wrote: > > If we create a file that has an inline extent followed by a prealloc > extent, then attempt to use the logical to inode ioctl on the prealloc > extent, but in the overwritten range, backref resolution will process > the inline extent. Depending on the leaf eb layout, this can panic. > Add a new test for this condition. In the long run, we can add spew when > we read out-of-bounds fields of inline extent items and simplify this > test to look for dmesg warnings rather than trying to force a fairly > fragile panic (dependent on non-standardized details of leaf layout). > > The test causes a kernel panic unless: > btrfs: fix logical_ino ioctl panic > is applied to the kernel. > > Signed-off-by: Boris Burkov <boris@xxxxxx> Reviewed-by: Filipe Manana <fdmanana@xxxxxxxx> Looks good, thanks. > --- > Changes for V2: > - move to btrfs/299 > - change to 64k extent buffers > - improve comments > - cut down on unneeded fsyncs > - various cleanups to requires/includes > > tests/btrfs/299 | 103 ++++++++++++++++++++++++++++++++++++++++++++ > tests/btrfs/299.out | 2 + > 2 files changed, 105 insertions(+) > create mode 100755 tests/btrfs/299 > create mode 100644 tests/btrfs/299.out > > diff --git a/tests/btrfs/299 b/tests/btrfs/299 > new file mode 100755 > index 00000000..42a08317 > --- /dev/null > +++ b/tests/btrfs/299 > @@ -0,0 +1,103 @@ > +#! /bin/bash > +# SPDX-License-Identifier: GPL-2.0 > +# Copyright (c) 2022 Meta Platforms, Inc. All Rights Reserved. > +# > +# FS QA Test 299 > +# > +# Given a file with extents: > +# [0 : 4096) (inline) > +# [4096 : N] (prealloc) > +# if a user uses the ioctl BTRFS_IOC_LOGICAL_INO[_V2] asking for the file of the > +# non-inline extent, it results in reading the offset field of the inline > +# extent, which is meaningless (it is full of user data..). If we are > +# particularly lucky, it can be past the end of the extent buffer, resulting in > +# a crash. This test creates that circumstance and asserts that logical inode > +# resolution is still successful. > +# > +. ./common/preamble > +_begin_fstest auto quick preallocrw > + > +# real QA test starts here > +# Modify as appropriate. > +_supported_fs btrfs > +_require_scratch > +_require_xfs_io_command "falloc" "-k" > +_require_btrfs_command inspect-internal dump-tree > +_require_btrfs_command inspect-internal logical-resolve > +_fixed_by_kernel_commit xxxxxxxx "btrfs: fix logical_ino ioctl panic" > + > +dump_tree() { > + $BTRFS_UTIL_PROG inspect-internal dump-tree $SCRATCH_DEV > +} > + > +get_extent_data() { > + local ino=$1 > + dump_tree $SCRATCH_DEV | grep -A4 "($ino EXTENT_DATA " > +} > + > +get_prealloc_offset() { > + local ino=$1 > + get_extent_data $ino | grep "disk byte" | $AWK_PROG '{print $5}' > +} > + > +# This test needs to create conditions s.t. the special inode's inline extent > +# is the first item in a leaf. Therefore, fix a leaf size and add > +# items that are otherwise not necessary to reproduce the inline-prealloc > +# condition to get to such a state. > +# > +# Roughly, the idea for getting the right item fill is to: > +# 1. create extra inline items to cause leaf splitting. > +# 2. put the target item in the middle so it is likely to catch the split > +# 3. add an extra variable inline item to tweak any final adjustments > +# > +# It took a bit of trial and error to hit working counts of inline items, since > +# it also had to account for dir and index items all going to the front. > + > +# use a 64k nodesize so that an fs with 64k pages and no subpage sector size > +# support will correctly reproduce the problem. > +_scratch_mkfs "--nodesize 64k" >> $seqres.full || _fail "mkfs failed" > +_scratch_mount > + > +f=$SCRATCH_MNT/f > +# write extra files before the evil file to use up the leaf and > +# help trick leaf balancing > +for i in {1..41}; do > + $XFS_IO_PROG -fc "pwrite -q 0 1024" $f.inl.$i > +done > + > +# write a variable inline file to help pad the preceeding leaf > +$XFS_IO_PROG -fc "pwrite -q 0 1" $f.inl-var.$i > + > +# falloc the evil file whose inline extent will start a leaf > +$XFS_IO_PROG -fc "falloc -k 0 1m" $f.evil > +$XFS_IO_PROG -fc fsync $f.evil > + > +# write extra files after the evil file to use up the leaf and > +# help trick leaf balancing > +for i in {1..42}; do > + $XFS_IO_PROG -fc "pwrite -q 0 1024" $f.inl.2.$i > +done > + > +# grab the prealloc offset from dump tree while it's still the only > +# extent data item for the inode > +ino=$(stat -c '%i' $f.evil) > +logical=$(get_prealloc_offset $ino) > + > +# do the "small write; fsync; small write" pattern which reproduces the desired > +# item pattern of an inline extent followed by a preallocated extent. The 23 > +# size is somewhat arbitrary, but ensures that the offset field is past the eb > +# when we are item 0 (borrowed from the actual crash this reproduces). > +$XFS_IO_PROG -fc "pwrite -q 0 23" $f.evil > +$XFS_IO_PROG -fc fsync $f.evil > +$XFS_IO_PROG -fc "pwrite -q 0 23" $f.evil > + > +# ensure we have all the extent_data items for when we do logical to inode > +# resolution > +sync > + > +# trigger the backref walk which accesses the bad inline extent > +btrfs inspect-internal logical-resolve $logical $SCRATCH_MNT > + > +echo "Silence is golden" > +status=0 > +exit > diff --git a/tests/btrfs/299.out b/tests/btrfs/299.out > new file mode 100644 > index 00000000..0fcc0304 > --- /dev/null > +++ b/tests/btrfs/299.out > @@ -0,0 +1,2 @@ > +QA output created by 299 > +Silence is golden > -- > 2.38.1 >