<div dir="ltr"><div>Applied to bionic and focal linux master-next</div><div><br></div><div>Thanks!</div><div>- Luke<br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Jan 29, 2023 at 6:12 PM Matthew Ruffell <<a href="mailto:matthew.ruffell@canonical.com">matthew.ruffell@canonical.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">BugLink: <a href="https://bugs.launchpad.net/bugs/2004132" rel="noreferrer" target="_blank">https://bugs.launchpad.net/bugs/2004132</a><br>
<br>
[Impact]<br>
<br>
xfstests btrfs/154 fails on both Bionic and Focal, leading to a kernel oops and<br>
the btrfs volume being forced readonly.<br>
<br>
In btrfs, item key collision is allowed for some item types, namely dir item and<br>
inode references. When inserting items into the btree, there are two objects,<br>
the btrfs_item and the item data. These objects must fit within the btree<br>
nodesize.<br>
<br>
When a hash collision occurs, and we call btrfs_search_slot() to place the<br>
objects in the tree, when btrfs_search_slot() reaches the leaf node, a check is<br>
performed to see if we need to split the leaf. The check is incorrect, returning<br>
that we need to split the leaf, since it thinks that both btrfs_item and the<br>
item data need to be inserted, when in reality, the item can be merged with the<br>
existing one and no new btrfs_item will be inserted.<br>
<br>
split_leaf() will return EOVERFLOW from following code:<br>
<br>
  if (extend && data_size + btrfs_item_size_nr(l, slot) +<br>
      sizeof(struct btrfs_item) > BTRFS_LEAF_DATA_SIZE(fs_info))<br>
      return -EOVERFLOW;<br>
<br>
In the rename case, btrfs_check_dir_item_collision() is called early stages of<br>
treewalking, and correctly calculates the needed size, taking into account that<br>
a hash collision has occurred.<br>
<br>
  data_size = sizeof(*di) + name_len;<br>
      if (data_size + btrfs_item_size_nr(leaf, slot) +<br>
          sizeof(struct btrfs_item) > BTRFS_LEAF_DATA_SIZE(root->fs_info))<br>
<br>
The two sizes reported from btrfs_check_dir_item_collision() and<br>
btrfs_search_slot() are different, and rename fails due to split_leaf()<br>
returning -EOVERFLOW, leading to transaction abort and forcing the volume<br>
readonly.<br>
<br>
Kernel oops:<br>
<br>
BTRFS: Transaction aborted (error -75)<br>
WARNING: CPU: 0 PID: 2921 at /build/linux-fTmV3T/linux-4.15.0/fs/btrfs/inode.c:10217 btrfs_rename+0xcf1/0xdf0 [btrfs]<br>
CPU: 0 PID: 2921 Comm: python3 Not tainted 4.15.0-202-generic #213-Ubuntu<br>
RIP: 0010:btrfs_rename+0xcf1/0xdf0 [btrfs]<br>
RSP: 0018:ffff9e6f4183fd20 EFLAGS: 00010282<br>
RAX: 0000000000000000 RBX: ffff91a493f27b98 RCX: 0000000000000006<br>
RDX: 0000000000000007 RSI: 0000000000000096 RDI: ffff91a4bfc1b4d0<br>
RBP: ffff9e6f4183fdc0 R08: 00000000000002b4 R09: 0000000000000004<br>
R10: 0000000000000000 R11: 0000000000000001 R12: 0000000000000236<br>
R13: ffff91a493f56518 R14: ffff91a4b6b57b40 R15: ffff91a493f27b98<br>
FS:  00007f6041081740(0000) GS:ffff91a4bfc00000(0000) knlGS:0000000000000000<br>
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033<br>
CR2: 00007f6040fe84c8 CR3: 000000015c8ca005 CR4: 0000000000760ef0<br>
PKRU: 55555554<br>
Call Trace:<br>
 btrfs_rename2+0x1d/0x30 [btrfs]<br>
 vfs_rename+0x46e/0x960<br>
 SyS_rename+0x362/0x3c0<br>
 do_syscall_64+0x73/0x130<br>
 entry_SYSCALL_64_after_hwframe+0x41/0xa6<br>
Code: 0f ba a8 d0 cd 00 00 02 72 2b 41 83 f8 fb 0f 84 d9 00 00 00 44 89 c6 48 c7 c7 68 43 4b c0 44 89 55 80 44 89 45 98 e8 8f 5c a6 d0 <0f> 0b 44 8b 45 98 44 8b 55 80 44 89 55 80 44 89 c1 44 89 45 98 <br>
---[ end trace 9c6b87a19f4436f3 ]---<br>
BTRFS: error (device vdd) in btrfs_rename:10217: errno=-75 unknown<br>
BTRFS info (device vdd): forced readonly<br>
<br>
[Testcase]<br>
<br>
Start a fresh Bionic or Focal VM. <br>
<br>
Attach two scratch disks, I used standard virtio disks with 3gb of storage each.<br>
These disks are /dev/vdc and /dev/vdd.<br>
<br>
Compile xfstests:<br>
<br>
$ sudo apt-get install acl attr automake bc dbench dump e2fsprogs fio gawk \<br>
        gcc git indent libacl1-dev libaio-dev libcap-dev libgdbm-dev libtool \<br>
        libtool-bin  libuuid1 lvm2 make psmisc python3 quota sed \<br>
        uuid-dev uuid-runtime xfsprogs linux-headers-$(uname -r) sqlite3 make<br>
$ sudo apt-get install  f2fs-tools ocfs2-tools udftools xfsdump \<br>
        xfslibs-dev<br>
$ git clone git://<a href="http://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git" rel="noreferrer" target="_blank">git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git</a><br>
$ cd xfstests-dev<br>
$ make<br>
$ sudo su<br>
# mkdir /test<br>
# mkdir /scratch<br>
# mkfs.btrfs -f /dev/vdc<br>
# cat << EOF >> ./local.config<br>
export TEST_DEV=/dev/vdc<br>
export TEST_DIR=/test<br>
export SCRATCH_DEV=/dev/vdd<br>
export SCRATCH_MNT=/scratch<br>
EOF<br>
<br>
# ./check btrfs/154<br>
<br>
btrfs/154       _check_dmesg: something found in dmesg (see /home/ubuntu/xfstests-dev/results//btrfs/154.dmesg)<br>
- output mismatch (see /home/ubuntu/xfstests-dev/results//btrfs/154.out.bad)<br>
    --- tests/btrfs/154.out     2023-01-28 02:53:03.566450703 +0000<br>
    +++ /home/ubuntu/xfstests-dev/results//btrfs/154.out.bad    2023-01-28 06:51:34.113121042 +0000<br>
    @@ -1,2 +1,6 @@<br>
     QA output created by 154<br>
    +Traceback (most recent call last):<br>
    +  File "/home/ubuntu/xfstests-dev/src/btrfs_crc32c_forged_name.py", line 99, in <module><br>
    +    os.rename(srcpath, dstpath)<br>
    +OSError: [Errno 75] Value too large for defined data type: '/scratch/309' -> b'/scratch/fa5d\x90O\x97015d7a47c48fdeb99b857c17e8038da6382fcb05e3a6b367589a8f54a8c3c1327584dfa630b4bd8c5bbb37b4decc2b82fecb4c940e0bd0989166c44e6af2855e9e42a02a57cffa2fc5283525ac53b2b0d2baaf874ff50b'<br>
Ran: btrfs/154<br>
Failures: btrfs/154<br>
Failed 1 of 1 tests<br>
<br>
If you examine dmesg, you will see the oops from the impact section.<br>
<br>
If you install the test kernel from the below ppa:<br>
<br>
<a href="https://launchpad.net/~mruffell/+archive/ubuntu/sf349467-btrfs-154" rel="noreferrer" target="_blank">https://launchpad.net/~mruffell/+archive/ubuntu/sf349467-btrfs-154</a><br>
<br>
The issue no longer occurs:<br>
<br>
# ./check btrfs/154<br>
<br>
Ran: btrfs/154<br>
Passed all 1 tests<br>
<br>
[Fix]<br>
<br>
This was fixed in 5.11-rc3 by the below commit:<br>
<br>
commit 9a664971569daf68254928149f580b4f5856d274<br>
Author: ethanwu <<a href="mailto:ethanwu@synology.com" target="_blank">ethanwu@synology.com</a>><br>
Date:   Tue Dec 1 17:25:12 2020 +0800<br>
Subject: btrfs: correctly calculate item size used when item key collision happens<br>
Link: <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9a664971569daf68254928149f580b4f5856d274" rel="noreferrer" target="_blank">https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9a664971569daf68254928149f580b4f5856d274</a><br>
<br>
This cherry-picks to Focal, and required a small backport to Bionic, removing<br>
the hunk that contained comments explaining the parameters to<br>
btrfs_search_slot().<br>
<br>
[Where problems could occur]<br>
<br>
Problems could occur when calculating the size required for btrfs_item and item<br>
data when hash collisions occur. Such collisions are rare in of itself, but<br>
possible if you have a large amount files or craft filenames to force collisions<br>
with the crc32 hash algorithm.<br>
<br>
If a regression were to occur, it could cause more transactions to be aborted,<br>
and would likely result in the users volume being forced read only. They might<br>
not lose any existing data, but data being written might be lost.<br>
<br>
ethanwu (1):<br>
  btrfs: correctly calculate item size used when item key collision<br>
    happens<br>
<br>
 fs/btrfs/ctree.c       | 24 ++++++++++++++++++++++--<br>
 fs/btrfs/ctree.h       |  6 ++++++<br>
 fs/btrfs/extent-tree.c |  2 ++<br>
 fs/btrfs/file-item.c   |  2 ++<br>
 4 files changed, 32 insertions(+), 2 deletions(-)<br>
<br>
-- <br>
2.37.2<br>
<br>
<br>
-- <br>
kernel-team mailing list<br>
<a href="mailto:kernel-team@lists.ubuntu.com" target="_blank">kernel-team@lists.ubuntu.com</a><br>
<a href="https://lists.ubuntu.com/mailman/listinfo/kernel-team" rel="noreferrer" target="_blank">https://lists.ubuntu.com/mailman/listinfo/kernel-team</a><br>
</blockquote></div></div>