CVE-2023-54180
Publication date:
30/12/2025
In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
btrfs: handle case when repair happens with dev-replace<br />
<br />
[BUG]<br />
There is a bug report that a BUG_ON() in btrfs_repair_io_failure()<br />
(originally repair_io_failure() in v6.0 kernel) got triggered when<br />
replacing a unreliable disk:<br />
<br />
BTRFS warning (device sda1): csum failed root 257 ino 2397453 off 39624704 csum 0xb0d18c75 expected csum 0x4dae9c5e mirror 3<br />
kernel BUG at fs/btrfs/extent_io.c:2380!<br />
invalid opcode: 0000 [#1] PREEMPT SMP NOPTI<br />
CPU: 9 PID: 3614331 Comm: kworker/u257:2 Tainted: G OE 6.0.0-5-amd64 #1 Debian 6.0.10-2<br />
Hardware name: Micro-Star International Co., Ltd. MS-7C60/TRX40 PRO WIFI (MS-7C60), BIOS 2.70 07/01/2021<br />
Workqueue: btrfs-endio btrfs_end_bio_work [btrfs]<br />
RIP: 0010:repair_io_failure+0x24a/0x260 [btrfs]<br />
Call Trace:<br />
<br />
clean_io_failure+0x14d/0x180 [btrfs]<br />
end_bio_extent_readpage+0x412/0x6e0 [btrfs]<br />
? __switch_to+0x106/0x420<br />
process_one_work+0x1c7/0x380<br />
worker_thread+0x4d/0x380<br />
? rescuer_thread+0x3a0/0x3a0<br />
kthread+0xe9/0x110<br />
? kthread_complete_and_exit+0x20/0x20<br />
ret_from_fork+0x22/0x30<br />
<br />
[CAUSE]<br />
<br />
Before the BUG_ON(), we got some read errors from the replace target<br />
first, note the mirror number (3, which is beyond RAID1 duplication,<br />
thus it&#39;s read from the replace target device).<br />
<br />
Then at the BUG_ON() location, we are trying to writeback the repaired<br />
sectors back the failed device.<br />
<br />
The check looks like this:<br />
<br />
ret = btrfs_map_block(fs_info, BTRFS_MAP_WRITE, logical,<br />
&map_length, &bioc, mirror_num);<br />
if (ret)<br />
goto out_counter_dec;<br />
BUG_ON(mirror_num != bioc->mirror_num);<br />
<br />
But inside btrfs_map_block(), we can modify bioc->mirror_num especially<br />
for dev-replace:<br />
<br />
if (dev_replace_is_ongoing && mirror_num == map->num_stripes + 1 &&<br />
!need_full_stripe(op) && dev_replace->tgtdev != NULL) {<br />
ret = get_extra_mirror_from_replace(fs_info, logical, *length,<br />
dev_replace->srcdev->devid,<br />
&mirror_num,<br />
&physical_to_patch_in_first_stripe);<br />
patch_the_first_stripe_for_dev_replace = 1;<br />
}<br />
<br />
Thus if we&#39;re repairing the replace target device, we&#39;re going to<br />
trigger that BUG_ON().<br />
<br />
But in reality, the read failure from the replace target device may be<br />
that, our replace hasn&#39;t reached the range we&#39;re reading, thus we&#39;re<br />
reading garbage, but with replace running, the range would be properly<br />
filled later.<br />
<br />
Thus in that case, we don&#39;t need to do anything but let the replace<br />
routine to handle it.<br />
<br />
[FIX]<br />
Instead of a BUG_ON(), just skip the repair if we&#39;re repairing the<br />
device replace target device.
Severity CVSS v4.0: Pending analysis
Last modification:
31/12/2025