| From foo@baz Sun May 27 17:33:38 CEST 2018 |
| From: Yufen Yu <yuyufen@huawei.com> |
| Date: Tue, 6 Feb 2018 17:39:15 +0800 |
| Subject: md raid10: fix NULL deference in handle_write_completed() |
| |
| From: Yufen Yu <yuyufen@huawei.com> |
| |
| [ Upstream commit 01a69cab01c184d3786af09e9339311123d63d22 ] |
| |
| In the case of 'recover', an r10bio with R10BIO_WriteError & |
| R10BIO_IsRecover will be progressed by handle_write_completed(). |
| This function traverses all r10bio->devs[copies]. |
| If devs[m].repl_bio != NULL, it thinks conf->mirrors[dev].replacement |
| is also not NULL. However, this is not always true. |
| |
| When there is an rdev of raid10 has replacement, then each r10bio |
| ->devs[m].repl_bio != NULL in conf->r10buf_pool. However, in 'recover', |
| even if corresponded replacement is NULL, it doesn't clear r10bio |
| ->devs[m].repl_bio, resulting in replacement NULL deference. |
| |
| This bug was introduced when replacement support for raid10 was |
| added in Linux 3.3. |
| |
| As NeilBrown suggested: |
| Elsewhere the determination of "is this device part of the |
| resync/recovery" is made by resting bio->bi_end_io. |
| If this is end_sync_write, then we tried to write here. |
| If it is NULL, then we didn't try to write. |
| |
| Fixes: 9ad1aefc8ae8 ("md/raid10: Handle replacement devices during resync.") |
| Cc: stable (V3.3+) |
| Suggested-by: NeilBrown <neilb@suse.com> |
| Signed-off-by: Yufen Yu <yuyufen@huawei.com> |
| Signed-off-by: Shaohua Li <sh.li@alibaba-inc.com> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/md/raid10.c | 6 ++++-- |
| 1 file changed, 4 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/md/raid10.c |
| +++ b/drivers/md/raid10.c |
| @@ -2636,7 +2636,8 @@ static void handle_write_completed(struc |
| for (m = 0; m < conf->copies; m++) { |
| int dev = r10_bio->devs[m].devnum; |
| rdev = conf->mirrors[dev].rdev; |
| - if (r10_bio->devs[m].bio == NULL) |
| + if (r10_bio->devs[m].bio == NULL || |
| + r10_bio->devs[m].bio->bi_end_io == NULL) |
| continue; |
| if (!r10_bio->devs[m].bio->bi_error) { |
| rdev_clear_badblocks( |
| @@ -2651,7 +2652,8 @@ static void handle_write_completed(struc |
| md_error(conf->mddev, rdev); |
| } |
| rdev = conf->mirrors[dev].replacement; |
| - if (r10_bio->devs[m].repl_bio == NULL) |
| + if (r10_bio->devs[m].repl_bio == NULL || |
| + r10_bio->devs[m].repl_bio->bi_end_io == NULL) |
| continue; |
| |
| if (!r10_bio->devs[m].repl_bio->bi_error) { |