* [PATCH] md/raid5: set pool_size before extra_page allocation to fix leak on error path
@ 2026-08-27 2:38 ghuicao
2026-08-27 2:53 ` sashiko-bot
` (2 more replies)
0 siblings, 3 replies; 17+ messages in thread
From: ghuicao @ 2026-08-27 2:38 UTC (permalink / raw)
To: Song Liu
Cc: Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel, stable,
Cao Guanghui
From: Cao Guanghui <caoguanghui@kylinos.cn>
In setup_conf(), conf->disks is allocated with max_disks slots and
extra_page is allocated for each slot. However, pool_size remains 0
(uninitialized from kzalloc) until grow_stripes() sets it later. If
any allocation or initialization between the extra_page loop and
grow_stripes() fails and jumps to abort, free_conf() iterates
pool_size (= 0) times and skips the extra_page freeing loop entirely,
leaking max_disks pages.
Set pool_size to max_disks right after the disks array allocation
succeeds, so that free_conf() correctly frees all allocated
extra_page entries on any error path. This is safe because:
- If kzalloc_objs(disks) fails, pool_size stays 0 and free_conf
skips the loop (kfree(NULL) is safe).
- grow_stripes() later sets pool_size = devs, which equals max_disks,
so the early assignment does not change the final value.
- resize_stripes() only updates pool_size on success and reallocates
the disks array in lockstep.
Fixes: d7bd398e97f2 ("md/r5cache: handle alloc_page failure")
Cc: stable@vger.kernel.org
Signed-off-by: Cao Guanghui <caoguanghui@kylinos.cn>
---
drivers/md/raid5.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -7732,8 +7732,9 @@ static struct r5conf *setup_conf(struct mddev *mddev)
conf->disks = kzalloc_objs(struct disk_info, max_disks);
if (!conf->disks)
goto abort;
+ conf->pool_size = max_disks;
for (i = 0; i < max_disks; i++) {
conf->disks[i].extra_page = alloc_page(GFP_KERNEL);
if (!conf->disks[i].extra_page)
--
2.34.1
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] md/raid5: set pool_size before extra_page allocation to fix leak on error path
2026-08-27 2:38 [PATCH] md/raid5: set pool_size before extra_page allocation to fix leak on error path ghuicao
@ 2026-08-27 2:53 ` sashiko-bot
2026-08-27 6:05 ` [PATCH v2 1/3] " ghuicao
2026-08-27 6:32 ` [PATCH v3 " ghuicao
2 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-27 2:53 UTC (permalink / raw)
To: ghuicao; +Cc: linux-raid, yukuai
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Memory leak of extra_page and stripe pages during raid5 reshape error path.
- [High] Kernel panic due to NULL pointer dereference in `raid5_free_percpu()` when handling hotplug callback allocation failures.
--
commit e2a9b5ec799de7e8af41dc93ccf252fa2539b0d8
Author: Cao Guanghui <caoguanghui@kylinos.cn>
md/raid5: set pool_size before extra_page allocation to fix leak on error path
Sets conf->pool_size right after the conf->disks array is allocated in
setup_conf(). This ensures that if subsequent initialization fails and
jumps to the abort label, free_conf() uses the correct pool size to clean
up the allocated extra_page entries, preventing a memory leak.
Cc: stable@vger.kernel.org
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index b91545ce090de..4efaef622fc05 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -7733,6 +7733,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
>
> if (!conf->disks)
> goto abort;
> + conf->pool_size = max_disks;
>
> for (i = 0; i < max_disks; i++) {
> conf->disks[i].extra_page = alloc_page(GFP_KERNEL);
[Severity: High]
This isn't a bug introduced by this patch, but it exists in the surrounding
reshape logic. Does this same stale conf->pool_size issue leak memory in
resize_stripes()?
If alloc_page(GFP_NOIO) fails in step 4 of resize_stripes(), the function
returns -ENOMEM but fails to update conf->pool_size because it is gated by
if (!err).
However, conf->disks was already permanently replaced in step 3, and new
pages were already allocated for the newly added slots:
drivers/md/raid5.c:resize_stripes()
} else {
kfree(conf->disks);
conf->disks = ndisks;
}
Later in the function:
if (!p)
err = -ENOMEM;
}
if (!err)
conf->pool_size = newsize;
mutex_unlock(&conf->cache_size_mutex);
return err;
Because conf->pool_size is not updated on error, subsequent teardown paths
like free_conf() and shrink_stripes() will use the old boundary and fail to
free the memory allocated for the new slots. Could this exhaust kernel
memory over multiple failed reshape attempts?
[Severity: High]
This is a pre-existing issue on the setup_conf() error path, but could
the abort path trigger a kernel panic due to a null pointer dereference
in raid5_free_percpu()?
If cpuhp_state_add_instance() fails in raid5_alloc_percpu(), the function
returns the error without cleaning up conf->percpu:
drivers/md/raid5.c:raid5_alloc_percpu()
err = cpuhp_state_add_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
if (!err) {
When setup_conf() receives the error and jumps to the abort label, free_conf()
calls raid5_free_percpu(). Because conf->percpu is not null, it incorrectly
assumes the hotplug node was hashed:
drivers/md/raid5.c:raid5_free_percpu()
if (!conf->percpu)
return;
cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
Since the node was never actually added, hlist_del() in the hotplug state
removal blindly unlinks the node, dereferencing node->pprev which is null:
kernel/cpu.c:__cpuhp_state_remove_instance()
remove:
hlist_del(node);
Can this crash the kernel during raid array initialization or hotplug events
if memory allocation fails in the hotplug subsystem?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260827023803.36602-1-ghuicao@163.com?part=1
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 1/3] md/raid5: set pool_size before extra_page allocation to fix leak on error path
2026-08-27 2:38 [PATCH] md/raid5: set pool_size before extra_page allocation to fix leak on error path ghuicao
2026-08-27 2:53 ` sashiko-bot
@ 2026-08-27 6:05 ` ghuicao
2026-08-27 6:05 ` [PATCH v2 2/3] md/raid5: fix leak and use-after-free in resize_stripes " ghuicao
` (2 more replies)
2026-08-27 6:32 ` [PATCH v3 " ghuicao
2 siblings, 3 replies; 17+ messages in thread
From: ghuicao @ 2026-08-27 6:05 UTC (permalink / raw)
To: Song Liu
Cc: Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel, stable,
Cao Guanghui
From: Cao Guanghui <caoguanghui@kylinos.cn>
In setup_conf(), conf->disks is allocated with max_disks slots and
extra_page is allocated for each slot. However, pool_size remains 0
(uninitialized from kzalloc) until grow_stripes() sets it later. If
any allocation or initialization between the extra_page loop and
grow_stripes() fails and jumps to abort, free_conf() iterates
pool_size (= 0) times and skips the extra_page freeing loop entirely,
leaking max_disks pages.
Set pool_size to max_disks right after the disks array allocation
succeeds, so that free_conf() correctly frees all allocated
extra_page entries on any error path. This is safe because:
- If kzalloc_objs(disks) fails, pool_size stays 0 and free_conf
skips the loop (kfree(NULL) is safe).
- grow_stripes() later sets pool_size = devs, which equals max_disks,
so the early assignment does not change the final value.
- resize_stripes() only updates pool_size on success and reallocates
the disks array in lockstep.
Fixes: d7bd398e97f2 ("md/r5cache: handle alloc_page failure")
Cc: stable@vger.kernel.org
Signed-off-by: Cao Guanghui <caoguanghui@kylinos.cn>
---
drivers/md/raid5.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -7732,8 +7732,9 @@ static struct r5conf *setup_conf(struct mddev *mddev)
conf->disks = kzalloc_objs(struct disk_info, max_disks);
if (!conf->disks)
goto abort;
+ conf->pool_size = max_disks;
for (i = 0; i < max_disks; i++) {
conf->disks[i].extra_page = alloc_page(GFP_KERNEL);
if (!conf->disks[i].extra_page)
--
2.34.1
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 2/3] md/raid5: fix leak and use-after-free in resize_stripes error path
2026-08-27 6:05 ` [PATCH v2 1/3] " ghuicao
@ 2026-08-27 6:05 ` ghuicao
2026-08-27 6:27 ` sashiko-bot
2026-08-27 6:05 ` [PATCH v2 3/3] md/raid5: fix NULL pointer dereference in raid5_free_percpu ghuicao
2026-08-27 6:27 ` [PATCH v2 1/3] md/raid5: set pool_size before extra_page allocation to fix leak on error path sashiko-bot
2 siblings, 1 reply; 17+ messages in thread
From: ghuicao @ 2026-08-27 6:05 UTC (permalink / raw)
To: Song Liu
Cc: Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel, stable,
Cao Guanghui
From: Cao Guanghui <caoguanghui@kylinos.cn>
resize_stripes() has two issues in how conf->disks is replaced:
1. Memory leak: conf->disks is replaced with ndisks in Step 3, but
pool_size is only updated at the end with "if (!err)". If Step 4
(allocating pages for new stripe slots) fails, pool_size retains the
old value. On teardown, free_conf() iterates only pool_size entries,
leaking (newsize - pool_size) extra_page allocations.
2. Use-after-free: conf->disks is freed and replaced without holding
mddev->lock, while raid5_status() (called from /proc/mdstat via
md_seq_show) reads conf->disks[i].rdev under mddev->lock. The
freeing and replacement happen under reconfig_mutex and
cache_size_mutex, which do not exclude mddev->lock holders.
Fix both by deferring the conf->disks replacement until after Step 4
succeeds, and performing the pointer swap under mddev->lock so that
concurrent readers in raid5_status() see either the old or new array,
never a freed one. If Step 4 fails, ndisks is freed instead.
This also preserves the original retry behavior: pool_size is only
updated on full success, so check_reshape() correctly calls
resize_stripes() again on retry.
Fixes: ad01c9e3752f ("[PATCH] md: Allow stripes to be expanded in preparation for expanding an array")
Cc: stable@vger.kernel.org
Signed-off-by: Cao Guanghui <caoguanghui@kylinos.cn>
---
Changes in v2:
- Defer conf->disks replacement to after Step 4 instead of setting
pool_size early, which would break reshape retry logic (Sashiko)
- Add spinlock protection around the pointer swap to fix a concurrent
use-after-free in raid5_status() (Sashiko)
drivers/md/raid5.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -2639,9 +2639,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
if (ndisks[i].extra_page)
put_page(ndisks[i].extra_page);
kfree(ndisks);
- } else {
- kfree(conf->disks);
- conf->disks = ndisks;
+ ndisks = NULL;
}
} else
err = -ENOMEM;
@@ -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);
conf->pool_size = newsize;
+ } else if (ndisks) {
+ 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;
--
2.34.1
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 3/3] md/raid5: fix NULL pointer dereference in raid5_free_percpu
2026-08-27 6:05 ` [PATCH v2 1/3] " ghuicao
2026-08-27 6:05 ` [PATCH v2 2/3] md/raid5: fix leak and use-after-free in resize_stripes " ghuicao
@ 2026-08-27 6:05 ` ghuicao
2026-08-27 6:18 ` sashiko-bot
2026-08-27 6:27 ` [PATCH v2 1/3] md/raid5: set pool_size before extra_page allocation to fix leak on error path sashiko-bot
2 siblings, 1 reply; 17+ messages in thread
From: ghuicao @ 2026-08-27 6:05 UTC (permalink / raw)
To: Song Liu
Cc: Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel, stable,
Cao Guanghui
From: Cao Guanghui <caoguanghui@kylinos.cn>
If cpuhp_state_add_instance() fails in raid5_alloc_percpu() (e.g., the
startup callback raid456_cpu_up_prepare fails due to an allocation
failure), conf->node is never added to the cpuhp instance list and
its pprev remains NULL (from kzalloc initialization).
When setup_conf() then jumps to abort, free_conf() calls
raid5_free_percpu(), which checks conf->percpu (non-NULL, since it was
allocated before the cpuhp failure) and proceeds to call
cpuhp_state_remove_instance(). This calls hlist_del() on the unhashed
node, which dereferences node->pprev (NULL), causing a kernel panic.
Guard the removal with hlist_unhashed() so that the cpuhp instance is
only removed if it was actually added.
Fixes: 29c6d1bbd7a2 ("md/raid5: Convert to hotplug state machine")
Cc: stable@vger.kernel.org
Signed-off-by: Cao Guanghui <caoguanghui@kylinos.cn>
---
drivers/md/raid5.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -7539,7 +7539,8 @@ static void raid5_free_percpu(struct r5conf *conf)
{
if (!conf->percpu)
return;
- cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
+ if (!hlist_unhashed(&conf->node))
+ cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
free_percpu(conf->percpu);
}
--
2.34.1
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 3/3] md/raid5: fix NULL pointer dereference in raid5_free_percpu
2026-08-27 6:05 ` [PATCH v2 3/3] md/raid5: fix NULL pointer dereference in raid5_free_percpu ghuicao
@ 2026-08-27 6:18 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-27 6:18 UTC (permalink / raw)
To: ghuicao; +Cc: linux-raid, yukuai
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Data race when checking hlist_unhashed(&conf->node) locklessly.
--
commit f000e30dfd8bae5a7346baf7add33f6d2b5316e6
Author: Cao Guanghui <caoguanghui@kylinos.cn>
md/raid5: fix NULL pointer dereference in raid5_free_percpu
This commit adds a check in raid5_free_percpu() to only remove the CPU
hotplug instance if conf->node is hashed, preventing a NULL pointer
dereference on its pprev pointer during error teardown when cpuhp
registration fails.
Cc: stable@vger.kernel.org
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index e481c48b45093..537225c14a236 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -7548,7 +7548,8 @@ static void raid5_free_percpu(struct r5conf *conf)
> if (!conf->percpu)
> return;
>
> - cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
> + if (!hlist_unhashed(&conf->node))
[Severity: Medium]
Does checking hlist_unhashed() locklessly here introduce a data race?
If one RAID array is being destroyed, it calls raid5_free_percpu() and
evaluates !hlist_unhashed(&conf->node) without holding the
cpuhp_state_mutex.
Concurrently, if another array is being created, it might take the lock
and call hlist_add_head() on the shared CPUHP_MD_RAID5_PREPARE list.
If the destroying array happens to be at the head, hlist_add_head()
updates its pprev pointer via WRITE_ONCE().
This concurrent modification can violate the kernel memory model and
trigger KCSAN load-tearing warnings. Could hlist_unhashed_lockless(),
which uses READ_ONCE(), be used here instead to safely handle concurrent
list operations?
> + cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
> free_percpu(conf->percpu);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260827060554.77849-1-ghuicao@163.com?part=3
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/3] md/raid5: set pool_size before extra_page allocation to fix leak on error path
2026-08-27 6:05 ` [PATCH v2 1/3] " ghuicao
2026-08-27 6:05 ` [PATCH v2 2/3] md/raid5: fix leak and use-after-free in resize_stripes " ghuicao
2026-08-27 6:05 ` [PATCH v2 3/3] md/raid5: fix NULL pointer dereference in raid5_free_percpu ghuicao
@ 2026-08-27 6:27 ` sashiko-bot
2 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-27 6:27 UTC (permalink / raw)
To: ghuicao; +Cc: yukuai, linux-raid
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] NULL pointer dereference in log_exit() when early allocations in setup_conf() fail before conf->mddev is initialized.
--
commit 9327d7b2c0b735410bac76b4b55b188fa14875d3
Author: Cao Guanghui <caoguanghui@kylinos.cn>
md/raid5: set pool_size before extra_page allocation to fix leak on error path
In setup_conf(), conf->disks is allocated with max_disks slots and
extra_page is allocated for each slot. However, pool_size remains 0
(uninitialized from kzalloc) until grow_stripes() sets it later. This
fixes a memory leak by setting pool_size earlier.
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index b91545ce090de..4efaef622fc05 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -7733,6 +7733,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
>
> if (!conf->disks)
> goto abort;
> + conf->pool_size = max_disks;
>
> for (i = 0; i < max_disks; i++) {
> conf->disks[i].extra_page = alloc_page(GFP_KERNEL);
[Severity: High]
This is a pre-existing issue, but I noticed a potential NULL pointer
dereference on these early error paths.
If any early memory allocation in setup_conf() fails (such as
conf->pending_data, conf->disks, or alloc_page() for extra pages here),
the code jumps to the abort label and calls free_conf(conf). This happens
before conf->mddev is assigned.
free_conf() unconditionally calls log_exit(conf). Because conf->log is NULL
on these paths, log_exit() checks raid5_has_ppl():
drivers/md/raid5-log.h:log_exit() {
if (conf->log)
r5l_exit_log(conf);
else if (raid5_has_ppl(conf))
ppl_exit_log(conf);
}
This function evaluates test_bit(MD_HAS_PPL, &conf->mddev->flags), which
dereferences the uninitialized conf->mddev pointer:
drivers/md/raid5-log.h:raid5_has_ppl() {
return test_bit(MD_HAS_PPL, &conf->mddev->flags);
}
Can conf->mddev = mddev; be assigned immediately after conf is allocated in
setup_conf() to avoid this crash during early aborts?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260827060554.77849-1-ghuicao@163.com?part=1
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/3] md/raid5: fix leak and use-after-free in resize_stripes error path
2026-08-27 6:05 ` [PATCH v2 2/3] md/raid5: fix leak and use-after-free in resize_stripes " ghuicao
@ 2026-08-27 6:27 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-27 6:27 UTC (permalink / raw)
To: ghuicao; +Cc: yukuai, linux-raid
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 <caoguanghui@kylinos.cn>
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
> 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
[ ... ]
> @@ -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
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 1/3] md/raid5: set pool_size before extra_page allocation to fix leak on error path
2026-08-27 2:38 [PATCH] md/raid5: set pool_size before extra_page allocation to fix leak on error path ghuicao
2026-08-27 2:53 ` sashiko-bot
2026-08-27 6:05 ` [PATCH v2 1/3] " ghuicao
@ 2026-08-27 6:32 ` ghuicao
2026-08-27 6:32 ` [PATCH v3 2/3] md/raid5: fix leak and use-after-free in resize_stripes " ghuicao
` (2 more replies)
2 siblings, 3 replies; 17+ messages in thread
From: ghuicao @ 2026-08-27 6:32 UTC (permalink / raw)
To: Song Liu
Cc: Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel, stable,
Cao Guanghui
From: Cao Guanghui <caoguanghui@kylinos.cn>
In setup_conf(), conf->disks is allocated with max_disks slots and
extra_page is allocated for each slot. However, pool_size remains 0
(uninitialized from kzalloc) until grow_stripes() sets it later. If
any allocation or initialization between the extra_page loop and
grow_stripes() fails and jumps to abort, free_conf() iterates
pool_size (= 0) times and skips the extra_page freeing loop entirely,
leaking max_disks pages.
Set pool_size to max_disks right after the disks array allocation
succeeds, so that free_conf() correctly frees all allocated
extra_page entries on any error path. This is safe because:
- If kzalloc_objs(disks) fails, pool_size stays 0 and free_conf
skips the loop (kfree(NULL) is safe).
- grow_stripes() later sets pool_size = devs, which equals max_disks,
so the early assignment does not change the final value.
- resize_stripes() only updates pool_size on success and reallocates
the disks array in lockstep.
Fixes: d7bd398e97f2 ("md/r5cache: handle alloc_page failure")
Cc: stable@vger.kernel.org
Signed-off-by: Cao Guanghui <caoguanghui@kylinos.cn>
---
drivers/md/raid5.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -7732,8 +7732,9 @@ static struct r5conf *setup_conf(struct mddev *mddev)
conf->disks = kzalloc_objs(struct disk_info, max_disks);
if (!conf->disks)
goto abort;
+ conf->pool_size = max_disks;
for (i = 0; i < max_disks; i++) {
conf->disks[i].extra_page = alloc_page(GFP_KERNEL);
if (!conf->disks[i].extra_page)
--
2.34.1
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 2/3] md/raid5: fix leak and use-after-free in resize_stripes error path
2026-08-27 6:32 ` [PATCH v3 " ghuicao
@ 2026-08-27 6:32 ` ghuicao
2026-08-27 7:02 ` sashiko-bot
2026-08-27 6:32 ` [PATCH v3 3/3] md/raid5: fix NULL pointer dereference in raid5_free_percpu ghuicao
2026-08-27 8:03 ` [PATCH v4 1/2] md/raid5: track disks array size to fix extra_page leak on error paths ghuicao
2 siblings, 1 reply; 17+ messages in thread
From: ghuicao @ 2026-08-27 6:32 UTC (permalink / raw)
To: Song Liu
Cc: Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel, stable,
Cao Guanghui
From: Cao Guanghui <caoguanghui@kylinos.cn>
resize_stripes() has two issues in how conf->disks is replaced:
1. Memory leak: conf->disks is replaced with ndisks in Step 3, but
pool_size is only updated at the end with "if (!err)". If Step 4
(allocating pages for new stripe slots) fails, pool_size retains the
old value. On teardown, free_conf() iterates only pool_size entries,
leaking (newsize - pool_size) extra_page allocations.
2. Use-after-free: conf->disks is freed and replaced without holding
mddev->lock, while raid5_status() (called from /proc/mdstat via
md_seq_show) reads conf->disks[i].rdev under mddev->lock. The
freeing and replacement happen under reconfig_mutex and
cache_size_mutex, which do not exclude mddev->lock holders.
Fix both by deferring the conf->disks replacement until after Step 4
succeeds, and performing the pointer swap under mddev->lock so that
concurrent readers in raid5_status() see either the old or new array,
never a freed one. If Step 4 fails, ndisks is freed instead.
This also preserves the original retry behavior: pool_size is only
updated on full success, so check_reshape() correctly calls
resize_stripes() again on retry.
Fixes: ad01c9e3752f ("[PATCH] md: Allow stripes to be expanded in preparation for expanding an array")
Cc: stable@vger.kernel.org
Signed-off-by: Cao Guanghui <caoguanghui@kylinos.cn>
---
Changes in v2:
- Defer conf->disks replacement to after Step 4 instead of setting
pool_size early, which would break reshape retry logic (Sashiko)
- Add spinlock protection around the pointer swap to fix a concurrent
use-after-free in raid5_status() (Sashiko)
drivers/md/raid5.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -2639,9 +2639,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
if (ndisks[i].extra_page)
put_page(ndisks[i].extra_page);
kfree(ndisks);
- } else {
- kfree(conf->disks);
- conf->disks = ndisks;
+ ndisks = NULL;
}
} else
err = -ENOMEM;
@@ -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);
conf->pool_size = newsize;
+ } else if (ndisks) {
+ 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;
--
2.34.1
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 3/3] md/raid5: fix NULL pointer dereference in raid5_free_percpu
2026-08-27 6:32 ` [PATCH v3 " ghuicao
2026-08-27 6:32 ` [PATCH v3 2/3] md/raid5: fix leak and use-after-free in resize_stripes " ghuicao
@ 2026-08-27 6:32 ` ghuicao
2026-08-27 7:17 ` sashiko-bot
2026-08-27 8:03 ` [PATCH v4 1/2] md/raid5: track disks array size to fix extra_page leak on error paths ghuicao
2 siblings, 1 reply; 17+ messages in thread
From: ghuicao @ 2026-08-27 6:32 UTC (permalink / raw)
To: Song Liu
Cc: Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel, stable,
Cao Guanghui
From: Cao Guanghui <caoguanghui@kylinos.cn>
If cpuhp_state_add_instance() fails in raid5_alloc_percpu() (e.g., the
startup callback raid456_cpu_up_prepare fails due to an allocation
failure), conf->node is never added to the cpuhp instance list and
its pprev remains NULL (from kzalloc initialization).
When setup_conf() then jumps to abort, free_conf() calls
raid5_free_percpu(), which checks conf->percpu (non-NULL, since it was
allocated before the cpuhp failure) and proceeds to call
cpuhp_state_remove_instance(). This calls hlist_del() on the unhashed
node, which dereferences node->pprev (NULL), causing a kernel panic.
Guard the removal with hlist_unhashed_lockless() so that the cpuhp
instance is only removed if it was actually added. The lockless variant
uses READ_ONCE() for the pprev read, which is safe here because the
actual removal is synchronized by cpuhp_state_mutex inside
cpuhp_state_remove_instance().
Fixes: 29c6d1bbd7a2 ("md/raid5: Convert to hotplug state machine")
Cc: stable@vger.kernel.org
Signed-off-by: Cao Guanghui <caoguanghui@kylinos.cn>
---
Changes in v2:
- Use hlist_unhashed_lockless() instead of hlist_unhashed() to avoid
a KCSAN data race warning when another array is concurrently added
to the same cpuhp instance list (Sashiko)
drivers/md/raid5.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -7539,7 +7539,8 @@ static void raid5_free_percpu(struct r5conf *conf)
{
if (!conf->percpu)
return;
- cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
+ if (!hlist_unhashed_lockless(&conf->node))
+ cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
free_percpu(conf->percpu);
}
--
2.34.1
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 2/3] md/raid5: fix leak and use-after-free in resize_stripes error path
2026-08-27 6:32 ` [PATCH v3 2/3] md/raid5: fix leak and use-after-free in resize_stripes " ghuicao
@ 2026-08-27 7:02 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-27 7:02 UTC (permalink / raw)
To: ghuicao; +Cc: yukuai, linux-raid
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Critical] Moving the replacement and freeing of `conf->disks` to after Step 4 introduces a severe Use-After-Free race with the main IO thread (`raid5d`).
Pre-existing issues:
- [High] Incomplete UAF fix: `conf->disks` is still vulnerable to a concurrent use-after-free by `raid5_error()` and `raid5_calc_degraded()`.
--
commit bd1f3c6216c041887763e5099827c3cd4343c423
Author: Cao Guanghui <caoguanghui@kylinos.cn>
md/raid5: fix leak and use-after-free in resize_stripes error path
This commit addresses a memory leak when stripe slot allocation fails, and
attempts to fix a concurrent use-after-free by swapping the conf->disks array
under mddev->lock. The array replacement is deferred until after new stripe
slots are successfully allocated.
> 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
[ ... ]
> @@ -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]
By deferring the kfree() of old_disks until after Step 4, does this code
introduce a race with the main IO thread?
During Step 4 (just above this code), resize_stripes() calls
raid5_release_stripe(), which returns stripes to service and wakes up the
raid5d IO thread.
Once awakened, raid5d resumes processing stripes and will locklessly access
the disks array:
raid5d()
handle_stripe()
analyse_stripe()
rdev = conf->disks[i].replacement;
Because raid5d does not acquire mddev->lock, won't it dereference the freed
old_disks array when this code executes concurrently?
[Severity: High]
This isn't a bug introduced by this patch, but does protecting the pointer
swap with mddev->lock completely prevent concurrent use-after-free of
conf->disks?
The commit message notes that raid5_status() reads conf->disks under
mddev->lock. However, a concurrent block IO error can trigger raid5_error(),
which calls raid5_calc_degraded():
raid5_error()
raid5_calc_degraded()
struct md_rdev *rdev = READ_ONCE(conf->disks[i].rdev);
In raid5_error(), this is protected by conf->device_lock, not mddev->lock.
If raid5_error() executes concurrently with this kfree(), won't it
dereference the freed old_disks array?
> conf->pool_size = newsize;
> + } else if (ndisks) {
> + 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/20260827063235.85375-1-ghuicao@163.com?part=2
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 3/3] md/raid5: fix NULL pointer dereference in raid5_free_percpu
2026-08-27 6:32 ` [PATCH v3 3/3] md/raid5: fix NULL pointer dereference in raid5_free_percpu ghuicao
@ 2026-08-27 7:17 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-27 7:17 UTC (permalink / raw)
To: ghuicao; +Cc: yukuai, linux-raid
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing workqueue flush before freeing worker structures during array teardown.
--
commit 7455143ee1b52af0e50fb9ef4f12b3a32d0dc0c2
Author: Cao Guanghui <caoguanghui@kylinos.cn>
md/raid5: fix NULL pointer dereference in raid5_free_percpu
If cpuhp_state_add_instance() fails in raid5_alloc_percpu(), conf->node is
never added to the cpuhp instance list and its pprev remains NULL. This
patch guards the removal with hlist_unhashed_lockless() so that the cpuhp
instance is only removed if it was actually added, preventing a kernel panic.
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index e481c48b45093..31417c91ab248 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -7548,7 +7548,8 @@ static void raid5_free_percpu(struct r5conf *conf)
> if (!conf->percpu)
> return;
>
> - cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
> + if (!hlist_unhashed_lockless(&conf->node))
> + cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
> free_percpu(conf->percpu);
> }
While reviewing the array teardown path modified here, I noticed a separate
issue in the nearby free_thread_groups() function:
drivers/md/raid5.c:free_thread_groups() {
...
if (conf->worker_groups)
kfree(conf->worker_groups[0].workers);
kfree(conf->worker_groups);
...
}
[Severity: High]
This isn't a bug introduced by this patch, but is a pre-existing issue.
Does this sequence miss a workqueue flush before freeing the worker structures?
During array teardown in md_stop(), raid5_quiesce() waits for active_stripes
to reach 0. This decrement and the subsequent wake_up(&conf->wait_for_quiescent)
can occur inside release_inactive_stripe_list(). This is called by
handle_active_stripes() (executed by raid5_do_work() on the worker thread)
after temporarily dropping conf->device_lock.
Once woken, raid5_quiesce() finishes and teardown proceeds to
free_thread_groups(). This function frees the memory backing worker->work
without calling flush_workqueue() or cancel_work_sync().
Meanwhile, the worker thread in raid5_do_work() could still be executing its
trailing loop and workqueue core cleanup code. Could this result in a
use-after-free of the worker structures, leading to memory corruption or a
kernel panic?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260827063235.85375-1-ghuicao@163.com?part=3
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v4 1/2] md/raid5: track disks array size to fix extra_page leak on error paths
2026-08-27 6:32 ` [PATCH v3 " ghuicao
2026-08-27 6:32 ` [PATCH v3 2/3] md/raid5: fix leak and use-after-free in resize_stripes " ghuicao
2026-08-27 6:32 ` [PATCH v3 3/3] md/raid5: fix NULL pointer dereference in raid5_free_percpu ghuicao
@ 2026-08-27 8:03 ` ghuicao
2026-08-27 8:03 ` [PATCH v4 2/2] md/raid5: fix NULL pointer dereference in raid5_free_percpu ghuicao
` (2 more replies)
2 siblings, 3 replies; 17+ messages in thread
From: ghuicao @ 2026-08-27 8:03 UTC (permalink / raw)
To: Song Liu
Cc: Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel, stable,
Cao Guanghui
From: Cao Guanghui <caoguanghui@kylinos.cn>
free_conf() iterates conf->pool_size entries to free extra_page
allocations, but pool_size may not reflect the actual size of the
conf->disks array. Two scenarios cause a mismatch:
1. setup_conf() early abort: pool_size is 0 (not yet set by
grow_stripes) but conf->disks has max_disks entries with
extra_page allocated. The loop iterates 0 times, leaking all
pages.
2. resize_stripes() Step 4 failure: conf->disks was replaced with
a newsize-entry array in Step 3, but pool_size is only updated
on success. The loop iterates pool_size (old, smaller value)
times, leaking (newsize - pool_size) pages.
Add a dedicated disks_cnt field to track the actual number of
entries in conf->disks. Set it immediately after each allocation
or replacement (in setup_conf and resize_stripes Step 3, where the
array is safely stalled with no concurrent access), and use it in
free_conf() instead of pool_size.
This leaves pool_size untouched, preserving the check_reshape()
retry behavior that depends on pool_size only being updated on
full success.
Fixes: d7bd398e97f2 ("md/r5cache: handle alloc_page failure")
Cc: stable@vger.kernel.org
Signed-off-by: Cao Guanghui <caoguanghui@kylinos.cn>
---
drivers/md/raid5.c | 4 +++-
drivers/md/raid5.h | 1 +
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -2642,6 +2642,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
} else {
kfree(conf->disks);
conf->disks = ndisks;
+ conf->disks_cnt = newsize;
}
} else
err = -ENOMEM;
@@ -7552,7 +7553,7 @@ static void free_conf(struct r5conf *conf)
free_thread_groups(conf);
shrink_stripes(conf);
raid5_free_percpu(conf);
- for (i = 0; i < conf->pool_size; i++)
+ for (i = 0; i < conf->disks_cnt; i++)
if (conf->disks[i].extra_page)
put_page(conf->disks[i].extra_page);
kfree(conf->disks);
@@ -7733,7 +7734,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
if (!conf->disks)
goto abort;
-
+ conf->disks_cnt = max_disks;
for (i = 0; i < max_disks; i++) {
conf->disks[i].extra_page = alloc_page(GFP_KERNEL);
if (!conf->disks[i].extra_page)
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -667,6 +667,7 @@ struct r5conf {
unsigned long cache_state;
struct shrinker *shrinker;
int pool_size; /* number of disks in stripeheads in pool */
+ int disks_cnt; /* number of entries in disks[] array */
spinlock_t device_lock;
struct disk_info *disks;
struct bio_set bio_split;
--
2.34.1
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v4 2/2] md/raid5: fix NULL pointer dereference in raid5_free_percpu
2026-08-27 8:03 ` [PATCH v4 1/2] md/raid5: track disks array size to fix extra_page leak on error paths ghuicao
@ 2026-08-27 8:03 ` ghuicao
2026-08-27 8:19 ` [PATCH v4 1/2] md/raid5: track disks array size to fix extra_page leak on error paths sashiko-bot
2026-09-05 2:34 ` yu kuai
2 siblings, 0 replies; 17+ messages in thread
From: ghuicao @ 2026-08-27 8:03 UTC (permalink / raw)
To: Song Liu
Cc: Yu Kuai, Li Nan, Xiao Ni, linux-raid, linux-kernel, stable,
Cao Guanghui
From: Cao Guanghui <caoguanghui@kylinos.cn>
If cpuhp_state_add_instance() fails in raid5_alloc_percpu() (e.g., the
startup callback raid456_cpu_up_prepare fails due to an allocation
failure), conf->node is never added to the cpuhp instance list and
its pprev remains NULL (from kzalloc initialization).
When setup_conf() then jumps to abort, free_conf() calls
raid5_free_percpu(), which checks conf->percpu (non-NULL, since it was
allocated before the cpuhp failure) and proceeds to call
cpuhp_state_remove_instance(). This calls hlist_del() on the unhashed
node, which dereferences node->pprev (NULL), causing a kernel panic.
Guard the removal with hlist_unhashed_lockless() so that the cpuhp
instance is only removed if it was actually added. The lockless variant
uses READ_ONCE() for the pprev read, which is safe here because the
actual removal is synchronized by cpuhp_state_mutex inside
cpuhp_state_remove_instance().
Fixes: 29c6d1bbd7a2 ("md/raid5: Convert to hotplug state machine")
Cc: stable@vger.kernel.org
Signed-off-by: Cao Guanghui <caoguanghui@kylinos.cn>
---
drivers/md/raid5.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -7539,7 +7539,8 @@ static void raid5_free_percpu(struct r5conf *conf)
{
if (!conf->percpu)
return;
- cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
+ if (!hlist_unhashed_lockless(&conf->node))
+ cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
free_percpu(conf->percpu);
}
--
2.34.1
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v4 1/2] md/raid5: track disks array size to fix extra_page leak on error paths
2026-08-27 8:03 ` [PATCH v4 1/2] md/raid5: track disks array size to fix extra_page leak on error paths ghuicao
2026-08-27 8:03 ` [PATCH v4 2/2] md/raid5: fix NULL pointer dereference in raid5_free_percpu ghuicao
@ 2026-08-27 8:19 ` sashiko-bot
2026-09-05 2:34 ` yu kuai
2 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-27 8:19 UTC (permalink / raw)
To: ghuicao; +Cc: yukuai, linux-raid
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] Retrying `resize_stripes()` after a partial failure leaks `extra_page` allocations from the previous attempt.
- [High] `resize_stripes()` leaks `stripe_head` pages if Step 4 fails (on architectures where PAGE_SIZE == DEFAULT_STRIPE_SIZE).
- [High] Use-After-Free race condition on `conf->disks` array between `raid5_status()` and `resize_stripes()`
--
commit c7f0ab9943c7136150bf0d1225ef069780dedbe5
Author: Cao Guanghui <caoguanghui@kylinos.cn>
md/raid5: track disks array size to fix extra_page leak on error paths
This commit introduces disks_cnt to struct r5conf to track the actual number
of entries in conf->disks and uses it in free_conf() to fix memory leaks
when pool_size doesn't match the actual allocated array size.
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index b91545ce090de..e5838bf516e38 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -2642,6 +2642,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
> } else {
> kfree(conf->disks);
[Severity: High]
This is a pre-existing issue, but can freeing the disks array directly here
cause a use-after-free race with raid5_status()?
raid5_status() runs with only mddev->lock held and iterates over
conf->disks[i].rdev:
drivers/md/raid5.c:raid5_status() {
...
struct md_rdev *rdev = READ_ONCE(conf->disks[i].rdev);
...
}
Would calling kfree() here (without kfree_rcu or synchronize_rcu) allow the
array to be freed while raid5_status() is actively reading from it?
[Severity: High]
This is also a pre-existing issue, but will this kfree() leak the
extra_page allocations on retry if a previous reshape failed in Step 4?
If resize_stripes() succeeds in Step 3 (updating conf->disks) but fails
in Step 4, conf->pool_size is not updated. On a retry, the earlier copy
loop only copies elements up to the outdated conf->pool_size:
drivers/md/raid5.c:resize_stripes() {
...
for (i = 0; i < conf->pool_size; i++)
ndisks[i] = conf->disks[i];
...
}
Will this cause the extra_page allocations at indices >= pool_size from
the previous attempt to be permanently abandoned and leaked when this
kfree(conf->disks) is called?
> conf->disks = ndisks;
> + conf->disks_cnt = newsize;
[Severity: High]
This isn't a bug introduced by this patch, but could resize_stripes()
leak stripe_head pages if a failure happens later in Step 4?
In Step 4, pages are allocated for the new stripe size:
drivers/md/raid5.c:resize_stripes() {
...
for (i = conf->raid_disks; i < newsize; i++)
if (nsh->dev[i].page == NULL) {
struct page *p = alloc_page(GFP_NOIO);
nsh->dev[i].page = p;
...
}
If an allocation fails, err = -ENOMEM is set, but the loop continues and
previously allocated pages remain attached. Because err is set,
conf->pool_size is never updated.
Later, shrink_buffers() bounds itself strictly to 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++) {
...
}
Will this unconditionally leak all orphaned pages residing at higher
indices (>= pool_size) when the stripe_head structures are dropped?
> }
> } else
> err = -ENOMEM;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260827063235.85375-1-ghuicao@163.com?part=1
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v4 1/2] md/raid5: track disks array size to fix extra_page leak on error paths
2026-08-27 8:03 ` [PATCH v4 1/2] md/raid5: track disks array size to fix extra_page leak on error paths ghuicao
2026-08-27 8:03 ` [PATCH v4 2/2] md/raid5: fix NULL pointer dereference in raid5_free_percpu ghuicao
2026-08-27 8:19 ` [PATCH v4 1/2] md/raid5: track disks array size to fix extra_page leak on error paths sashiko-bot
@ 2026-09-05 2:34 ` yu kuai
2 siblings, 0 replies; 17+ messages in thread
From: yu kuai @ 2026-09-05 2:34 UTC (permalink / raw)
To: ghuicao, Song Liu, yu kuai
Cc: Li Nan, Xiao Ni, linux-raid, linux-kernel, stable, Cao Guanghui
在 2026/8/27 16:03, ghuicao@163.com 写道:
> From: Cao Guanghui<caoguanghui@kylinos.cn>
>
> free_conf() iterates conf->pool_size entries to free extra_page
> allocations, but pool_size may not reflect the actual size of the
> conf->disks array. Two scenarios cause a mismatch:
>
> 1. setup_conf() early abort: pool_size is 0 (not yet set by
> grow_stripes) but conf->disks has max_disks entries with
> extra_page allocated. The loop iterates 0 times, leaking all
> pages.
>
> 2. resize_stripes() Step 4 failure: conf->disks was replaced with
> a newsize-entry array in Step 3, but pool_size is only updated
> on success. The loop iterates pool_size (old, smaller value)
> times, leaking (newsize - pool_size) pages.
>
> Add a dedicated disks_cnt field to track the actual number of
> entries in conf->disks. Set it immediately after each allocation
> or replacement (in setup_conf and resize_stripes Step 3, where the
> array is safely stalled with no concurrent access), and use it in
> free_conf() instead of pool_size.
>
> This leaves pool_size untouched, preserving the check_reshape()
> retry behavior that depends on pool_size only being updated on
> full success.
>
> Fixes: d7bd398e97f2 ("md/r5cache: handle alloc_page failure")
> Cc:stable@vger.kernel.org
> Signed-off-by: Cao Guanghui<caoguanghui@kylinos.cn>
> ---
> drivers/md/raid5.c | 4 +++-
> drivers/md/raid5.h | 1 +
> 2 files changed, 4 insertions(+), 1 deletion(-)
Applied v4 to md-7.3.
--
Thanks,
Kuai
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-09-05 2:35 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-27 2:38 [PATCH] md/raid5: set pool_size before extra_page allocation to fix leak on error path ghuicao
2026-08-27 2:53 ` sashiko-bot
2026-08-27 6:05 ` [PATCH v2 1/3] " ghuicao
2026-08-27 6:05 ` [PATCH v2 2/3] md/raid5: fix leak and use-after-free in resize_stripes " ghuicao
2026-08-27 6:27 ` sashiko-bot
2026-08-27 6:05 ` [PATCH v2 3/3] md/raid5: fix NULL pointer dereference in raid5_free_percpu ghuicao
2026-08-27 6:18 ` sashiko-bot
2026-08-27 6:27 ` [PATCH v2 1/3] md/raid5: set pool_size before extra_page allocation to fix leak on error path sashiko-bot
2026-08-27 6:32 ` [PATCH v3 " ghuicao
2026-08-27 6:32 ` [PATCH v3 2/3] md/raid5: fix leak and use-after-free in resize_stripes " ghuicao
2026-08-27 7:02 ` sashiko-bot
2026-08-27 6:32 ` [PATCH v3 3/3] md/raid5: fix NULL pointer dereference in raid5_free_percpu ghuicao
2026-08-27 7:17 ` sashiko-bot
2026-08-27 8:03 ` [PATCH v4 1/2] md/raid5: track disks array size to fix extra_page leak on error paths ghuicao
2026-08-27 8:03 ` [PATCH v4 2/2] md/raid5: fix NULL pointer dereference in raid5_free_percpu ghuicao
2026-08-27 8:19 ` [PATCH v4 1/2] md/raid5: track disks array size to fix extra_page leak on error paths sashiko-bot
2026-09-05 2:34 ` yu kuai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox