CVE-2026-46050
Description
In the Linux kernel, the following vulnerability has been resolved:
md/raid10: fix deadlock with check operation and nowait requests
When an array check is running it will raise the barrier at which point normal requests will become blocked and increment the nr_pending value to signal there is work pending inside of wait_barrier(). NOWAIT requests do not block and so will return immediately with an error, and additionally do not increment nr_pending in wait_barrier(). Upstream change commit 43806c3d5b9b ("raid10: cleanup memleak at raid10_make_request") added a call to raid_end_bio_io() to fix a memory leak when NOWAIT requests hit this condition. raid_end_bio_io() eventually calls allow_barrier() and it will unconditionally do an atomic_dec_and_test(&conf->nr_pending) even though the corresponding increment on nr_pending didn't happen in the NOWAIT case.
This can be easily seen by starting a check operation while an application is doing nowait IO on the same array. This results in a deadlocked state due to nr_pending value underflowing and so the md resync thread gets stuck waiting for nr_pending to == 0.
Output of r10conf state of the array when we hit this condition:
crash> struct r10conf barrier = 1, nr_pending = { counter = -41 }, nr_waiting = 15, nr_queued = 0,
Example of md_sync thread stuck waiting on raise_barrier() and other requests stuck in wait_barrier():
md1_resync [<0>] raise_barrier+0xce/0x1c0 [<0>] raid10_sync_request+0x1ca/0x1ed0 [<0>] md_do_sync+0x779/0x1110 [<0>] md_thread+0x90/0x160 [<0>] kthread+0xbe/0xf0 [<0>] ret_from_fork+0x34/0x50 [<0>] ret_from_fork_asm+0x1a/0x30
kworker/u1040:2+flush-253:4 [<0>] wait_barrier+0x1de/0x220 [<0>] regular_request_wait+0x30/0x180 [<0>] raid10_make_request+0x261/0x1000 [<0>] md_handle_request+0x13b/0x230 [<0>] __submit_bio+0x107/0x1f0 [<0>] submit_bio_noacct_nocheck+0x16f/0x390 [<0>] ext4_io_submit+0x24/0x40 [<0>] ext4_do_writepages+0x254/0xc80 [<0>] ext4_writepages+0x84/0x120 [<0>] do_writepages+0x7a/0x260 [<0>] __writeback_single_inode+0x3d/0x300 [<0>] writeback_sb_inodes+0x1dd/0x470 [<0>] __writeback_inodes_wb+0x4c/0xe0 [<0>] wb_writeback+0x18b/0x2d0 [<0>] wb_workfn+0x2a1/0x400 [<0>] process_one_work+0x149/0x330 [<0>] worker_thread+0x2d2/0x410 [<0>] kthread+0xbe/0xf0 [<0>] ret_from_fork+0x34/0x50 [<0>] ret_from_fork_asm+0x1a/0x30
Affected products
1Patches
107d96f3120a7fmd/raid10: fix deadlock with check operation and nowait requests
1 file changed · +2 −3
drivers/md/raid10.c+2 −3 modifieddiff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index cfbd345805ca2d..4901ebe45c8755 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1184,7 +1184,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } @@ -1372,7 +1372,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } -- cgit 1.3-korg
965d6162dd88md/raid10: fix deadlock with check operation and nowait requests
1 file changed · +2 −3
drivers/md/raid10.c+2 −3 modifieddiff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 6bcf6852c20005..99aabf5734f5d9 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1204,7 +1204,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } @@ -1425,7 +1425,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } -- cgit 1.3-korg
42fe37c90184md/raid10: fix deadlock with check operation and nowait requests
1 file changed · +2 −3
drivers/md/raid10.c+2 −3 modifieddiff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index db07c99c4d9470..4b02313854b67c 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1182,7 +1182,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } @@ -1381,7 +1381,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } -- cgit 1.3-korg
cac2106bb9a2md/raid10: fix deadlock with check operation and nowait requests
1 file changed · +2 −3
drivers/md/raid10.c+2 −3 modifieddiff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index d58ae150b45022..706037d2a87c45 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1184,7 +1184,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } @@ -1372,7 +1372,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } -- cgit 1.3-korg
1cdff2937c61md/raid10: fix deadlock with check operation and nowait requests
1 file changed · +2 −3
drivers/md/raid10.c+2 −3 modifieddiff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 0653b5d8545a6c..12cbeec026c55c 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1184,7 +1184,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } @@ -1372,7 +1372,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } -- cgit 1.3-korg
1cdff2937c61md/raid10: fix deadlock with check operation and nowait requests
2 files changed · +4 −6
drivers/md/raid10.c+2 −3 modifieddiff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 0653b5d8545a6c..12cbeec026c55c 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1184,7 +1184,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } @@ -1372,7 +1372,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } -- cgit 1.3-korg
drivers/md/raid10.c+2 −3 modifieddiff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 0653b5d8545a6c..12cbeec026c55c 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1184,7 +1184,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } @@ -1372,7 +1372,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } -- cgit 1.3-korg
42fe37c90184md/raid10: fix deadlock with check operation and nowait requests
2 files changed · +4 −6
drivers/md/raid10.c+2 −3 modifieddiff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index db07c99c4d9470..4b02313854b67c 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1182,7 +1182,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } @@ -1381,7 +1381,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } -- cgit 1.3-korg
drivers/md/raid10.c+2 −3 modifieddiff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index db07c99c4d9470..4b02313854b67c 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1182,7 +1182,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } @@ -1381,7 +1381,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } -- cgit 1.3-korg
7d96f3120a7fmd/raid10: fix deadlock with check operation and nowait requests
1 file changed · +2 −3
drivers/md/raid10.c+2 −3 modifieddiff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index cfbd345805ca2d..4901ebe45c8755 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1184,7 +1184,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } @@ -1372,7 +1372,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } -- cgit 1.3-korg
965d6162dd88md/raid10: fix deadlock with check operation and nowait requests
1 file changed · +2 −3
drivers/md/raid10.c+2 −3 modifieddiff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 6bcf6852c20005..99aabf5734f5d9 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1204,7 +1204,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } @@ -1425,7 +1425,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } -- cgit 1.3-korg
cac2106bb9a2md/raid10: fix deadlock with check operation and nowait requests
1 file changed · +2 −3
drivers/md/raid10.c+2 −3 modifieddiff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index d58ae150b45022..706037d2a87c45 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1184,7 +1184,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } @@ -1372,7 +1372,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { - raid_end_bio_io(r10_bio); + free_r10bio(r10_bio); return; } -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing accounting for NOWAIT requests in the barrier mechanism: `raid_end_bio_io()` unconditionally decrements `nr_pending` via `allow_barrier()`, but NOWAIT requests never incremented `nr_pending` in `wait_barrier()`, causing an underflow that deadlocks the resync thread."
Attack vector
An attacker or local user triggers a RAID10 array check operation (e.g. via `mdadm --check`) while simultaneously issuing NOWAIT I/O requests to the same array. The check raises the barrier, causing normal requests to block and increment `nr_pending`. NOWAIT requests return immediately without incrementing `nr_pending`, but the code path introduced by commit 43806c3d5b9b calls `raid_end_bio_io()` which unconditionally decrements `nr_pending` via `allow_barrier()`. This mismatch causes `nr_pending` to underflow (observed value -41), preventing the resync thread from ever seeing `nr_pending == 0`, resulting in a permanent deadlock of the md resync thread and all subsequent I/O [patch_id=2660129].
Affected code
The bug is in `drivers/md/raid10.c` in the functions `raid10_read_request()` and `raid10_write_request()` [patch_id=2660129]. When `regular_request_wait()` returns false for a NOWAIT bio, the code previously called `raid_end_bio_io(r10_bio)` which eventually calls `allow_barrier()` and unconditionally decrements `conf->nr_pending`. The fix changes these calls to `free_r10bio(r10_bio)` instead.
What the fix does
The patch replaces `raid_end_bio_io(r10_bio)` with `free_r10bio(r10_bio)` in both `raid10_read_request()` and `raid10_write_request()` when `regular_request_wait()` fails for a NOWAIT request [patch_id=2660129]. `raid_end_bio_io()` calls `allow_barrier()` which unconditionally decrements `conf->nr_pending`, but NOWAIT requests never incremented that counter. By using `free_r10bio()` instead, the bio is freed without touching the barrier accounting, preventing the `nr_pending` underflow that caused the deadlock. The memory leak that commit 43806c3d5b9b originally fixed is still addressed because `free_r10bio()` properly releases the `r10_bio` structure.
Preconditions
- configA RAID10 array must be configured and active on the system.
- inputAn application must issue NOWAIT I/O requests (REQ_NOWAIT) to the RAID10 array.
- inputA check operation (e.g. via mdadm --check) must be started on the same array while NOWAIT I/O is in flight.
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/1cdff2937c618f81058422bbdc4974a3e7ec9379nvd
- git.kernel.org/stable/c/42fe37c90184cd1568838b84b488934c3671c963nvd
- git.kernel.org/stable/c/7d96f3120a7fb7210d21b520c5b6f495da6ba436nvd
- git.kernel.org/stable/c/965d6162dd88cc7cc193cf7f5bfc132d8bbf0523nvd
- git.kernel.org/stable/c/cac2106bb9a2180b288079b49ed626414fb5bc45nvd
News mentions
0No linked articles in our index yet.