btrfs: fix missing lastunlinktrans update when removing a directory
btrfs: fix btrfsioctlspaceinfo() slotcount TOCTOU which can lead to info-leak
In the Linux kernel, the following vulnerability has been resolved:
btrfs: fix EEXIST abort due to non-consecutive gaps in chunk allocation
I have been observing a number of systems aborting at insertdevextents() in btrfscreatependingblockgroups(). The following is a sample stack trace of such an abort coming from forced chunk allocation (typically behind CONFIGBTRFSEXPERIMENTAL) but this can theoretically happen to any DUP chunk allocation.
[81.801] ------------[ cut here ]------------ [81.801] BTRFS: Transaction aborted (error -17) [81.801] WARNING: fs/btrfs/block-group.c:2876 at btrfscreatependingblockgroups+0x721/0x770 [btrfs], CPU#1: bash/319 [81.802] Modules linked in: virtionet btrfs xor zstdcompress raid6pq nullblk [81.803] CPU: 1 UID: 0 PID: 319 Comm: bash Kdump: loaded Not tainted 6.19.0-rc6+ #319 NONE [81.803] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Arch Linux 1.17.0-2-2 04/01/2014 [81.804] RIP: 0010:btrfscreatependingblockgroups+0x723/0x770 [btrfs] [81.806] RSP: 0018:ffffa36241a6bce8 EFLAGS: 00010282 [81.806] RAX: 000000000000000d RBX: ffff8e699921e400 RCX: 0000000000000000 [81.807] RDX: 0000000002040001 RSI: 00000000ffffffef RDI: ffffffffc0608bf0 [81.807] RBP: 00000000ffffffef R08: ffff8e69830f6000 R09: 0000000000000007 [81.808] R10: ffff8e699921e5e8 R11: 0000000000000000 R12: ffff8e6999228000 [81.808] R13: ffff8e6984d82000 R14: ffff8e69966a69c0 R15: ffff8e69aa47b000 [81.809] FS: 00007fec6bdd9740(0000) GS:ffff8e6b1b379000(0000) knlGS:0000000000000000 [81.809] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [81.810] CR2: 00005604833670f0 CR3: 0000000116679000 CR4: 00000000000006f0 [81.810] Call Trace: [81.810] <TASK> [81.810] btrfsendtransaction+0x3e/0x2b0 [btrfs] [81.811] btrfsforcechunkallocstore+0xcd/0x140 [btrfs] [81.811] kernfsfopwriteiter+0x15f/0x240 [81.812] vfswrite+0x264/0x500 [81.812] ksyswrite+0x6c/0xe0 [81.812] dosyscall64+0x66/0x770 [81.812] entrySYSCALL64afterhwframe+0x76/0x7e [81.813] RIP: 0033:0x7fec6be66197 [81.814] RSP: 002b:00007fffb159dd30 EFLAGS: 00000202 ORIGRAX: 0000000000000001 [81.815] RAX: ffffffffffffffda RBX: 00007fec6bdd9740 RCX: 00007fec6be66197 [81.815] RDX: 0000000000000002 RSI: 0000560483374f80 RDI: 0000000000000001 [81.816] RBP: 0000560483374f80 R08: 0000000000000000 R09: 0000000000000000 [81.816] R10: 0000000000000000 R11: 0000000000000202 R12: 0000000000000002 [81.817] R13: 00007fec6bfb85c0 R14: 00007fec6bfb5ee0 R15: 00005604833729c0 [81.817] </TASK> [81.817] irq event stamp: 20039 [81.818] hardirqs last enabled at (20047): [<ffffffff99a68302>] upconsolesem+0x52/0x60 [81.818] hardirqs last disabled at (20056): [<ffffffff99a682e7>] upconsolesem+0x37/0x60 [81.819] softirqs last enabled at (19470): [<ffffffff999d2b46>] irqexitrcu+0x96/0xc0 [81.819] softirqs last disabled at (19463): [<ffffffff999d2b46>] irqexitrcu+0x96/0xc0 [81.820] ---[ end trace 0000000000000000 ]--- [81.820] BTRFS: error (device dm-7 state A) in btrfscreatependingblockgroups:2876: errno=-17 Object already exists
Inspecting these aborts with drgn, I observed a pattern of overlapping chunkmaps. Note how stripe 1 of the first chunk overlaps in physical address with stripe 0 of the second chunk.
Physical Start Physical End Length Logical Type Stripe ---------------------------------------------------------------------------------------------------- 0x0000000102500000 0x0000000142500000 1.0G 0x0000000641d00000 META|DUP 0/2 0x0000000142500000 0x0000000182500000 1.0G 0x0000000641d00000 META|DUP 1/2 0x0000000142500000 0x0000000182500000 1.0G 0x0000000601d00000 META|DUP 0/2 0x0000000182500000 0x00000001c2500000 1.0G 0x0000000601d00000 META|DUP 1/2
Now how could this possibly happen? All chunk allocation is ---truncated---
In the Linux kernel, the following vulnerability has been resolved:
btrfs: reserve enough transaction items for qgroup ioctls
Currently our qgroup ioctls don't reserve any space, they just do a transaction join, which does not reserve any space, neither for the quota tree updates nor for the delayed refs generated when updating the quota tree. The quota root uses the global block reserve, which is fine most of the time since we don't expect a lot of updates to the quota root, or to be too close to -ENOSPC such that other critical metadata updates need to resort to the global reserve.
However this is not optimal, as not reserving proper space may result in a transaction abort due to not reserving space for delayed refs and then abusing the use of the global block reserve.
For example, the following reproducer (which is unlikely to model any real world use case, but just to illustrate the problem), triggers such a transaction abort due to -ENOSPC when running delayed refs:
$ cat test.sh #!/bin/bash
DEV=/dev/nullb0 MNT=/mnt/nullb0
umount $DEV &> /dev/null # Limit device to 1G so that it's much faster to reproduce the issue. mkfs.btrfs -f -b 1G $DEV mount -o commit=600 $DEV $MNT
fallocate -l 800M $MNT/filler btrfs quota enable $MNT
for ((i = 1; i <= 400000; i++)); do btrfs qgroup create 1/$i $MNT done
umount $MNT
When running this, we can see in dmesg/syslog that a transaction abort happened:
[436.490] BTRFS error (device nullb0): failed to run delayed ref for logical 30408704 numbytes 16384 type 176 action 1 refmod 1: -28 [436.493] ------------[ cut here ]------------ [436.494] BTRFS: Transaction aborted (error -28) [436.495] WARNING: fs/btrfs/extent-tree.c:2247 at btrfsrundelayedrefs+0xd9/0x110 [btrfs], CPU#4: umount/2495372 [436.497] Modules linked in: btrfs loop (...) [436.508] CPU: 4 UID: 0 PID: 2495372 Comm: umount Tainted: G W 6.19.0-rc8-btrfs-next-225+ #1 PREEMPT(full) [436.510] Tainted: [W]=WARN [436.511] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org 04/01/2014 [436.513] RIP: 0010:btrfsrundelayedrefs+0xdf/0x110 [btrfs] [436.514] Code: 0f 82 ea (...) [436.518] RSP: 0018:ffffd511850b7d78 EFLAGS: 00010292 [436.519] RAX: 00000000ffffffe4 RBX: ffff8f120dad37e0 RCX: 0000000002040001 [436.520] RDX: 0000000000000002 RSI: 00000000ffffffe4 RDI: ffffffffc090fd80 [436.522] RBP: 0000000000000000 R08: 0000000000000001 R09: ffffffffc04d1867 [436.523] R10: ffff8f18dc1fffa8 R11: 0000000000000003 R12: ffff8f173aa89400 [436.524] R13: 0000000000000000 R14: ffff8f173aa89400 R15: 0000000000000000 [436.526] FS: 00007fe59045d840(0000) GS:ffff8f192e22e000(0000) knlGS:0000000000000000 [436.527] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [436.528] CR2: 00007fe5905ff2b0 CR3: 000000060710a002 CR4: 0000000000370ef0 [436.530] Call Trace: [436.530] <TASK> [436.530] btrfscommittransaction+0x73/0xc00 [btrfs] [436.531] ? btrfsattachtransactionbarrier+0x1e/0x70 [btrfs] [436.532] syncfilesystem+0x7a/0x90 [436.533] genericshutdownsuper+0x28/0x180 [436.533] killanonsuper+0x12/0x40 [436.534] btrfskillsuper+0x12/0x20 [btrfs] [436.534] deactivatelockedsuper+0x2f/0xb0 [436.534] cleanupmnt+0xea/0x180 [436.535] taskworkrun+0x58/0xa0 [436.535] exittousermodeloop+0xed/0x480 [436.536] ? x64sysumount+0x68/0x80 [436.536] dosyscall64+0x2a5/0xf20 [436.537] entrySYSCALL64afterhwframe+0x76/0x7e [436.537] RIP: 0033:0x7fe5906b6217 [436.538] Code: 0d 00 f7 (...) [436.540] RSP: 002b:00007ffcd87a61f8 EFLAGS: 00000246 ORIGRAX: 00000000000000a6 [436.541] RAX: 0000000000000000 RBX: 00005618b9ecadc8 RCX: 00007fe5906b6217 [436.541] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00005618b9ecb100 [436.542] RBP: 0000000000000000 R08: 00007ffcd87a4fe0 R09: 00000000ffffffff [436.544] R10: 0000000000000103 R11: ---truncated---
In the Linux kernel, the following vulnerability has been resolved:
btrfs: log new dentries when logging parent dir of a conflicting inode
If we log the parent directory of a conflicting inode, we are not logging the new dentries of the directory, so when we finish we have the parent directory's inode marked as logged but we did not log its new dentries. As a consequence if the parent directory is explicitly fsynced later and it does not have any new changes since we logged it, the fsync is a no-op and after a power failure the new dentries are missing.
Example scenario:
$ mkdir foo
$ sync
$rmdir foo
$ mkdir dir1 $ mkdir dir2
# A file with the same name and parent as the directory we just deleted # and was persisted in a past transaction. So the deleted directory's # inode is a conflicting inode of this new file's inode. $ touch foo
$ ln foo dir2/link
# The fsync on dir2 will log the parent directory (".") because the # conflicting inode (deleted directory) does not exists anymore, but it # it does not log its new dentries (dir1). $ xfsio -c "fsync" dir2
# This fsync on the parent directory is no-op, since the previous fsync # logged it (but without logging its new dentries). $ xfsio -c "fsync" .
<power failure>
# After log replay dir1 is missing.
Fix this by ensuring we log new dir dentries whenever we log the parent directory of a no longer existing conflicting inode.
A test case for fstests will follow soon.