Skip to content

Commit cf89af1

Browse files
asjkdave
authored andcommitted
btrfs: dev-replace: fail mount if we don't have replace item with target device
If there is a device BTRFS_DEV_REPLACE_DEVID without the device replace item, then it means the filesystem is inconsistent state. This is either corruption or a crafted image. Fail the mount as this needs a closer look what is actually wrong. As of now if BTRFS_DEV_REPLACE_DEVID is present without the replace item, in __btrfs_free_extra_devids() we determine that there is an extra device, and free those extra devices but continue to mount the device. However, we were wrong in keeping tack of the rw_devices so the syzbot testcase failed: WARNING: CPU: 1 PID: 3612 at fs/btrfs/volumes.c:1166 close_fs_devices.part.0+0x607/0x800 fs/btrfs/volumes.c:1166 Kernel panic - not syncing: panic_on_warn set ... CPU: 1 PID: 3612 Comm: syz-executor.2 Not tainted 5.9.0-rc4-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x198/0x1fd lib/dump_stack.c:118 panic+0x347/0x7c0 kernel/panic.c:231 __warn.cold+0x20/0x46 kernel/panic.c:600 report_bug+0x1bd/0x210 lib/bug.c:198 handle_bug+0x38/0x90 arch/x86/kernel/traps.c:234 exc_invalid_op+0x14/0x40 arch/x86/kernel/traps.c:254 asm_exc_invalid_op+0x12/0x20 arch/x86/include/asm/idtentry.h:536 RIP: 0010:close_fs_devices.part.0+0x607/0x800 fs/btrfs/volumes.c:1166 RSP: 0018:ffffc900091777e0 EFLAGS: 00010246 RAX: 0000000000040000 RBX: ffffffffffffffff RCX: ffffc9000c8b7000 RDX: 0000000000040000 RSI: ffffffff83097f47 RDI: 0000000000000007 RBP: dffffc0000000000 R08: 0000000000000001 R09: ffff8880988a187f R10: 0000000000000000 R11: 0000000000000001 R12: ffff88809593a130 R13: ffff88809593a1ec R14: ffff8880988a1908 R15: ffff88809593a050 close_fs_devices fs/btrfs/volumes.c:1193 [inline] btrfs_close_devices+0x95/0x1f0 fs/btrfs/volumes.c:1179 open_ctree+0x4984/0x4a2d fs/btrfs/disk-io.c:3434 btrfs_fill_super fs/btrfs/super.c:1316 [inline] btrfs_mount_root.cold+0x14/0x165 fs/btrfs/super.c:1672 The fix here is, when we determine that there isn't a replace item then fail the mount if there is a replace target device (devid 0). CC: [email protected] # 4.19+ Reported-by: [email protected] Signed-off-by: Anand Jain <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent a4852cf commit cf89af1

File tree

2 files changed

+31
-21
lines changed

2 files changed

+31
-21
lines changed

fs/btrfs/dev-replace.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,17 @@ int btrfs_init_dev_replace(struct btrfs_fs_info *fs_info)
9191
ret = btrfs_search_slot(NULL, dev_root, &key, path, 0, 0);
9292
if (ret) {
9393
no_valid_dev_replace_entry_found:
94+
/*
95+
* We don't have a replace item or it's corrupted. If there is
96+
* a replace target, fail the mount.
97+
*/
98+
if (btrfs_find_device(fs_info->fs_devices,
99+
BTRFS_DEV_REPLACE_DEVID, NULL, NULL, false)) {
100+
btrfs_err(fs_info,
101+
"found replace target device without a valid replace item");
102+
ret = -EUCLEAN;
103+
goto out;
104+
}
94105
ret = 0;
95106
dev_replace->replace_state =
96107
BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED;
@@ -143,8 +154,19 @@ int btrfs_init_dev_replace(struct btrfs_fs_info *fs_info)
143154
case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
144155
case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
145156
case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
146-
dev_replace->srcdev = NULL;
147-
dev_replace->tgtdev = NULL;
157+
/*
158+
* We don't have an active replace item but if there is a
159+
* replace target, fail the mount.
160+
*/
161+
if (btrfs_find_device(fs_info->fs_devices,
162+
BTRFS_DEV_REPLACE_DEVID, NULL, NULL, false)) {
163+
btrfs_err(fs_info,
164+
"replace devid present without an active replace item");
165+
ret = -EUCLEAN;
166+
} else {
167+
dev_replace->srcdev = NULL;
168+
dev_replace->tgtdev = NULL;
169+
}
148170
break;
149171
case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
150172
case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:

fs/btrfs/volumes.c

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,22 +1056,13 @@ static void __btrfs_free_extra_devids(struct btrfs_fs_devices *fs_devices,
10561056
continue;
10571057
}
10581058

1059-
if (device->devid == BTRFS_DEV_REPLACE_DEVID) {
1060-
/*
1061-
* In the first step, keep the device which has
1062-
* the correct fsid and the devid that is used
1063-
* for the dev_replace procedure.
1064-
* In the second step, the dev_replace state is
1065-
* read from the device tree and it is known
1066-
* whether the procedure is really active or
1067-
* not, which means whether this device is
1068-
* used or whether it should be removed.
1069-
*/
1070-
if (step == 0 || test_bit(BTRFS_DEV_STATE_REPLACE_TGT,
1071-
&device->dev_state)) {
1072-
continue;
1073-
}
1074-
}
1059+
/*
1060+
* We have already validated the presence of BTRFS_DEV_REPLACE_DEVID,
1061+
* in btrfs_init_dev_replace() so just continue.
1062+
*/
1063+
if (device->devid == BTRFS_DEV_REPLACE_DEVID)
1064+
continue;
1065+
10751066
if (device->bdev) {
10761067
blkdev_put(device->bdev, device->mode);
10771068
device->bdev = NULL;
@@ -1080,9 +1071,6 @@ static void __btrfs_free_extra_devids(struct btrfs_fs_devices *fs_devices,
10801071
if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) {
10811072
list_del_init(&device->dev_alloc_list);
10821073
clear_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state);
1083-
if (!test_bit(BTRFS_DEV_STATE_REPLACE_TGT,
1084-
&device->dev_state))
1085-
fs_devices->rw_devices--;
10861074
}
10871075
list_del_init(&device->dev_list);
10881076
fs_devices->num_devices--;

0 commit comments

Comments
 (0)