From: Xiao Ni xni@redhat.com
[ Upstream commit 5f286f33553d600e6c2fb5a23dd6afcf99b3ebac ]
UNTIL_STOP is used to avoid mddev is freed on the last close before adding disks to mddev. And it should be cleared when stopping an array which is mentioned in commit efeb53c0e572 ("md: Allow md devices to be created by name."). So reset ->hold_active to 0 in md_clean.
And MD_CLOSING should be kept until mddev is freed to avoid reopen.
Reviewed-by: Yu Kuai yukuai3@huawei.com Signed-off-by: Xiao Ni xni@redhat.com Link: https://lore.kernel.org/linux-raid/20250611073108.25463-3-xni@redhat.com Signed-off-by: Yu Kuai yukuai3@huawei.com Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
**Backport Status: YES**
This commit should be backported to stable kernel trees. Here's my detailed analysis:
## Bug Fix Analysis
This commit fixes a race condition in the MD (Multiple Device) subsystem where the MD_CLOSING flag could be cleared prematurely, allowing an mddev to be reopened while it's in the process of being freed. This is a clear use-after-free vulnerability.
## Key Issues Fixed
1. **Race condition with MD_CLOSING flag**: The old code would conditionally clear MD_CLOSING based on whether `hold_active` was set: ```c if (mddev->hold_active) mddev->flags = 0; // This clears MD_CLOSING! else mddev->flags &= BIT_ULL_MASK(MD_CLOSING); ```
2. **Improper hold_active handling**: The commit properly moves the clearing of `hold_active` from `do_md_stop()` into `md_clean()`, making the code cleaner and more predictable.
## Impact on Users
- **Security Impact**: This prevents a potential use-after-free vulnerability where an mddev could be reopened while being freed. - **Stability Impact**: Prevents crashes or undefined behavior when racing between mddev close/free and reopen operations. - **Data Integrity**: Could prevent corruption scenarios where operations are performed on a partially freed mddev structure.
## Code Changes Analysis
The changes are minimal and focused: 1. Removes the conditional logic that could clear MD_CLOSING inappropriately 2. Always preserves MD_CLOSING flag in `md_clean()` with: `mddev->flags &= BIT_ULL_MASK(MD_CLOSING)` 3. Moves `hold_active = 0` assignment from `do_md_stop()` to `md_clean()` for better code organization 4. Updates comments to clarify the behavior
## Stable Tree Criteria
✓ **Fixes a real bug**: Yes - race condition leading to potential use- after-free ✓ **Small and contained**: Yes - only 15 lines changed in a single function ✓ **No new features**: Correct - only fixes existing behavior ✓ **Minimal regression risk**: Yes - preserves existing semantics while fixing the race ✓ **Important enough**: Yes - security/stability issue in core storage subsystem
The commit message explicitly references commit efeb53c0e572 which introduced the UNTIL_STOP mechanism, showing this is fixing a long- standing issue in the MD subsystem's lifecycle management.
drivers/md/md.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c index 2758c3ee2f56..60b49f7c74c2 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -6265,15 +6265,10 @@ static void md_clean(struct mddev *mddev) mddev->persistent = 0; mddev->level = LEVEL_NONE; mddev->clevel[0] = 0; - /* - * Don't clear MD_CLOSING, or mddev can be opened again. - * 'hold_active != 0' means mddev is still in the creation - * process and will be used later. - */ - if (mddev->hold_active) - mddev->flags = 0; - else - mddev->flags &= BIT_ULL_MASK(MD_CLOSING); + /* if UNTIL_STOP is set, it's cleared here */ + mddev->hold_active = 0; + /* Don't clear MD_CLOSING, or mddev can be opened again. */ + mddev->flags &= BIT_ULL_MASK(MD_CLOSING); mddev->sb_flags = 0; mddev->ro = MD_RDWR; mddev->metadata_type[0] = 0; @@ -6545,9 +6540,6 @@ static int do_md_stop(struct mddev *mddev, int mode, export_array(mddev); md_clean(mddev); set_bit(MD_DELETED, &mddev->flags); - - if (mddev->hold_active == UNTIL_STOP) - mddev->hold_active = 0; } md_new_event(); sysfs_notify_dirent_safe(mddev->sysfs_state);