Re: [PATCH v5 06/29] md: wait for behind writes before destroying bitmap
[email protected] Sun, 02 Aug 2026 20:40:01 +0000
| Newsgroups | org.kernel.vger.linux-raid |
|---|---|
| Message-ID | <[email protected]> |
Thank you for your contribution! Sashiko AI review found 3 potential issue(= s) to consider: Pre-existing issues: - [High] Dynamic removal of llbitmap via sysfs or ioctl does not wait for b= ehind writes, leading to a NULL pointer dereference. - [High] Teardown asymmetry in llbitmap_destroy allows the pending_timer to= be re-armed after deletion, leading to a Use-After-Free of llbitmap. - [High] Lockless TOCTOU race on `mddev->bitmap_ops` leading to NULL pointe= r dereference. -- --- Patch [6]: [PATCH v5 06/29] md: wait for behind writes before destroyin= g bitmap --- commit 908390619225b153696b5648f3002688751593a6 Author: Yu Kuai <[email protected]> md: wait for behind writes before destroying bitmap __md_stop() previously destroyed the bitmap before calling mddev_detach(), skipping the wait for behind writes. This commit reorders them to call mddev_detach() first, ensuring behind writes are waited on while the bitmap is still alive. > diff --git a/drivers/md/md.c b/drivers/md/md.c > index 51b620edbef75..b61040315aef6 100644 > --- a/drivers/md/md.c > +++ b/drivers/md/md.c > @@ -7085,8 +7085,8 @@ static void __md_stop(struct mddev *mddev) > { > struct md_personality *pers =3D mddev->pers; > =20 > - md_bitmap_destroy(mddev); > mddev_detach(mddev); > + md_bitmap_destroy(mddev); [Severity: High] This is a pre-existing issue, but does dynamic removal of the bitmap via sysfs fail to wait for behind writes? When a user writes to bitmap/location to clear the bitmap dynamically, location_store() calls mddev_suspend_and_lock() to suspend the array. mddev_suspend() waits for active_io to drop to 0. For RAID1 behind writes, active_io is dropped by md_end_clone_io() when non-writemostly devices complete their writes: drivers/md/md.c:md_handle_request() { ... percpu_ref_put(&mddev->active_io); ... } This means behind writes to writemostly devices can still be in flight. location_store() then proceeds to free the bitmap: drivers/md/md-bitmap.c:location_store() { ... md_bitmap_destroy_nosysfs(mddev); ... } When behind writes finally complete, could close_write() dereference the now NULL bitmap_ops pointer? drivers/md/raid1.c:close_write() { ... mddev->bitmap_ops->end_behind_write(mddev); ... } [Severity: High] This is a pre-existing issue, but can the llbitmap pending_timer be re-armed after it is deleted during destruction? llbitmap_destroy() deletes the timer and flushes the workqueue: drivers/md/md-llbitmap.c:llbitmap_destroy() { ... timer_delete_sync(&llbitmap->pending_timer); flush_workqueue(md_llbitmap_io_wq); ... } If the timer expires right before deletion, the handler queues md_llbitmap_daemon_fn() to the workqueue. If unexpired dirty pages exist, the daemon function evaluates time_before(jiffies, pctl->expire). Since llbitmap_resume() sets pctl->expire to LONG_MAX, this evaluates to true, setting restart to true. The timer is then unconditionally re-armed: drivers/md/md-llbitmap.c:md_llbitmap_daemon_fn() { ... if (restart) mod_timer(&llbitmap->pending_timer, ...); ... } The flush_workqueue() call in llbitmap_destroy() will wait for this work to finish, but it does not prevent the timer from being re-armed. Could this leave the timer armed with a freed llbitmap pointer, causing a use-after-free when it fires? [Severity: High] This isn't a bug introduced by this patch, but is there a TOCTOU race on mddev->bitmap_ops between the teardown path and concurrent readers? In the background thread, md_check_recovery() checks md_bitmap_enabled(), which locklessly verifies mddev->bitmap_ops is not NULL: drivers/md/md.c:md_check_recovery() { ... if (md_bitmap_enabled(mddev, false) && mddev->bitmap_ops->daemon_work) mddev->bitmap_ops->daemon_work(mddev); ... } Concurrently, a write to bitmap/location via sysfs triggers location_store(= ), which acquires reconfig_mutex (but not mddev->lock or bitmap_info.mutex) and calls md_bitmap_destroy_nosysfs(), unconditionally setting the ops pointer to NULL: drivers/md/md.c:md_bitmap_destroy_nosysfs() { ... mddev->bitmap_ops =3D NULL; ... } Could md_check_recovery() proceed to dereference the now NULL pointer? A similar race appears to exist with md_seq_show() reading /proc/mdstat, which holds locks not respected by the sysfs teardown path: drivers/md/md.c:md_seq_show() { ... md_bitmap_status(seq, mddev); ... } > spin_lock(&mddev->lock); > mddev->pers =3D NULL; > spin_unlock(&mddev->lock); --=20 Sashiko AI review =C2=B7 https://sashiko.dev/#/patchset/20260802195038.1642= [email protected]?part=3D6