Thread (40 messages) 40 messages, 2 authors, 1d ago

Re: [PATCH v3 05/21] md/md-llbitmap: track bitmap sync_size explicitly

From: sashiko-bot@kernel.org
Date: 2026-07-28 09:16:06

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

New issues:
- [Critical] Missing upper and lower bounds validation on the newly tracked `sync_size` read from the superblock leads to arbitrary out-of-bounds memory accesses.
- [High] Validation of `chunksize` in `llbitmap_read_sb` incorrectly uses `mddev->resync_max_sectors` instead of `sync_size`.
- [High] Torn read of the 64-bit `sync_size` field leading to superblock corruption and memory allocation bugs on reboot, along with unprotected bitmap geometry updates.

Pre-existing issues:
- [High] A teardown race condition in `llbitmap_destroy()` fails to prevent a workqueue task from re-arming a timer after it has been deleted, causing a UAF.
--
--- Patch [5]: [PATCH v3 05/21] md/md-llbitmap: track bitmap sync_size explicitly ---
commit e1b87a51dfe616ddb9c0f12bcc69bdac5d5044d5
Author: Yu Kuai [off-list ref]

md/md-llbitmap: track bitmap sync_size explicitly
    
Track llbitmap's own sync_size instead of always using
mddev->resync_max_sectors directly.

This is the minimal bookkeeping needed before llbitmap can track old
and new reshape geometry independently.
quoted hunk ↗ jump to hunk
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index f0c20afa354eb..e3500a1a52c07 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
[ ... ]
quoted hunk ↗ jump to hunk
@@ -987,6 +991,9 @@ static int llbitmap_read_sb(struct llbitmap *llbitmap)
 		goto out_put_page;
 	}
 
+	sync_size = le64_to_cpu(sb->sync_size);
[Severity: Critical]
Is it necessary to validate the upper and lower bounds of the sync_size
read from the superblock here?

If sync_size is manipulated to be near U64_MAX, calculating nr_pages
might truncate to a small 32-bit integer, while chunks remains huge.
Could this cause llbitmap_init_state() to perform massive out-of-bounds
writes into the undersized pctl array?

Alternatively, if sync_size is too small, nr_pages is small, but incoming
IO up to mddev->resync_max_sectors calculates a page_start that exceeds
the bounds of pctl:

drivers/md/md-llbitmap.c:llbitmap_start_write() {
    ...
    int page_start = (start + BITMAP_DATA_OFFSET) >> PAGE_SHIFT;
    ...
    while (page_start <= page_end) {
        llbitmap_raise_barrier(llbitmap, page_start);
    ...
}

Could this lead to out-of-bounds memory accesses if a maliciously crafted
bitmap superblock is mounted?


[Severity: High]
Should the chunksize validation further down in llbitmap_read_sb() be
updated to use sync_size as well?

    if (chunksize < DIV_ROUND_UP_SECTOR_T(mddev->resync_max_sectors,
                                          mddev->bitmap_info.space << SECTOR_SHIFT)) {

If an array is grown and stopped before the bitmap is resized,
mddev->resync_max_sectors reflects the new larger size, but the bitmap
superblock retains the old sync_size and chunksize.

Will checking if the old chunksize can cover the new resync_max_sectors
cause a false failure and abort bitmap initialization in this scenario?
+	if (!sync_size)
+		sync_size = mddev->resync_max_sectors;
 	chunksize = le32_to_cpu(sb->chunksize);
 	if (!is_power_of_2(chunksize)) {
[ ... ]
quoted hunk ↗ jump to hunk
@@ -1153,6 +1161,7 @@ static int llbitmap_resize(struct mddev *mddev, sector_t blocks, int chunksize)
 	llbitmap->chunkshift = ffz(~chunksize);
 	llbitmap->chunksize = chunksize;
 	llbitmap->chunks = chunks;
+	llbitmap->sync_size = blocks;
 
 	return 0;
 }
[Severity: High]
Are these updates to the 64-bit sync_size and geometry variables
adequately protected from concurrent access?

Unlike the legacy bitmap which uses mddev->pers->quiesce() to pause IO,
llbitmap_resize() does not pause IO. This allows active IO paths to read
a mix of old and new geometry values.

Additionally, llbitmap_update_sb() concurrently reads llbitmap->sync_size
locklessly:

drivers/md/md-llbitmap.c:llbitmap_update_sb() {
    ...
    sb->sync_size = cpu_to_le64(llbitmap->sync_size);
    ...
}

On 32-bit platforms, might this race result in a torn read of the 64-bit
value, causing a corrupted value to be written to the disk superblock?

[ ... ]

This is a pre-existing issue, but does the teardown sequence in
llbitmap_destroy() allow the pending_timer to be re-armed after deletion?

In llbitmap_destroy():

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

If the daemon work is concurrently running in the workqueue,
timer_delete_sync() returns immediately because the timer is no longer
pending.

The workqueue is then flushed, but at the end of the worker function
md_llbitmap_daemon_fn(), it unconditionally re-arms the timer:

    if (restart)
        mod_timer(&llbitmap->pending_timer,
                  jiffies + mddev->bitmap_info.daemon_sleep * HZ);

When llbitmap is subsequently freed, will the re-armed timer dereference
the freed memory when it fires?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1785206690.git.yukuai@fygo.io?part=5
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help