| From 16a200f66ede3f9afa2e51d90ade017aaa18d213 Mon Sep 17 00:00:00 2001 |
| From: Anand Jain <anand.jain@oracle.com> |
| Date: Sun, 4 Jul 2021 19:14:39 +0800 |
| Subject: btrfs: check for missing device in btrfs_trim_fs |
| |
| From: Anand Jain <anand.jain@oracle.com> |
| |
| commit 16a200f66ede3f9afa2e51d90ade017aaa18d213 upstream. |
| |
| A fstrim on a degraded raid1 can trigger the following null pointer |
| dereference: |
| |
| BTRFS info (device loop0): allowing degraded mounts |
| BTRFS info (device loop0): disk space caching is enabled |
| BTRFS info (device loop0): has skinny extents |
| BTRFS warning (device loop0): devid 2 uuid 97ac16f7-e14d-4db1-95bc-3d489b424adb is missing |
| BTRFS warning (device loop0): devid 2 uuid 97ac16f7-e14d-4db1-95bc-3d489b424adb is missing |
| BTRFS info (device loop0): enabling ssd optimizations |
| BUG: kernel NULL pointer dereference, address: 0000000000000620 |
| PGD 0 P4D 0 |
| Oops: 0000 [#1] SMP NOPTI |
| CPU: 0 PID: 4574 Comm: fstrim Not tainted 5.13.0-rc7+ #31 |
| Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 |
| RIP: 0010:btrfs_trim_fs+0x199/0x4a0 [btrfs] |
| RSP: 0018:ffff959541797d28 EFLAGS: 00010293 |
| RAX: 0000000000000000 RBX: ffff946f84eca508 RCX: a7a67937adff8608 |
| RDX: ffff946e8122d000 RSI: 0000000000000000 RDI: ffffffffc02fdbf0 |
| RBP: ffff946ea4615000 R08: 0000000000000001 R09: 0000000000000000 |
| R10: 0000000000000000 R11: ffff946e8122d960 R12: 0000000000000000 |
| R13: ffff959541797db8 R14: ffff946e8122d000 R15: ffff959541797db8 |
| FS: 00007f55917a5080(0000) GS:ffff946f9bc00000(0000) knlGS:0000000000000000 |
| CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 |
| CR2: 0000000000000620 CR3: 000000002d2c8001 CR4: 00000000000706f0 |
| Call Trace: |
| btrfs_ioctl_fitrim+0x167/0x260 [btrfs] |
| btrfs_ioctl+0x1c00/0x2fe0 [btrfs] |
| ? selinux_file_ioctl+0x140/0x240 |
| ? syscall_trace_enter.constprop.0+0x188/0x240 |
| ? __x64_sys_ioctl+0x83/0xb0 |
| __x64_sys_ioctl+0x83/0xb0 |
| |
| Reproducer: |
| |
| $ mkfs.btrfs -fq -d raid1 -m raid1 /dev/loop0 /dev/loop1 |
| $ mount /dev/loop0 /btrfs |
| $ umount /btrfs |
| $ btrfs dev scan --forget |
| $ mount -o degraded /dev/loop0 /btrfs |
| |
| $ fstrim /btrfs |
| |
| The reason is we call btrfs_trim_free_extents() for the missing device, |
| which uses device->bdev (NULL for missing device) to find if the device |
| supports discard. |
| |
| Fix is to check if the device is missing before calling |
| btrfs_trim_free_extents(). |
| |
| CC: stable@vger.kernel.org # 5.4+ |
| Reviewed-by: Filipe Manana <fdmanana@suse.com> |
| Signed-off-by: Anand Jain <anand.jain@oracle.com> |
| Reviewed-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/btrfs/extent-tree.c | 3 +++ |
| 1 file changed, 3 insertions(+) |
| |
| --- a/fs/btrfs/extent-tree.c |
| +++ b/fs/btrfs/extent-tree.c |
| @@ -6034,6 +6034,9 @@ int btrfs_trim_fs(struct btrfs_fs_info * |
| mutex_lock(&fs_info->fs_devices->device_list_mutex); |
| devices = &fs_info->fs_devices->devices; |
| list_for_each_entry(device, devices, dev_list) { |
| + if (test_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state)) |
| + continue; |
| + |
| ret = btrfs_trim_free_extents(device, &group_trimmed); |
| if (ret) { |
| dev_failed++; |