mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3] md: Fix the null-ptr-deref of 'mddev->private' while submitting IO
@ 2026-09-18  7:09 Zhihao Cheng
  2026-09-18  7:29 ` Abd-Alrhman Masalkhi
  0 siblings, 1 reply; 8+ messages in thread
From: Zhihao Cheng @ 2026-09-18  7:09 UTC (permalink / raw)
  To: song, yukuai, xiao, magiclinan, eadavis, abd.masalkhi
  Cc: linux-raid, linux-kernel, chengzhihao1, yangerkun, yi.zhang

Concurrent processes md_stop and IO submitting could trigger a
null-ptr-deref 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

              P1
  fd = open(/dev/md0, O_RDWR)
	                      P2 (forked from P1, fd' <= fd)
  write(fd)
   submit_bio
    md_handle_request
     raid1_make_request
      raid1_write_request
                                  ioctl(fd, STOP_ARRAY)
		                   mddev_set_closing_and_sync_blockdev
				   // check passed, mddev->openers = 1,
				   // because md_open() is only called
				   // once in P1->open
				   do_md_stop
				    __md_stop
				     mddev->private = NULL

       conf = mddev->private // NULL
        wait_barrier(conf, sector) // null-ptr-deref !

It is a common problem for raid0/1/10/5, and __md_stop could be triggered
by several paths(eg. ioctl, sysfs, ->dtr). Fix it by replacing
mddev_lock() with mddev_suspend_and_lock() for all __md_stop() callers.
The caller dm_table_destroy() is guaranteed being invoked with device
suspended, so raid_dtr() could keep using mddev_lock_nointr().
The caller array_state_store() is guaranteed by the check
mddev_set_closing_and_sync_blockdev(mddev, 0). For example, someone open
/dev/mdx, write something and close /dev/mdx, it won't trigger the
problem, all dirty pages can be flushed before mddev->openers decrement.
Besides, fail the submitting IO in md_handle_request() if the
'mddev->pers' becomes NULL.

Fetch a reproducer in https://bugzilla.kernel.org/show_bug.cgi?id=222020

Fixes: 1da177e4c3f4 ("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>
---
 v1->v2:
  1. Add 'mddev->pers != NULL' check before make_request
  2. Delete dm-raid caller(->dtr) modifications
  3. Move memalloc_noio_restore after mddev_unlock_and_resume
 v2->v3:
  1. Remove modifications in array_state_store()
  2. update commit msg
 drivers/md/md.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index 680b34a63cb3..e73338c26d53 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -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)
@@ -8299,6 +8313,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;
-- 
2.52.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3] md: Fix the null-ptr-deref of 'mddev->private' while submitting IO
  2026-09-18  7:09 [PATCH v3] md: Fix the null-ptr-deref of 'mddev->private' while submitting IO Zhihao Cheng
@ 2026-09-18  7:29 ` Abd-Alrhman Masalkhi
  2026-09-18  7:38   ` Zhihao Cheng
  0 siblings, 1 reply; 8+ messages in thread
From: Abd-Alrhman Masalkhi @ 2026-09-18  7:29 UTC (permalink / raw)
  To: Zhihao Cheng, song, yukuai, xiao, magiclinan, eadavis
  Cc: linux-raid, linux-kernel, chengzhihao1, yangerkun, yi.zhang


Hi Zhihao,

On Fri, Sep 18, 2026 at 15:09 +0800, Zhihao Cheng wrote:
> Concurrent processes md_stop and IO submitting could trigger a
> null-ptr-deref 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
>
>               P1
>   fd = open(/dev/md0, O_RDWR)
> 	                      P2 (forked from P1, fd' <= fd)
>   write(fd)
>    submit_bio
>     md_handle_request
>      raid1_make_request
>       raid1_write_request
>                                   ioctl(fd, STOP_ARRAY)
> 		                   mddev_set_closing_and_sync_blockdev
> 				   // check passed, mddev->openers = 1,
> 				   // because md_open() is only called
> 				   // once in P1->open
> 				   do_md_stop
> 				    __md_stop
> 				     mddev->private = NULL
>
>        conf = mddev->private // NULL
>         wait_barrier(conf, sector) // null-ptr-deref !
>
> It is a common problem for raid0/1/10/5, and __md_stop could be triggered
> by several paths(eg. ioctl, sysfs, ->dtr). Fix it by replacing
> mddev_lock() with mddev_suspend_and_lock() for all __md_stop() callers.
> The caller dm_table_destroy() is guaranteed being invoked with device
> suspended, so raid_dtr() could keep using mddev_lock_nointr().
> The caller array_state_store() is guaranteed by the check
> mddev_set_closing_and_sync_blockdev(mddev, 0). For example, someone open
> /dev/mdx, write something and close /dev/mdx, it won't trigger the
> problem, all dirty pages can be flushed before mddev->openers decrement.
> Besides, fail the submitting IO in md_handle_request() if the
> 'mddev->pers' becomes NULL.
>
> Fetch a reproducer in https://bugzilla.kernel.org/show_bug.cgi?id=222020
>
> Fixes: 1da177e4c3f4 ("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>
> ---
>  v1->v2:
>   1. Add 'mddev->pers != NULL' check before make_request
>   2. Delete dm-raid caller(->dtr) modifications
>   3. Move memalloc_noio_restore after mddev_unlock_and_resume
>  v2->v3:
>   1. Remove modifications in array_state_store()
>   2. update commit msg
>  drivers/md/md.c | 15 +++++++++++++++
>  1 file changed, 15 insertions(+)
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 680b34a63cb3..e73338c26d53 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -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) {
Isn't this case already handled by md_submit_bio(). take a look in
md_submit_bio()

> +		/*
> +		 * 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)
> @@ -8299,6 +8313,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;
> -- 
> 2.52.0
>

-- 
Best Regards,
Abd-Alrhman

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3] md: Fix the null-ptr-deref of 'mddev->private' while submitting IO
  2026-09-18  7:29 ` Abd-Alrhman Masalkhi
@ 2026-09-18  7:38   ` Zhihao Cheng
  2026-09-18  8:20     ` Abd-Alrhman Masalkhi
  0 siblings, 1 reply; 8+ messages in thread
From: Zhihao Cheng @ 2026-09-18  7:38 UTC (permalink / raw)
  To: Abd-Alrhman Masalkhi, song, yukuai, xiao, magiclinan, eadavis
  Cc: linux-raid, linux-kernel, yangerkun, yi.zhang

在 2026/9/18 15:29, Abd-Alrhman Masalkhi 写道:
> 
> Hi Zhihao,
> 
> On Fri, Sep 18, 2026 at 15:09 +0800, Zhihao Cheng wrote:
>> Concurrent processes md_stop and IO submitting could trigger a
>> null-ptr-deref 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
>>
>>                P1
>>    fd = open(/dev/md0, O_RDWR)
>> 	                      P2 (forked from P1, fd' <= fd)
>>    write(fd)
>>     submit_bio
>>      md_handle_request
>>       raid1_make_request
>>        raid1_write_request
>>                                    ioctl(fd, STOP_ARRAY)
>> 		                   mddev_set_closing_and_sync_blockdev
>> 				   // check passed, mddev->openers = 1,
>> 				   // because md_open() is only called
>> 				   // once in P1->open
>> 				   do_md_stop
>> 				    __md_stop
>> 				     mddev->private = NULL
>>
>>         conf = mddev->private // NULL
>>          wait_barrier(conf, sector) // null-ptr-deref !
>>
>> It is a common problem for raid0/1/10/5, and __md_stop could be triggered
>> by several paths(eg. ioctl, sysfs, ->dtr). Fix it by replacing
>> mddev_lock() with mddev_suspend_and_lock() for all __md_stop() callers.
>> The caller dm_table_destroy() is guaranteed being invoked with device
>> suspended, so raid_dtr() could keep using mddev_lock_nointr().
>> The caller array_state_store() is guaranteed by the check
>> mddev_set_closing_and_sync_blockdev(mddev, 0). For example, someone open
>> /dev/mdx, write something and close /dev/mdx, it won't trigger the
>> problem, all dirty pages can be flushed before mddev->openers decrement.
>> Besides, fail the submitting IO in md_handle_request() if the
>> 'mddev->pers' becomes NULL.
>>
>> Fetch a reproducer in https://bugzilla.kernel.org/show_bug.cgi?id=222020
>>
>> Fixes: 1da177e4c3f4 ("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>
>> ---
>>   v1->v2:
>>    1. Add 'mddev->pers != NULL' check before make_request
>>    2. Delete dm-raid caller(->dtr) modifications
>>    3. Move memalloc_noio_restore after mddev_unlock_and_resume
>>   v2->v3:
>>    1. Remove modifications in array_state_store()
>>    2. update commit msg
>>   drivers/md/md.c | 15 +++++++++++++++
>>   1 file changed, 15 insertions(+)
>>
>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>> index 680b34a63cb3..e73338c26d53 100644
>> --- a/drivers/md/md.c
>> +++ b/drivers/md/md.c
>> @@ -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) {
> Isn't this case already handled by md_submit_bio(). take a look in
> md_submit_bio()
Hi,Abd-Alrhman
I understand that 'mddev == NULL || mddev->pers == NULL' in 
md_submit_bio() is a qiuck check, there still exists a small race window:
      P1                     P2
md_submit_bio
  if (mddev == NULL || mddev->pers == NULL)
                         md_ioctl
                          mddev_suspend_and_lock
                          do_md_stop->__md_stop
                          // set mddev->pers/private as NULL
                          mddev_unlock_and_resume
  md_handle_request
   if (is_suspended(mddev, bio))
   percpu_ref_tryget_live(&mddev->active_io)
   mddev->pers // null-ptr-deref
> 
>> +		/*
>> +		 * 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)
>> @@ -8299,6 +8313,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;
>> -- 
>> 2.52.0
>>
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3] md: Fix the null-ptr-deref of 'mddev->private' while submitting IO
  2026-09-18  7:38   ` Zhihao Cheng
@ 2026-09-18  8:20     ` Abd-Alrhman Masalkhi
  2026-09-18  8:34       ` Zhihao Cheng
  0 siblings, 1 reply; 8+ messages in thread
From: Abd-Alrhman Masalkhi @ 2026-09-18  8:20 UTC (permalink / raw)
  To: Zhihao Cheng, song, yukuai, xiao, magiclinan, eadavis
  Cc: linux-raid, linux-kernel, yangerkun, yi.zhang

On Fri, Sep 18, 2026 at 15:38 +0800, Zhihao Cheng wrote:
> 在 2026/9/18 15:29, Abd-Alrhman Masalkhi 写道:
>> 
>> Hi Zhihao,
>> 
>> On Fri, Sep 18, 2026 at 15:09 +0800, Zhihao Cheng wrote:
>>> Concurrent processes md_stop and IO submitting could trigger a
>>> null-ptr-deref 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
>>>
>>>                P1
>>>    fd = open(/dev/md0, O_RDWR)
>>> 	                      P2 (forked from P1, fd' <= fd)
>>>    write(fd)
>>>     submit_bio
>>>      md_handle_request
>>>       raid1_make_request
>>>        raid1_write_request
>>>                                    ioctl(fd, STOP_ARRAY)
>>> 		                   mddev_set_closing_and_sync_blockdev
>>> 				   // check passed, mddev->openers = 1,
>>> 				   // because md_open() is only called
>>> 				   // once in P1->open
>>> 				   do_md_stop
>>> 				    __md_stop
>>> 				     mddev->private = NULL
>>>
>>>         conf = mddev->private // NULL
>>>          wait_barrier(conf, sector) // null-ptr-deref !
>>>
>>> It is a common problem for raid0/1/10/5, and __md_stop could be triggered
>>> by several paths(eg. ioctl, sysfs, ->dtr). Fix it by replacing
>>> mddev_lock() with mddev_suspend_and_lock() for all __md_stop() callers.
>>> The caller dm_table_destroy() is guaranteed being invoked with device
>>> suspended, so raid_dtr() could keep using mddev_lock_nointr().
>>> The caller array_state_store() is guaranteed by the check
>>> mddev_set_closing_and_sync_blockdev(mddev, 0). For example, someone open
>>> /dev/mdx, write something and close /dev/mdx, it won't trigger the
>>> problem, all dirty pages can be flushed before mddev->openers decrement.
>>> Besides, fail the submitting IO in md_handle_request() if the
>>> 'mddev->pers' becomes NULL.
>>>
>>> Fetch a reproducer in https://bugzilla.kernel.org/show_bug.cgi?id=222020
>>>
>>> Fixes: 1da177e4c3f4 ("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>
>>> ---
>>>   v1->v2:
>>>    1. Add 'mddev->pers != NULL' check before make_request
>>>    2. Delete dm-raid caller(->dtr) modifications
>>>    3. Move memalloc_noio_restore after mddev_unlock_and_resume
>>>   v2->v3:
>>>    1. Remove modifications in array_state_store()
>>>    2. update commit msg
>>>   drivers/md/md.c | 15 +++++++++++++++
>>>   1 file changed, 15 insertions(+)
>>>
>>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>>> index 680b34a63cb3..e73338c26d53 100644
>>> --- a/drivers/md/md.c
>>> +++ b/drivers/md/md.c
>>> @@ -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) {
>> Isn't this case already handled by md_submit_bio(). take a look in
>> md_submit_bio()
> Hi,Abd-Alrhman
> I understand that 'mddev == NULL || mddev->pers == NULL' in 
> md_submit_bio() is a qiuck check, there still exists a small race window:
>       P1                     P2
> md_submit_bio
>   if (mddev == NULL || mddev->pers == NULL)
>                          md_ioctl
>                           mddev_suspend_and_lock
>                           do_md_stop->__md_stop
>                           // set mddev->pers/private as NULL
>                           mddev_unlock_and_resume
>   md_handle_request
>    if (is_suspended(mddev, bio))
>    percpu_ref_tryget_live(&mddev->active_io)
>    mddev->pers // null-ptr-deref

It makes sense. It would solve only the null pointer reference, but I
see another issue. Look at the comment before calling
mddev_set_closing_and_sync_blockdev() in md_ioctl(). it says "Need to
flush page cache, and ensure no-one else opens and writes". The
suspending happens after flushing the page cache, in this case,
there might be other writes in-flight.

>> 
>>> +		/*
>>> +		 * 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)
>>> @@ -8299,6 +8313,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;
>>> -- 
>>> 2.52.0
>>>
>> 
>

-- 
Best Regards,
Abd-Alrhman

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3] md: Fix the null-ptr-deref of 'mddev->private' while submitting IO
  2026-09-18  8:20     ` Abd-Alrhman Masalkhi
@ 2026-09-18  8:34       ` Zhihao Cheng
  2026-09-18  8:54         ` Abd-Alrhman Masalkhi
  0 siblings, 1 reply; 8+ messages in thread
From: Zhihao Cheng @ 2026-09-18  8:34 UTC (permalink / raw)
  To: Abd-Alrhman Masalkhi, song, yukuai, xiao, magiclinan, eadavis
  Cc: linux-raid, linux-kernel, yangerkun, yi.zhang

在 2026/9/18 16:20, Abd-Alrhman Masalkhi 写道:
> On Fri, Sep 18, 2026 at 15:38 +0800, Zhihao Cheng wrote:
>> 在 2026/9/18 15:29, Abd-Alrhman Masalkhi 写道:
>>>
>>> Hi Zhihao,
>>>
>>> On Fri, Sep 18, 2026 at 15:09 +0800, Zhihao Cheng wrote:
>>>> Concurrent processes md_stop and IO submitting could trigger a
>>>> null-ptr-deref 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
>>>>
>>>>                 P1
>>>>     fd = open(/dev/md0, O_RDWR)
>>>> 	                      P2 (forked from P1, fd' <= fd)
>>>>     write(fd)
>>>>      submit_bio
>>>>       md_handle_request
>>>>        raid1_make_request
>>>>         raid1_write_request
>>>>                                     ioctl(fd, STOP_ARRAY)
>>>> 		                   mddev_set_closing_and_sync_blockdev
>>>> 				   // check passed, mddev->openers = 1,
>>>> 				   // because md_open() is only called
>>>> 				   // once in P1->open
>>>> 				   do_md_stop
>>>> 				    __md_stop
>>>> 				     mddev->private = NULL
>>>>
>>>>          conf = mddev->private // NULL
>>>>           wait_barrier(conf, sector) // null-ptr-deref !
>>>>
>>>> It is a common problem for raid0/1/10/5, and __md_stop could be triggered
>>>> by several paths(eg. ioctl, sysfs, ->dtr). Fix it by replacing
>>>> mddev_lock() with mddev_suspend_and_lock() for all __md_stop() callers.
>>>> The caller dm_table_destroy() is guaranteed being invoked with device
>>>> suspended, so raid_dtr() could keep using mddev_lock_nointr().
>>>> The caller array_state_store() is guaranteed by the check
>>>> mddev_set_closing_and_sync_blockdev(mddev, 0). For example, someone open
>>>> /dev/mdx, write something and close /dev/mdx, it won't trigger the
>>>> problem, all dirty pages can be flushed before mddev->openers decrement.
>>>> Besides, fail the submitting IO in md_handle_request() if the
>>>> 'mddev->pers' becomes NULL.
>>>>
>>>> Fetch a reproducer in https://bugzilla.kernel.org/show_bug.cgi?id=222020
>>>>
>>>> Fixes: 1da177e4c3f4 ("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>
>>>> ---
>>>>    v1->v2:
>>>>     1. Add 'mddev->pers != NULL' check before make_request
>>>>     2. Delete dm-raid caller(->dtr) modifications
>>>>     3. Move memalloc_noio_restore after mddev_unlock_and_resume
>>>>    v2->v3:
>>>>     1. Remove modifications in array_state_store()
>>>>     2. update commit msg
>>>>    drivers/md/md.c | 15 +++++++++++++++
>>>>    1 file changed, 15 insertions(+)
>>>>
>>>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>>>> index 680b34a63cb3..e73338c26d53 100644
>>>> --- a/drivers/md/md.c
>>>> +++ b/drivers/md/md.c
>>>> @@ -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) {
>>> Isn't this case already handled by md_submit_bio(). take a look in
>>> md_submit_bio()
>> Hi,Abd-Alrhman
>> I understand that 'mddev == NULL || mddev->pers == NULL' in
>> md_submit_bio() is a qiuck check, there still exists a small race window:
>>        P1                     P2
>> md_submit_bio
>>    if (mddev == NULL || mddev->pers == NULL)
>>                           md_ioctl
>>                            mddev_suspend_and_lock
>>                            do_md_stop->__md_stop
>>                            // set mddev->pers/private as NULL
>>                            mddev_unlock_and_resume
>>    md_handle_request
>>     if (is_suspended(mddev, bio))
>>     percpu_ref_tryget_live(&mddev->active_io)
>>     mddev->pers // null-ptr-deref
> 
> It makes sense. It would solve only the null pointer reference, but I
> see another issue. Look at the comment before calling
> mddev_set_closing_and_sync_blockdev() in md_ioctl(). it says "Need to
> flush page cache, and ensure no-one else opens and writes". The
> suspending happens after flushing the page cache, in this case,
> there might be other writes in-flight.

At present, the current implementation does not align with the expected 
annotations. Yu Kai privately gave me a feasible suggestion, which is to 
have md_ioctl operate on a character device(like /dev/mapper/control) 
instead of an md block device. This fix patch will serve as a temporary 
solution before the official implementation is merged. By the way, I 
don't have much time to implment it, do you have time to deal with the 
official solution?
> 
>>>
>>>> +		/*
>>>> +		 * 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)
>>>> @@ -8299,6 +8313,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;
>>>> -- 
>>>> 2.52.0
>>>>
>>>
>>
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3] md: Fix the null-ptr-deref of 'mddev->private' while submitting IO
  2026-09-18  8:34       ` Zhihao Cheng
@ 2026-09-18  8:54         ` Abd-Alrhman Masalkhi
  2026-09-18  9:30           ` yu kuai
  0 siblings, 1 reply; 8+ messages in thread
From: Abd-Alrhman Masalkhi @ 2026-09-18  8:54 UTC (permalink / raw)
  To: Zhihao Cheng, song, yukuai, xiao, magiclinan, eadavis
  Cc: linux-raid, linux-kernel, yangerkun, yi.zhang


Hi Zhihao,

On Fri, Sep 18, 2026 at 16:34 +0800, Zhihao Cheng wrote:
> 在 2026/9/18 16:20, Abd-Alrhman Masalkhi 写道:
>> On Fri, Sep 18, 2026 at 15:38 +0800, Zhihao Cheng wrote:
>>> 在 2026/9/18 15:29, Abd-Alrhman Masalkhi 写道:
>>>>
>>>> Hi Zhihao,
>>>>
>>>> On Fri, Sep 18, 2026 at 15:09 +0800, Zhihao Cheng wrote:
>>>>> Concurrent processes md_stop and IO submitting could trigger a
>>>>> null-ptr-deref 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
>>>>>
>>>>>                 P1
>>>>>     fd = open(/dev/md0, O_RDWR)
>>>>> 	                      P2 (forked from P1, fd' <= fd)
>>>>>     write(fd)
>>>>>      submit_bio
>>>>>       md_handle_request
>>>>>        raid1_make_request
>>>>>         raid1_write_request
>>>>>                                     ioctl(fd, STOP_ARRAY)
>>>>> 		                   mddev_set_closing_and_sync_blockdev
>>>>> 				   // check passed, mddev->openers = 1,
>>>>> 				   // because md_open() is only called
>>>>> 				   // once in P1->open
>>>>> 				   do_md_stop
>>>>> 				    __md_stop
>>>>> 				     mddev->private = NULL
>>>>>
>>>>>          conf = mddev->private // NULL
>>>>>           wait_barrier(conf, sector) // null-ptr-deref !
>>>>>
>>>>> It is a common problem for raid0/1/10/5, and __md_stop could be triggered
>>>>> by several paths(eg. ioctl, sysfs, ->dtr). Fix it by replacing
>>>>> mddev_lock() with mddev_suspend_and_lock() for all __md_stop() callers.
>>>>> The caller dm_table_destroy() is guaranteed being invoked with device
>>>>> suspended, so raid_dtr() could keep using mddev_lock_nointr().
>>>>> The caller array_state_store() is guaranteed by the check
>>>>> mddev_set_closing_and_sync_blockdev(mddev, 0). For example, someone open
>>>>> /dev/mdx, write something and close /dev/mdx, it won't trigger the
>>>>> problem, all dirty pages can be flushed before mddev->openers decrement.
>>>>> Besides, fail the submitting IO in md_handle_request() if the
>>>>> 'mddev->pers' becomes NULL.
>>>>>
>>>>> Fetch a reproducer in https://bugzilla.kernel.org/show_bug.cgi?id=222020
>>>>>
>>>>> Fixes: 1da177e4c3f4 ("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>
>>>>> ---
>>>>>    v1->v2:
>>>>>     1. Add 'mddev->pers != NULL' check before make_request
>>>>>     2. Delete dm-raid caller(->dtr) modifications
>>>>>     3. Move memalloc_noio_restore after mddev_unlock_and_resume
>>>>>    v2->v3:
>>>>>     1. Remove modifications in array_state_store()
>>>>>     2. update commit msg
>>>>>    drivers/md/md.c | 15 +++++++++++++++
>>>>>    1 file changed, 15 insertions(+)
>>>>>
>>>>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>>>>> index 680b34a63cb3..e73338c26d53 100644
>>>>> --- a/drivers/md/md.c
>>>>> +++ b/drivers/md/md.c
>>>>> @@ -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) {
>>>> Isn't this case already handled by md_submit_bio(). take a look in
>>>> md_submit_bio()
>>> Hi,Abd-Alrhman
>>> I understand that 'mddev == NULL || mddev->pers == NULL' in
>>> md_submit_bio() is a qiuck check, there still exists a small race window:
>>>        P1                     P2
>>> md_submit_bio
>>>    if (mddev == NULL || mddev->pers == NULL)
>>>                           md_ioctl
>>>                            mddev_suspend_and_lock
>>>                            do_md_stop->__md_stop
>>>                            // set mddev->pers/private as NULL
>>>                            mddev_unlock_and_resume
>>>    md_handle_request
>>>     if (is_suspended(mddev, bio))
>>>     percpu_ref_tryget_live(&mddev->active_io)
>>>     mddev->pers // null-ptr-deref
>> 
>> It makes sense. It would solve only the null pointer reference, but I
>> see another issue. Look at the comment before calling
>> mddev_set_closing_and_sync_blockdev() in md_ioctl(). it says "Need to
>> flush page cache, and ensure no-one else opens and writes". The
>> suspending happens after flushing the page cache, in this case,
>> there might be other writes in-flight.
>
> At present, the current implementation does not align with the expected 
> annotations. Yu Kai privately gave me a feasible suggestion, which is to 
> have md_ioctl operate on a character device(like /dev/mapper/control) 
> instead of an md block device. This fix patch will serve as a temporary 
> solution before the official implementation is merged. By the way, I 
> don't have much time to implment it, do you have time to deal with the 
> official solution?

Thanks for the summary. I can take on the official implementation.
Before I get started, could we align on the interface details and any
other relevant considerations?

>> 
>>>>
>>>>> +		/*
>>>>> +		 * 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)
>>>>> @@ -8299,6 +8313,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;
>>>>> -- 
>>>>> 2.52.0
>>>>>
>>>>
>>>
>> 
>

-- 
Best Regards,
Abd-Alrhman

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3] md: Fix the null-ptr-deref of 'mddev->private' while submitting IO
  2026-09-18  8:54         ` Abd-Alrhman Masalkhi
@ 2026-09-18  9:30           ` yu kuai
  2026-09-18  9:41             ` Abd-Alrhman Masalkhi
  0 siblings, 1 reply; 8+ messages in thread
From: yu kuai @ 2026-09-18  9:30 UTC (permalink / raw)
  To: Abd-Alrhman Masalkhi, Zhihao Cheng, song, xiao, magiclinan,
	eadavis, yu kuai
  Cc: linux-raid, linux-kernel, yangerkun, yi.zhang

Hi,

在 2026/9/18 16:54, Abd-Alrhman Masalkhi 写道:
> Hi Zhihao,
>
> On Fri, Sep 18, 2026 at 16:34 +0800, Zhihao Cheng wrote:
>> 在 2026/9/18 16:20, Abd-Alrhman Masalkhi 写道:
>>> On Fri, Sep 18, 2026 at 15:38 +0800, Zhihao Cheng wrote:
>>>> 在 2026/9/18 15:29, Abd-Alrhman Masalkhi 写道:
>>>>> Hi Zhihao,
>>>>>
>>>>> On Fri, Sep 18, 2026 at 15:09 +0800, Zhihao Cheng wrote:
>>>>>> Concurrent processes md_stop and IO submitting could trigger a
>>>>>> null-ptr-deref 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
>>>>>>
>>>>>>                  P1
>>>>>>      fd = open(/dev/md0, O_RDWR)
>>>>>> 	                      P2 (forked from P1, fd' <= fd)
>>>>>>      write(fd)
>>>>>>       submit_bio
>>>>>>        md_handle_request
>>>>>>         raid1_make_request
>>>>>>          raid1_write_request
>>>>>>                                      ioctl(fd, STOP_ARRAY)
>>>>>> 		                   mddev_set_closing_and_sync_blockdev
>>>>>> 				   // check passed, mddev->openers = 1,
>>>>>> 				   // because md_open() is only called
>>>>>> 				   // once in P1->open
>>>>>> 				   do_md_stop
>>>>>> 				    __md_stop
>>>>>> 				     mddev->private = NULL
>>>>>>
>>>>>>           conf = mddev->private // NULL
>>>>>>            wait_barrier(conf, sector) // null-ptr-deref !
>>>>>>
>>>>>> It is a common problem for raid0/1/10/5, and __md_stop could be triggered
>>>>>> by several paths(eg. ioctl, sysfs, ->dtr). Fix it by replacing
>>>>>> mddev_lock() with mddev_suspend_and_lock() for all __md_stop() callers.
>>>>>> The caller dm_table_destroy() is guaranteed being invoked with device
>>>>>> suspended, so raid_dtr() could keep using mddev_lock_nointr().
>>>>>> The caller array_state_store() is guaranteed by the check
>>>>>> mddev_set_closing_and_sync_blockdev(mddev, 0). For example, someone open
>>>>>> /dev/mdx, write something and close /dev/mdx, it won't trigger the
>>>>>> problem, all dirty pages can be flushed before mddev->openers decrement.
>>>>>> Besides, fail the submitting IO in md_handle_request() if the
>>>>>> 'mddev->pers' becomes NULL.
>>>>>>
>>>>>> Fetch a reproducer in https://bugzilla.kernel.org/show_bug.cgi?id=222020
>>>>>>
>>>>>> Fixes: 1da177e4c3f4 ("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>
>>>>>> ---
>>>>>>     v1->v2:
>>>>>>      1. Add 'mddev->pers != NULL' check before make_request
>>>>>>      2. Delete dm-raid caller(->dtr) modifications
>>>>>>      3. Move memalloc_noio_restore after mddev_unlock_and_resume
>>>>>>     v2->v3:
>>>>>>      1. Remove modifications in array_state_store()
>>>>>>      2. update commit msg
>>>>>>     drivers/md/md.c | 15 +++++++++++++++
>>>>>>     1 file changed, 15 insertions(+)
>>>>>>
>>>>>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>>>>>> index 680b34a63cb3..e73338c26d53 100644
>>>>>> --- a/drivers/md/md.c
>>>>>> +++ b/drivers/md/md.c
>>>>>> @@ -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) {
>>>>> Isn't this case already handled by md_submit_bio(). take a look in
>>>>> md_submit_bio()
>>>> Hi,Abd-Alrhman
>>>> I understand that 'mddev == NULL || mddev->pers == NULL' in
>>>> md_submit_bio() is a qiuck check, there still exists a small race window:
>>>>         P1                     P2
>>>> md_submit_bio
>>>>     if (mddev == NULL || mddev->pers == NULL)
>>>>                            md_ioctl
>>>>                             mddev_suspend_and_lock
>>>>                             do_md_stop->__md_stop
>>>>                             // set mddev->pers/private as NULL
>>>>                             mddev_unlock_and_resume
>>>>     md_handle_request
>>>>      if (is_suspended(mddev, bio))
>>>>      percpu_ref_tryget_live(&mddev->active_io)
>>>>      mddev->pers // null-ptr-deref
>>> It makes sense. It would solve only the null pointer reference, but I
>>> see another issue. Look at the comment before calling
>>> mddev_set_closing_and_sync_blockdev() in md_ioctl(). it says "Need to
>>> flush page cache, and ensure no-one else opens and writes". The
>>> suspending happens after flushing the page cache, in this case,
>>> there might be other writes in-flight.
>> At present, the current implementation does not align with the expected
>> annotations. Yu Kai privately gave me a feasible suggestion, which is to
>> have md_ioctl operate on a character device(like /dev/mapper/control)
>> instead of an md block device. This fix patch will serve as a temporary
>> solution before the official implementation is merged. By the way, I
>> don't have much time to implment it, do you have time to deal with the
>> official solution?
> Thanks for the summary. I can take on the official implementation.
> Before I get started, could we align on the interface details and any
> other relevant considerations?

I think the following procedures(details should be considered more):

1) add a new char device in mdraid to issue ioctl to array;
2) update mdadm to use the new ioctl;
3) a kernel message to warn the old block device ioctl is deprecated, and suggest
user to upgrade mdadm;
4) Since the new ioctl will introduce UAPI change, we should wait for a long time,
perhaps more than 1 year, before the deprecated old ioctl code in kernel can be removed.

>
>>>>>> +		/*
>>>>>> +		 * 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)
>>>>>> @@ -8299,6 +8313,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;
>>>>>> -- 
>>>>>> 2.52.0
>>>>>>
-- 
Thanks,
Kuai

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3] md: Fix the null-ptr-deref of 'mddev->private' while submitting IO
  2026-09-18  9:30           ` yu kuai
@ 2026-09-18  9:41             ` Abd-Alrhman Masalkhi
  0 siblings, 0 replies; 8+ messages in thread
From: Abd-Alrhman Masalkhi @ 2026-09-18  9:41 UTC (permalink / raw)
  To: yu kuai, Zhihao Cheng, song, xiao, magiclinan, eadavis, yu kuai
  Cc: linux-raid, linux-kernel, yangerkun, yi.zhang


Hi Kuai,

On Fri, Sep 18, 2026 at 17:30 +0800, yu kuai wrote:
> Hi,
>
> 在 2026/9/18 16:54, Abd-Alrhman Masalkhi 写道:
>> Hi Zhihao,
>>
>> On Fri, Sep 18, 2026 at 16:34 +0800, Zhihao Cheng wrote:
>>> 在 2026/9/18 16:20, Abd-Alrhman Masalkhi 写道:
>>>> On Fri, Sep 18, 2026 at 15:38 +0800, Zhihao Cheng wrote:
>>>>> 在 2026/9/18 15:29, Abd-Alrhman Masalkhi 写道:
>>>>>> Hi Zhihao,
>>>>>>
>>>>>> On Fri, Sep 18, 2026 at 15:09 +0800, Zhihao Cheng wrote:
>>>>>>> Concurrent processes md_stop and IO submitting could trigger a
>>>>>>> null-ptr-deref 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
>>>>>>>
>>>>>>>                  P1
>>>>>>>      fd = open(/dev/md0, O_RDWR)
>>>>>>> 	                      P2 (forked from P1, fd' <= fd)
>>>>>>>      write(fd)
>>>>>>>       submit_bio
>>>>>>>        md_handle_request
>>>>>>>         raid1_make_request
>>>>>>>          raid1_write_request
>>>>>>>                                      ioctl(fd, STOP_ARRAY)
>>>>>>> 		                   mddev_set_closing_and_sync_blockdev
>>>>>>> 				   // check passed, mddev->openers = 1,
>>>>>>> 				   // because md_open() is only called
>>>>>>> 				   // once in P1->open
>>>>>>> 				   do_md_stop
>>>>>>> 				    __md_stop
>>>>>>> 				     mddev->private = NULL
>>>>>>>
>>>>>>>           conf = mddev->private // NULL
>>>>>>>            wait_barrier(conf, sector) // null-ptr-deref !
>>>>>>>
>>>>>>> It is a common problem for raid0/1/10/5, and __md_stop could be triggered
>>>>>>> by several paths(eg. ioctl, sysfs, ->dtr). Fix it by replacing
>>>>>>> mddev_lock() with mddev_suspend_and_lock() for all __md_stop() callers.
>>>>>>> The caller dm_table_destroy() is guaranteed being invoked with device
>>>>>>> suspended, so raid_dtr() could keep using mddev_lock_nointr().
>>>>>>> The caller array_state_store() is guaranteed by the check
>>>>>>> mddev_set_closing_and_sync_blockdev(mddev, 0). For example, someone open
>>>>>>> /dev/mdx, write something and close /dev/mdx, it won't trigger the
>>>>>>> problem, all dirty pages can be flushed before mddev->openers decrement.
>>>>>>> Besides, fail the submitting IO in md_handle_request() if the
>>>>>>> 'mddev->pers' becomes NULL.
>>>>>>>
>>>>>>> Fetch a reproducer in https://bugzilla.kernel.org/show_bug.cgi?id=222020
>>>>>>>
>>>>>>> Fixes: 1da177e4c3f4 ("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>
>>>>>>> ---
>>>>>>>     v1->v2:
>>>>>>>      1. Add 'mddev->pers != NULL' check before make_request
>>>>>>>      2. Delete dm-raid caller(->dtr) modifications
>>>>>>>      3. Move memalloc_noio_restore after mddev_unlock_and_resume
>>>>>>>     v2->v3:
>>>>>>>      1. Remove modifications in array_state_store()
>>>>>>>      2. update commit msg
>>>>>>>     drivers/md/md.c | 15 +++++++++++++++
>>>>>>>     1 file changed, 15 insertions(+)
>>>>>>>
>>>>>>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>>>>>>> index 680b34a63cb3..e73338c26d53 100644
>>>>>>> --- a/drivers/md/md.c
>>>>>>> +++ b/drivers/md/md.c
>>>>>>> @@ -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) {
>>>>>> Isn't this case already handled by md_submit_bio(). take a look in
>>>>>> md_submit_bio()
>>>>> Hi,Abd-Alrhman
>>>>> I understand that 'mddev == NULL || mddev->pers == NULL' in
>>>>> md_submit_bio() is a qiuck check, there still exists a small race window:
>>>>>         P1                     P2
>>>>> md_submit_bio
>>>>>     if (mddev == NULL || mddev->pers == NULL)
>>>>>                            md_ioctl
>>>>>                             mddev_suspend_and_lock
>>>>>                             do_md_stop->__md_stop
>>>>>                             // set mddev->pers/private as NULL
>>>>>                             mddev_unlock_and_resume
>>>>>     md_handle_request
>>>>>      if (is_suspended(mddev, bio))
>>>>>      percpu_ref_tryget_live(&mddev->active_io)
>>>>>      mddev->pers // null-ptr-deref
>>>> It makes sense. It would solve only the null pointer reference, but I
>>>> see another issue. Look at the comment before calling
>>>> mddev_set_closing_and_sync_blockdev() in md_ioctl(). it says "Need to
>>>> flush page cache, and ensure no-one else opens and writes". The
>>>> suspending happens after flushing the page cache, in this case,
>>>> there might be other writes in-flight.
>>> At present, the current implementation does not align with the expected
>>> annotations. Yu Kai privately gave me a feasible suggestion, which is to
>>> have md_ioctl operate on a character device(like /dev/mapper/control)
>>> instead of an md block device. This fix patch will serve as a temporary
>>> solution before the official implementation is merged. By the way, I
>>> don't have much time to implment it, do you have time to deal with the
>>> official solution?
>> Thanks for the summary. I can take on the official implementation.
>> Before I get started, could we align on the interface details and any
>> other relevant considerations?
>
> I think the following procedures(details should be considered more):
>
> 1) add a new char device in mdraid to issue ioctl to array;
> 2) update mdadm to use the new ioctl;
> 3) a kernel message to warn the old block device ioctl is deprecated, and suggest
> user to upgrade mdadm;
> 4) Since the new ioctl will introduce UAPI change, we should wait for a long time,
> perhaps more than 1 year, before the deprecated old ioctl code in kernel can be removed.
>

Thanks, I’ll start working on the implementation.

>>
>>>>>>> +		/*
>>>>>>> +		 * 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)
>>>>>>> @@ -8299,6 +8313,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;
>>>>>>> -- 
>>>>>>> 2.52.0
>>>>>>>
> -- 
> Thanks,
> Kuai

-- 
Best Regards,
Abd-Alrhman

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-09-18  9:41 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-18  7:09 [PATCH v3] md: Fix the null-ptr-deref of 'mddev->private' while submitting IO Zhihao Cheng
2026-09-18  7:29 ` Abd-Alrhman Masalkhi
2026-09-18  7:38   ` Zhihao Cheng
2026-09-18  8:20     ` Abd-Alrhman Masalkhi
2026-09-18  8:34       ` Zhihao Cheng
2026-09-18  8:54         ` Abd-Alrhman Masalkhi
2026-09-18  9:30           ` yu kuai
2026-09-18  9:41             ` Abd-Alrhman Masalkhi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®