mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
md: Fix the null-ptr-deref of 'mddev->private' while submitting IO
Concurrent processes md_stop and IO submission can trigger a NULL
pointer dereference of mddev->private:
BUG: kernel NULL pointer dereference, address: 0000000000000070
RIP: 0010:_wait_barrier+0x2f/0x250
Call Trace:
raid1_make_request+0x150/0xf50
md_handle_request+0x104/0x530
md_submit_bio+0x76/0x130
submit_bio+0xdd/0x250
submit_bio_wait+0x1f/0x40
__blkdev_direct_IO_simple+0x1f6/0x370
blkdev_write_iter+0x3b2/0x520
ksys_write+0x7d/0x190
One process can submit I/O through an open file while a forked process
uses the same file to issue STOP_ARRAY. The opener check permits this
because md_open() was called only once. do_md_stop() can then clear
mddev->private while the I/O is entering the personality make_request
callback.
This affects raid0, raid1, raid10, and raid5. Suspend STOP_ARRAY while
holding the reconfiguration lock so teardown waits for active_io
references to drain. Fail I/O in md_handle_request() if mddev->pers is
NULL after the array resumes.
The sysfs stopping paths are protected by
mddev_set_closing_and_sync_blockdev(). Apply that protection to both
clear and inactive transitions even when the array is read-only.
dm-raid teardown is already invoked with the device suspended.
Fetch a reproducer in
https://bugzilla.kernel.org/show_bug.cgi?id=222020
Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Reported-by: syzbot+3fe892ea5fc292e1353f@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=3fe892ea5fc292e1353f
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Link: https://patch.msgid.link/20260922030538.1634904-1-chengzhihao1@huawei.com
Signed-off-by: Yu Kuai <yukuai@fygo.io>
This commit is contained in:
parent
73b0df9294
commit
4c90b76e01
|
|
@ -414,6 +414,20 @@ bool md_handle_request(struct mddev *mddev, struct bio *bio)
|
|||
if (!percpu_ref_tryget_live(&mddev->active_io))
|
||||
goto check_suspended;
|
||||
}
|
||||
if (!mddev->pers) {
|
||||
/*
|
||||
* The __md_stop() sets 'mddev->private' to NULL during
|
||||
* the IO submitting, check 'mddev->pers' before the IO
|
||||
* being processed by specific driver to avoid the
|
||||
* null-ptr-deref of 'mddev-><member>'. The check is
|
||||
* safe because the IO has got the 'mddev->active_io'
|
||||
* reference, and all __md_stop() callers will wait for
|
||||
* the reference to be zero.
|
||||
*/
|
||||
bio_io_error(bio);
|
||||
percpu_ref_put(&mddev->active_io);
|
||||
return true;
|
||||
}
|
||||
if (!mddev->pers->make_request(mddev, bio)) {
|
||||
percpu_ref_put(&mddev->active_io);
|
||||
if (mddev_is_dm(mddev) && mddev->pers->prepare_suspend)
|
||||
|
|
@ -4670,7 +4684,8 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)
|
|||
case readonly:
|
||||
case inactive:
|
||||
case read_auto:
|
||||
if (!mddev->pers || !md_is_rdwr(mddev))
|
||||
if (!mddev->pers ||
|
||||
(st != clear && st != inactive && !md_is_rdwr(mddev)))
|
||||
break;
|
||||
/* write sysfs will not open mddev and opener should be 0 */
|
||||
err = mddev_set_closing_and_sync_blockdev(mddev, 0);
|
||||
|
|
@ -8299,6 +8314,7 @@ static bool md_ioctl_need_suspend(unsigned int cmd)
|
|||
case HOT_REMOVE_DISK:
|
||||
case SET_BITMAP_FILE:
|
||||
case SET_ARRAY_INFO:
|
||||
case STOP_ARRAY:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user