OpenVZ / Virtuozzo kernel development (devel@openvz.org)
 help / color / mirror / Atom feed
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

  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