From: no-reply@sashiko.virtuozzo.com
To: andrey.zhadchenko@virtuozzo.com, "Denis V. Lunev" <den@openvz.org>
Cc: devel@openvz.org
Subject: Re: [Devel] [PATCH hci-8.1 1/3] dm-qcow2: fix a failed refcount table relocation leaking md pages
Date: Tue, 3 Nov 2026 08:45:24 +0000 [thread overview]
Message-ID: <010701a1a095a6cb-c91275b2-2f8f-437b-a8dc-abee3b54ad61-000000@eu-central-1.amazonses.com> (raw)
In-Reply-To: <20261103083708.131379-2-den@openvz.org>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] dm-qcow2: relocate_refcount_table() error path frees md pages still queued on wb lists
--
commit de2ba3b61c5585914bdb1778601206847fadbda6
Author: Denis V. Lunev <den@openvz.org>
dm-qcow2: fix a failed refcount table relocation leaking md pages
This commit changes the error path of relocate_refcount_table() so that it
frees exactly the md pages the allocation loop inserted. Previously it freed
only one page per cluster starting from end. The new range is
[new_md_start, new_md_end) in PAGE_SIZE steps.
> diff --git a/drivers/md/dm-qcow2-map.c b/drivers/md/dm-qcow2-map.c
> --- a/drivers/md/dm-qcow2-map.c
> +++ b/drivers/md/dm-qcow2-map.c
[ ... ]
> @@ -2419,7 +2421,7 @@ static int relocate_refcount_table(struct qcow2 *qcow2, struct qio **qio)
> return 1;
>
> err_free_r2_pages:
> - for (i = end; i < r2_end; i += clu_size) {
> + for (i = new_md_start; i < new_md_end; i += PAGE_SIZE) {
> ret = handle_md_page(qcow2, i >> PAGE_SHIFT, NULL, &md);
> if (ret <= 0)
> break;
> spin_lock_irq(&qcow2->md_pages_lock);
> qcow2_md_page_erase(qcow2, md);
> spin_unlock_irq(&qcow2->md_pages_lock);
> qcow2_free_md_page(md);
[Severity: Medium]
Can this cleanup free md pages that are still linked on a writeback list?
The loop now frees every page in the inserted range. That range includes
the last new-R1 cluster pages and all pages of multi-page refblock
clusters. The loop calls qcow2_md_page_erase() and qcow2_free_md_page(),
but it never does list_del(&md->wb_link) and never clears MD_DIRTY.
qcow2_md_page_erase() only does rb_erase(). qcow2_free_md_page() only does
WARN_ON_ONCE(md->wbd || md->lockd), put_page() and kfree().
Pages in this range can be dirtied before the error label is reached.
mark_cluster_used() calls qcow2_md_make_dirty() on the new R2 pages:
mark_cluster_used()
set_r2_entry(qcow2, r2_md, r2_index_in_page, 1);
...
qcow2_md_make_dirty(qcow2, r2_md, true);
qcow2_md_make_dirty() then links the page onto the writeback list:
if (!(md->status & MD_DIRTY)) {
md->status |= MD_DIRTY;
list_add_tail(&md->wb_link, head);
Consider a failure in the mark-unused loop after the mark-used loop has
run:
relocate_refcount_table()
mark_cluster_used() /* new R2 pages dirty and queued */
qcow2_handle_r1r2_maps() ... /* WARN_ON_ONCE(ret <= 0) */
goto err_free_r2_pages
The same applies to the other late gotos after the header write: the
renumber failure, the calc_refcounters_map() < 0 case, and the
WARN_ON_ONCE(ret <= 0) checks in the connect and mark-used loops.
The freed md pages would then remain linked on slow_wb_batch_list or
wb_batch_list. When the writeback worker runs, it would walk the stale
entries:
submit_metadata_writeback()
md = list_first_entry_or_null(&qcow2->wb_batch_list,
struct md_page, wb_link);
...
list_del_init(&md->wb_link);
Could that be a use-after-free or list corruption?
I could not construct a concrete trigger, since these paths are guarded by
WARN_ON_ONCE. The allocation-failure and header-write-failure cases named
in the commit message are fine, because no page in the range is dirty yet.
The cached hdr.refcount_table_offset and hdr.refcount_table_clusters are
also not rolled back at this label, and the on-disk header has already been
rewritten. The existing "/* TODO: switch to RO */" comment covers this.
Should the cleanup unlink wb_link and clear MD_DIRTY for each page before
freeing it?
--
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103083708.131379-1-den@openvz.org?part=1
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
next prev parent reply other threads:[~2026-11-03 8:46 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 8:37 [Devel] [PATCH hci-8.1 0/3] dm-qcow2: fix merges not stopping the users of the images they change Denis V. Lunev
2026-11-03 8:37 ` [Devel] [PATCH hci-8.1 1/3] dm-qcow2: fix a failed refcount table relocation leaking md pages Denis V. Lunev
2026-11-03 8:45 ` no-reply [this message]
2026-11-03 8:37 ` [Devel] [PATCH hci-8.1 2/3] dm-qcow2: fix backward merge of an inner image racing its reads Denis V. Lunev
2026-11-03 8:44 ` no-reply
2026-11-03 8:37 ` [Devel] [PATCH hci-8.1 3/3] dm-qcow2: fix SEEK_HOLE/SEEK_DATA using an image freed by a merge Denis V. Lunev
2026-11-03 8:47 ` no-reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=010701a1a095a6cb-c91275b2-2f8f-437b-a8dc-abee3b54ad61-000000@eu-central-1.amazonses.com \
--to=no-reply@sashiko.virtuozzo.com \
--cc=andrey.zhadchenko@virtuozzo.com \
--cc=den@openvz.org \
--cc=devel@openvz.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox