Thread (17 messages) 17 messages, 3 authors, 22d ago

Re: [PATCH v2 2/3] md/raid5: fix leak and use-after-free in resize_stripes error path

From: sashiko-bot@kernel.org
Date: 2026-08-27 06:27:51

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Concurrent lockless read of `conf->disks` in IO and error paths races with synchronous `kfree(old_disks)`.
- [High] Memory leak of `stripe_head` pages (`nsh->dev[i].page`) when Step 4 of `resize_stripes()` fails.
--

commit 42a858726c79b14d53c892c406bd10d16e4932f8
Author: Cao Guanghui [off-list ref]

md/raid5: fix leak and use-after-free in resize_stripes error path

This commit attempts to fix a memory leak and a use-after-free issue
in the resize_stripes error path. It defers the conf->disks replacement
and performs it under mddev->lock to prevent concurrent reads in
raid5_status from seeing a freed array.

Cc: stable@vger.kernel.org
quoted hunk ↗ jump to hunk
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 4efaef622fc05..e481c48b45093 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
[ ... ]
quoted hunk ↗ jump to hunk
@@ -2685,8 +2683,20 @@ static int resize_stripes(struct r5conf *conf, int newsize)
 	}
 	/* critical section pass, GFP_NOIO no longer needed */
 
-	if (!err)
+	if (!err && ndisks) {
+		struct disk_info *old_disks = conf->disks;
+
+		spin_lock_irq(&conf->mddev->lock);
+		conf->disks = ndisks;
+		spin_unlock_irq(&conf->mddev->lock);
+		kfree(old_disks);
[Severity: Critical]
This is a pre-existing issue, but the patch's attempt to fix the conf->disks
use-after-free is incomplete and leaves critical race windows open. This
kfree() is synchronous and doesn't wait for an RCU grace period.

Because check_reshape() invokes resize_stripes() before reshape_position is
updated, chunk_aligned_read is still enabled. Does this allow
raid5_read_one_chunk() to run concurrently and read the freed conf->disks
array completely locklessly?

drivers/md/raid5.c:raid5_read_one_chunk() {
    ...
    rdev = conf->disks[dd_idx].replacement;
    if (!rdev || test_bit(Faulty, &rdev->flags) ||
        READ_ONCE(rdev->recovery_offset) < end_sector) {
        rdev = conf->disks[dd_idx].rdev;
    ...
}

Additionally, raid5_calc_degraded() iterates over conf->disks holding only
rcu_read_lock(). Will this synchronous kfree() cause a use-after-free for
those RCU readers as well?

drivers/md/raid5.c:raid5_calc_degraded() {
    ...
    struct md_rdev *rdev = READ_ONCE(conf->disks[i].rdev);
    ...
}
 		conf->pool_size = newsize;
+	} else if (ndisks) {
[Severity: High]
This isn't a bug introduced by this patch, but the patch attempts to fix
leaks in this error path and structurally misses this one. When Step 4 of
resize_stripes() fails to allocate a page for the new stripe slots
(nsh->dev[i].page), err is set to -ENOMEM and the pool_size update is
skipped here.

During array teardown, shrink_buffers() iterates only up to
sh->raid_conf->pool_size:

drivers/md/raid5.c:shrink_buffers() {
    ...
    int num = sh->raid_conf->pool_size;

#if PAGE_SIZE == DEFAULT_STRIPE_SIZE
    for (i = 0; i < num ; i++) {
    ...
}

Are the successfully allocated pages at indices >= pool_size permanently
leaked when free_stripe() destroys the slab object, because the cleanup loop
misses them?
+		for (i = conf->pool_size; i < newsize; i++)
+			if (ndisks[i].extra_page)
+				put_page(ndisks[i].extra_page);
+		kfree(ndisks);
+	}
 	mutex_unlock(&conf->cache_size_mutex);
 
 	return err;
-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260827060554.77849-1-ghuicao@163.com?part=2
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help