Re: [PATCH v4 01/25] md/md-llbitmap: clear flush state after daemon flush

[email protected] Sat, 01 Aug 2026 17:43:41 +0000
Newsgroups org.kernel.vger.linux-raid
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 1 potential issue(=
s) to consider:
- [High] Consuming LLPageFlush in the daemon introduces a race condition th=
at re-arms the timer during array shutdown, leading to a Use-After-Free of =
the llbitmap struct.
--

--- Patch [1]: [PATCH v4 01/25] md/md-llbitmap: clear flush state after dae=
mon flush ---
commit abba2aa11371c8630f75f9bd27a43851e99339eb
Author: Yu Kuai <[email protected]>

md/md-llbitmap: clear flush state after daemon flush

This commit clears the LLPageFlush bit in md_llbitmap_daemon_fn() rather
than leaving it set. This prevents previously flushed pages from bypassing
the barrier_idle expiry check on subsequent daemon runs, making new writes
wait for the full idle window.

> diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
> index 5a4e2abaa7577..131582724e7e9 100644
> --- a/drivers/md/md-llbitmap.c
> +++ b/drivers/md/md-llbitmap.c
> @@ -1066,14 +1066,14 @@ static void md_llbitmap_daemon_fn(struct work_str=
uct *work)
> =20
>  	for (idx =3D 0; idx < llbitmap->nr_pages; idx++) {
>  		struct llbitmap_page_ctl *pctl =3D llbitmap->pctl[idx];
> +		bool flush =3D test_and_clear_bit(LLPageFlush, &pctl->flags);
> =20
>  		if (idx > 0) {
>  			start =3D end + 1;
>  			end =3D min(end + PAGE_SIZE, llbitmap->chunks - 1);
>  		}
> =20
> -		if (!test_bit(LLPageFlush, &pctl->flags) &&
> -		    time_before(jiffies, pctl->expire)) {
> +		if (!flush && time_before(jiffies, pctl->expire)) {

[Severity: High]
Could clearing the flush bit here introduce a race condition during array
shutdown that leads to a use-after-free of the llbitmap struct?

If llbitmap/daemon_sleep is configured to a small value, stopping the array
invokes llbitmap_flush(), which sets LLPageFlush and queues the daemon:

llbitmap_flush()
    ...
    queue_work(md_llbitmap_io_wq, &llbitmap->daemon_work);
    flush_work(&llbitmap->daemon_work);

If the daemon is concurrently running, it can consume the newly set
LLPageFlush bits via test_and_clear_bit(). The explicitly queued daemon run
would then see flush =3D=3D false, evaluate the unexpired pages, set restar=
t =3D
true, and re-arm the timer.

If disk flushing takes longer than the sleep interval, the timer can fire
and queue the work again.

Later, llbitmap_destroy() calls:

llbitmap_destroy()
    ...
    timer_delete_sync(&llbitmap->pending_timer);
    flush_workqueue(md_llbitmap_io_wq);

Because the timer already fired, timer_delete_sync() does nothing. Then
flush_workqueue() executes the queued daemon work, which incorrectly re-arms
the timer after the deletion point. The struct is then freed, and the timer
subsequently fires on freed memory.

>  			restart =3D true;
>  			continue;
>  		}

--=20
Sashiko AI review =C2=B7 https://sashiko.dev/#/patchset/20260801172519.2982=
[email protected]?part=3D1