From nobody Mon Sep 28 00:10:33 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1786995160; cv=none; d=zohomail.com; s=zohoarc; b=KhBNYp78WD+IJ3A3KgfJhFVLxxF3KnsnmV5X7Pr9vHFM6c+qYbdCaokHXDCvFBOKHOC8cDJdFMn1ZTOfj3C4P1anZKNt0lyR48EfXpjcm+ccma9UZzn08JrIQy9jbYXX+w2ok4jDETzlPoqQC8D7CelN6UBKQnDCiBlds8CjbR8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1786995160; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=F3/9uID+5UQQyk8mlPm0RPopuhgY7yZ45MSr8eG3FmQ=; b=TLstZkb3oBLFRy+GyRyr1CQ1YxSpWtwSV2s6Wk+3M1qkUE93pme5NYUXfmJEwsmoCtH/892aNezlpdvidi7gUwwA8JHXA4cekClr2T0JtSoX0PvQJGdd/v4k2gB6kM3IINE4JuND3qdCLdiwC9oh2IQ5fXTvWztXKna5zol7FtQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1786995160956637.3450309073368; Mon, 17 Aug 2026 12:32:40 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ww33w-0003Se-Na; Mon, 17 Aug 2026 15:32:00 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ww331-0003E3-Bs; Mon, 17 Aug 2026 15:31:03 -0400 Received: from forwardcorp1d.mail.yandex.net ([178.154.239.200]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ww32x-00032E-RX; Mon, 17 Aug 2026 15:31:03 -0400 Received: from mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net [IPv6:2a02:6b8:c42:65a0:0:640:e1de:0]) by forwardcorp1d.mail.yandex.net (postfix) with ESMTPS id 7572B80B0B; Mon, 17 Aug 2026 22:30:55 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:d2f::1:33]) by mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net (smtpcorp) with ESMTPSA id rUaw3t0XVW20-wZGEHwKs; Mon, 17 Aug 2026 22:30:55 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1786995055; bh=F3/9uID+5UQQyk8mlPm0RPopuhgY7yZ45MSr8eG3FmQ=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=p8/kFoWb1Ryx+U2Cz9mX08eEIOe8TcLabGopAMnmgIsihYcstx30dWxzi2oU+NPZ2 Je0izJCTwtm5O6UgX3NoZECdu96FmC3lVJbxo8bLCBEyuLv6P3opXf8s05x06/XQOV VcCZpuobbpMBP8DQdaQg8mCUWruVotVDDnc2RR44= Authentication-Results: mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru Subject: [PULL 1/6] job: keep job paused across overlapping pause requests Date: Mon, 17 Aug 2026 22:29:43 +0300 Message-ID: <20260817192948.1743452-2-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260817192948.1743452-1-vsementsov@yandex-team.ru> References: <20260817192948.1743452-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=178.154.239.200; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1d.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1786995162397158500 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" job_pause_point_locked() sets job->paused before yielding and clears it unconditionally on wake, before re-checking whether a pause is still pending. job_pause() re-enters a parked job only while it is not yet paused, so the wake that resumes one comes from a drain *ending* (job_resume() -> job_enter_cond()). If the next drain begins before that wake runs, the woken coroutine clears job->paused while pause_count is already > 0 again: AioContext change (BQL thread) job coroutine (iothread) ----------------------------- ------------------------ parked in job_pause_point(): paused=3D1, pause_count=3D1, yielded drain ends -> job_resume(): pause_count =3D 0 job_enter_cond(): queue wake ..> (wake pending) bdrv_try_change_aio_context(): bdrv_drain_all_begin(): job_pause() per node pause_count =3D N (> 0) wake runs, leaves job_do_yield(): paused =3D 0 (pause_count =3D=3D N) tran_commit -> job_set_aio_context(): assert(paused || completed) --> abort: paused =3D=3D 0 bdrv_try_change_aio_context() drains precisely to quiesce the job before changing its AioContext, but that brief paused=3D=3D0 window trips the assertion. It is guest-triggerable: a virtio-blk reset (virtio_blk_stop_ioeventfd() -> blk_set_aio_context()) racing a running mirror/blockCopy job hits it, as do x-blockdev-set-iothread, blockdev hot-plug/unplug and job completion. Keep job->paused set while a pause is still pending: loop the yield until job_should_pause_locked() is false (or the job is cancelled), and only then clear job->paused. Drained-state consumers then never observe a pending-pause job as unpaused. Signed-off-by: Denis V. Lunev Message-ID: <20260623152406.1180235-2-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Vladimir Sementsov-Ogievskiy --- job.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/job.c b/job.c index e7479084726..d7220aaf30c 100644 --- a/job.c +++ b/job.c @@ -629,7 +629,14 @@ static void coroutine_fn job_pause_point_locked(Job *j= ob) ? JOB_STATUS_STANDBY : JOB_STATUS_PAUSED); job->paused =3D true; - job_do_yield_locked(job, -1); + /* + * Stay paused across back-to-back pause requests: a transient + * paused =3D=3D false while pause_count > 0 would be observed as + * "not paused" by job_set_aio_context() and other drain consumers. + */ + do { + job_do_yield_locked(job, -1); + } while (job_should_pause_locked(job) && !job_is_cancelled_locked(= job)); job->paused =3D false; job_state_transition_locked(job, status); } --=20 2.43.0 From nobody Mon Sep 28 00:10:33 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1786995180; cv=none; d=zohomail.com; s=zohoarc; b=TOFCa8usQ6gbWPk27cPC5+Dj1wEkhks/EXfy5UK+78brNP8T8dXMu4OjesexcN+i/x1n8LKl+14xclpc9/cz6lfQGrWPkhm9wcd/9p/eVckLUhmOMlYULEH5fPdBs5S4m3OjunWque6+GxPztL3QRfANg0AadgJ7NmZ8Amp2NXI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1786995180; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=IhnY7dl5HPtxi/v1FI9u4iSL+biA3nduueZ/yMxFM8o=; b=Ei99w+NQsRNfNlyXNz2FgdHcq8Of9OLZpnLb4wDDMzyMt0bXD0flsWQ35QIlJvjznsKX2uKwiZfCoa+a8IsigfvxEy5/sOgQWNNKmjpsx9K2xCe2T/TJD71PQOiUhIuoqS0Ru54qV4Agq7U1a1RfEyRwz1uUs2kLa4SbpDShY54= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1786995180656354.9178679385807; Mon, 17 Aug 2026 12:33:00 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ww33N-0003Ij-Lt; Mon, 17 Aug 2026 15:31:41 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ww330-0003E2-Fz; Mon, 17 Aug 2026 15:31:03 -0400 Received: from forwardcorp1d.mail.yandex.net ([178.154.239.200]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ww32x-00032G-Rv; Mon, 17 Aug 2026 15:31:01 -0400 Received: from mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net [IPv6:2a02:6b8:c42:65a0:0:640:e1de:0]) by forwardcorp1d.mail.yandex.net (postfix) with ESMTPS id F258D80669; Mon, 17 Aug 2026 22:30:55 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:d2f::1:33]) by mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net (smtpcorp) with ESMTPSA id rUaw3t0XVW20-i4pnX9CA; Mon, 17 Aug 2026 22:30:55 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1786995055; bh=IhnY7dl5HPtxi/v1FI9u4iSL+biA3nduueZ/yMxFM8o=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=F4npuaIjoSvN4FCq4uMGGWlxIJRRpTQVW44HIgE1/S1gy4bRjWTc/FmHnshqFKrlV OVhSglolbXxi7yFy9ibTKj+lGroyWSvbR1KpIfnUs+y2APfLc5SCfBtMKst4DEvm2N 6mYnrWhhDaZ2x0yn+h65kVxzqWBQZJtNshJmEgss= Authentication-Results: mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru Subject: [PULL 2/6] tests/unit/test-blockjob: cover keeping a job paused while a pause is pending Date: Mon, 17 Aug 2026 22:29:44 +0300 Message-ID: <20260817192948.1743452-3-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260817192948.1743452-1-vsementsov@yandex-team.ru> References: <20260817192948.1743452-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=178.154.239.200; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1d.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1786995182575158500 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" Add a regression test for the previous commit. A job that has reached its pause point is spuriously re-entered (job_enter()) while a pause is still pending (pause_count > 0), reproducing what an overlapping drain does: one drain's job_resume() wakes the job while the next drain's job_pause() is already counted. The job must stay parked - it must not run job code or clear job->paused, or job_set_aio_context() could observe paused =3D=3D false and abort. The test counts the job's run-loop iterations: without the fix the re-entered job clears job->paused, runs one iteration and re-pauses, so the counter advances; with the fix it stays parked and the counter is unchanged. It runs in the main AioContext, so job_enter() is synchronous and the check is deterministic. Signed-off-by: Denis V. Lunev Message-ID: <20260623152406.1180235-3-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Tested-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Vladimir Sementsov-Ogievskiy --- tests/unit/test-blockjob.c | 100 +++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/tests/unit/test-blockjob.c b/tests/unit/test-blockjob.c index abdbe4b8350..3b77d19b911 100644 --- a/tests/unit/test-blockjob.c +++ b/tests/unit/test-blockjob.c @@ -388,6 +388,105 @@ static void test_cancel_concluded(void) cancel_common(s); } =20 +typedef struct PauseCountJob { + BlockJob common; + int n; + bool should_complete; +} PauseCountJob; + +static void pause_count_job_complete(Job *job, Error **errp) +{ + PauseCountJob *s =3D container_of(job, PauseCountJob, common.job); + s->should_complete =3D true; +} + +static int coroutine_fn pause_count_job_run(Job *job, Error **errp) +{ + PauseCountJob *s =3D container_of(job, PauseCountJob, common.job); + + while (!s->should_complete) { + if (job_is_cancelled(&s->common.job)) { + return 0; + } + s->n++; + /* + * Yields; while a pause is pending the yield is skipped and the j= ob + * parks in job_pause_point() instead. + */ + job_sleep_ns(&s->common.job, 10 * 1000 * 1000); + } + + return 0; +} + +static const BlockJobDriver pause_count_job_driver =3D { + .job_driver =3D { + .instance_size =3D sizeof(PauseCountJob), + .free =3D block_job_free, + .user_resume =3D block_job_user_resume, + .run =3D pause_count_job_run, + .complete =3D pause_count_job_complete, + }, +}; + +/* + * A job that has reached its pause point must stay paused while a pause is + * still pending (pause_count > 0). An overlapping drain re-enters the job= (one + * drain's job_resume() wakes it while the next drain's job_pause() is alr= eady + * counted); the job must not run or clear job->paused, otherwise + * job_set_aio_context() can observe paused =3D=3D false and abort. + */ +static void test_pause_keeps_paused(void) +{ + BlockBackend *blk; + BlockJob *bjob; + PauseCountJob *s; + Job *job; + int n0; + + blk =3D create_blk(NULL); + bjob =3D mk_job(blk, "job0", &pause_count_job_driver, true, + JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); + s =3D container_of(bjob, PauseCountJob, common); + job =3D &bjob->job; + WITH_JOB_LOCK_GUARD() { + job_ref_locked(job); + } + + job_start(job); + + /* Pause the running job; it parks in job_pause_point() with paused se= t. */ + WITH_JOB_LOCK_GUARD() { + job_pause_locked(job); + g_assert_true(job->paused); + g_assert_cmpint(job->status, =3D=3D, JOB_STATUS_PAUSED); + } + n0 =3D s->n; + + /* + * Spurious wake while the pause is still pending. The job must stay p= arked: + * the bug clears job->paused, runs an iteration (s->n advances) and + * re-pauses, exposing a paused =3D=3D false window. + */ + job_enter(job); + WITH_JOB_LOCK_GUARD() { + g_assert_true(job->paused); + } + g_assert_cmpint(s->n, =3D=3D, n0); + + /* Resume and tear down. */ + WITH_JOB_LOCK_GUARD() { + job_resume_locked(job); + } + job_cancel_sync(job, true); + WITH_JOB_LOCK_GUARD() { + Job *dummy =3D job; + job_dismiss_locked(&dummy, &error_abort); + job_unref_locked(job); + } + destroy_blk(blk); +} + int main(int argc, char **argv) { qemu_init_main_loop(&error_abort); @@ -402,5 +501,6 @@ int main(int argc, char **argv) g_test_add_func("/blockjob/cancel/standby", test_cancel_standby); g_test_add_func("/blockjob/cancel/pending", test_cancel_pending); g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded); + g_test_add_func("/blockjob/pause/keep_paused", test_pause_keeps_paused= ); return g_test_run(); } --=20 2.43.0 From nobody Mon Sep 28 00:10:33 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1786995178; cv=none; d=zohomail.com; s=zohoarc; b=DOETgs4EdGurjOQ0kvlSgj2/etTKUd1mZUckw9+K2N8iZndCKAxwCQiV5EbZU0KVy9RzSRISBfVHJdr7/7Uv+0n/45IubxinkG+mAb5BAzFLZR+/QKDTRx7APcsjZbq5U6twgHySl9aZfK+exQIT4KouHA9NTxDm2VQjF1CMsWI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1786995178; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=rWTw2RiDpf/4J2CrXlG+TnOzcZJf+4Zlq07OFSZhshQ=; b=GNzJY6hV0WHuzOW4cQVSfitiG6zKQeM9TBIrOjF/Fj4WlhM4fkAlDEaCytt/fOaew4sCKep4AID6U5eOPa1qlgEsdhF6uET2MmTp4rfneQSHBulUo60JV4RAR7BqN2ZkmOawUUI9DfbAYOXIEioCNUtarQHES5nQDkUcOcMGvGs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1786995178482421.6484817834398; Mon, 17 Aug 2026 12:32:58 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ww33x-0003UX-7j; Mon, 17 Aug 2026 15:32:01 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ww331-0003E8-QH; Mon, 17 Aug 2026 15:31:03 -0400 Received: from forwardcorp1d.mail.yandex.net ([178.154.239.200]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ww32x-00032O-Ta; Mon, 17 Aug 2026 15:31:03 -0400 Received: from mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net [IPv6:2a02:6b8:c42:65a0:0:640:e1de:0]) by forwardcorp1d.mail.yandex.net (postfix) with ESMTPS id C4D8B80B09; Mon, 17 Aug 2026 22:30:56 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:d2f::1:33]) by mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net (smtpcorp) with ESMTPSA id rUaw3t0XVW20-LtnBpLII; Mon, 17 Aug 2026 22:30:56 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1786995056; bh=rWTw2RiDpf/4J2CrXlG+TnOzcZJf+4Zlq07OFSZhshQ=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=k263rtlMU69VDOtmzvDFAbJeU4YXxSs2s0Eg0QAnAJ0tAz9nY66N+449nLaDzITLy 0RUER0cGsTAdr1K1DFhIuExYJnYxjZgGfhMMetvEbQ96Nncav2S54JbmvVqOkML5EY vzChLCFOOc0g8Y1xGbY3pdZZbbeoIF2glSSzIQ58= Authentication-Results: mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru, Eric Blake , John Snow , Andrey Drobyshev Subject: [PULL 3/6] block/monitor: reject persistent bitmap add on a read-only node Date: Mon, 17 Aug 2026 22:29:45 +0300 Message-ID: <20260817192948.1743452-4-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260817192948.1743452-1-vsementsov@yandex-team.ru> References: <20260817192948.1743452-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=178.154.239.200; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1d.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1786995180549158500 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" qmp_block_dirty_bitmap_add() marks a new bitmap persistent without checking write access to its node. bdrv_create_dirty_bitmap() always creates bitmaps writable, so a persistent bitmap added to an already read-only node stays writable in memory on a node that can never store it, and the next global inactivation fails: Lost persistent bitmaps during inactivation of node '': No write ac= cess migration_block_inactivate: bdrv_inactivate_all() failed: -22 Forcing it read-only instead does not help: it was never stored, so it stays unpromotable on the next reopen to read-write and can trip bdrv_set_dirty()'s readonly assert on the first write. Reject the add instead, for both read-only and inactive nodes -- an already-inactive node skips qcow2_inactivate() on close, so a bitmap added during that window would never get stored either. Wrapped in a transaction, this denies the whole transaction, since qmp_transaction() is already all-or-none. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy CC: John Snow CC: Andrey Drobyshev Message-ID: <20260716112242.3000035-2-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/monitor/bitmap-qmp-cmds.c | 15 +++++++--- qapi/block-core.json | 4 ++- .../tests/remove-bitmap-from-backing | 29 ++++++++++++++++++- .../tests/remove-bitmap-from-backing.out | 20 +++++++++++++ 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmd= s.c index a738e7bbf7a..d87ca982aa9 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -125,10 +125,17 @@ void qmp_block_dirty_bitmap_add(const char *node, con= st char *name, disabled =3D false; } =20 - if (persistent && - !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) - { - return; + if (persistent) { + if (!bdrv_is_writable(bs)) { + error_setg(errp, "Cannot add a persistent bitmap to " + "read-only or inactive node '%s'", + bdrv_get_node_name(bs)); + return; + } + + if (!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp))= { + return; + } } =20 bitmap =3D bdrv_create_dirty_bitmap(bs, granularity, name, errp); diff --git a/qapi/block-core.json b/qapi/block-core.json index 1f87b078505..199efc1e008 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2353,7 +2353,9 @@ # @persistent: the bitmap is persistent, i.e. it will be saved to the # corresponding block device image file on its close. For now # only Qcow2 disks support persistent bitmaps. Default is false -# for `block-dirty-bitmap-add`. (Since: 2.10) +# for `block-dirty-bitmap-add`. This fails if the node is +# read-only or inactive, since such a bitmap could never be +# stored. (Since: 2.10) # # @disabled: the bitmap is created in the disabled state, which means # that it will not track drive changes. The bitmap may be enabled diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing b/tests/qe= mu-iotests/tests/remove-bitmap-from-backing index 15be32dcb96..a54984fa58e 100755 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing @@ -35,7 +35,7 @@ qemu_img('bitmap', '--add', base, 'bitmap0') # Just assert that our method of checking bitmaps in the image works. assert 'bitmaps' in qemu_img_info(base)['format-specific']['data'] =20 -vm =3D iotests.VM().add_drive(top, 'backing.node-name=3Dbase') +vm =3D iotests.VM().add_drive(top, 'node-name=3Dtop,backing.node-name=3Dba= se') vm.launch() =20 log('Trying to remove persistent bitmap from r-o base node, should fail:') @@ -66,6 +66,33 @@ result =3D vm.qmp('blockdev-reopen', **new_base_opts) if result !=3D {'return': {}}: log('Failed to reopen: ' + str(result)) =20 +log('Adding a persistent bitmap to the r-o base node, should fail:') +vm.qmp_log('block-dirty-bitmap-add', node=3D'base', name=3D'bitmap1', + persistent=3DTrue) + +log('Same add inside a transaction, preceded by an otherwise valid') +log('action: the whole transaction must fail and roll back the') +log('already-succeeded first action too:') +vm.qmp_log('transaction', actions=3D[ + {'type': 'block-dirty-bitmap-add', + 'data': {'node': 'top', 'name': 'bitmap2', 'persistent': True}}, + {'type': 'block-dirty-bitmap-add', + 'data': {'node': 'base', 'name': 'bitmap1', 'persistent': True}}, +]) + +log('bitmap2 on the rw top node must not have survived the rollback:') +vm.qmp_log('block-dirty-bitmap-remove', node=3D'top', name=3D'bitmap2') + +log('Marking the rw top node inactive:') +vm.qmp_log('blockdev-set-active', **{'node-name': 'top', 'active': False}) + +log('Adding a persistent bitmap to a rw but inactive node, should fail:') +vm.qmp_log('block-dirty-bitmap-add', node=3D'top', name=3D'bitmap3', + persistent=3DTrue) + +log('Reactivating the top node:') +vm.qmp_log('blockdev-set-active', **{'node-name': 'top', 'active': True}) + vm.shutdown() =20 if 'bitmaps' in qemu_img_info(base)['format-specific']['data']: diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out b/test= s/qemu-iotests/tests/remove-bitmap-from-backing.out index c28af82c752..fe105fe0a38 100644 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing.out @@ -4,3 +4,23 @@ Trying to remove persistent bitmap from r-o base node, sho= uld fail: Remove persistent bitmap from base node reopened to RW: {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", = "node": "base"}} {"return": {}} +Adding a persistent bitmap to the r-o base node, should fail: +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap1", "no= de": "base", "persistent": true}} +{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitma= p to read-only or inactive node 'base'"}} +Same add inside a transaction, preceded by an otherwise valid +action: the whole transaction must fail and roll back the +already-succeeded first action too: +{"execute": "transaction", "arguments": {"actions": [{"data": {"name": "bi= tmap2", "node": "top", "persistent": true}, "type": "block-dirty-bitmap-add= "}, {"data": {"name": "bitmap1", "node": "base", "persistent": true}, "type= ": "block-dirty-bitmap-add"}]}} +{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitma= p to read-only or inactive node 'base'"}} +bitmap2 on the rw top node must not have survived the rollback: +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap2", = "node": "top"}} +{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap2' not fo= und"}} +Marking the rw top node inactive: +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-na= me": "top"}} +{"return": {}} +Adding a persistent bitmap to a rw but inactive node, should fail: +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap3", "no= de": "top", "persistent": true}} +{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitma= p to read-only or inactive node 'top'"}} +Reactivating the top node: +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-nam= e": "top"}} +{"return": {}} --=20 2.43.0 From nobody Mon Sep 28 00:10:33 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1786995136; cv=none; d=zohomail.com; s=zohoarc; b=jcUPRvMEL3w47Y6cbWhpP/D56zds1l1HCUiap8DjsRR2MRGo/qbauC4GZibhJJnfzv1fW4PbsMg7xu/fm8ioo+DI+s1ptIUAI2XOxyhnuswhWc3l6x3v+5Xq6e4BtXJ4Ph/WATnJdIHX+V64FVuJYilLep6mL6ExWdKGIWl6AmQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1786995136; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=5RnyRJmuwjgpDZyKGCFUOKKFq8Hlu6VvFptd9ZcPzz8=; b=eTG+hTucCMkw/tlOzJkcJKBX3qbYU6DNsiEDK/YlCZhz7f8Mi0o5MgaCsNcazJ8scJtdYwDJrsVRTB4dlPf0YjlbIipCa87vI21gk49iD5tQnH8QvJ89AYMrF9INEPDBdYJBMWZsJF6H5i6Aq86KS2t3Wzg3Z12QwWnuoUgMmAY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1786995136163959.1499329856872; Mon, 17 Aug 2026 12:32:16 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ww33t-0003Os-Se; Mon, 17 Aug 2026 15:31:58 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ww330-0003E1-G2; Mon, 17 Aug 2026 15:31:03 -0400 Received: from forwardcorp1d.mail.yandex.net ([178.154.239.200]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ww32x-00032T-RV; Mon, 17 Aug 2026 15:31:01 -0400 Received: from mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net [IPv6:2a02:6b8:c42:65a0:0:640:e1de:0]) by forwardcorp1d.mail.yandex.net (postfix) with ESMTPS id 8E46080B0E; Mon, 17 Aug 2026 22:30:57 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:d2f::1:33]) by mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net (smtpcorp) with ESMTPSA id rUaw3t0XVW20-4gj4S6Z3; Mon, 17 Aug 2026 22:30:56 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1786995056; bh=5RnyRJmuwjgpDZyKGCFUOKKFq8Hlu6VvFptd9ZcPzz8=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=Db9E6VZGQDAFouCiKHpf5vpKzakWE/ho0KQe8mJvR7ld5JmoJu7IujCkAuptECKL/ lVf9Ff8sCwecOex/TERa2/IE4dbVPzXuTYs/w50BAPxpbrujRqQ9/YiwCwnSauM7ue SIUN+JprWWTESRrSw4e1IqG+yqXYkG8Ft5vqPGh4= Authentication-Results: mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru, Eric Blake , John Snow , Andrey Drobyshev Subject: [PULL 4/6] migration/block-dirty-bitmap: reject bitmap load onto ro node Date: Mon, 17 Aug 2026 22:29:46 +0300 Message-ID: <20260817192948.1743452-5-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260817192948.1743452-1-vsementsov@yandex-team.ru> References: <20260817192948.1743452-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=178.154.239.200; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1d.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1786995140393158500 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" dirty_bitmap_load_start() creates an incoming migrated bitmap with bdrv_create_dirty_bitmap() and, if the source marked it persistent, calls bdrv_dirty_bitmap_set_persistence() without checking whether the destination node can be written to. Same gap as qmp_block_dirty_bitmap_add(), reached via incoming migration: a persistent bitmap for a read-only destination (e.g. a migrated CD-ROM-class attachment with dirty-bitmaps migration enabled) ends up writable in memory on a node that can never store it. Reject it the same way, with one difference from the QMP path: every destination node is BDRV_O_INACTIVE until migration completes, so bdrv_is_writable() would reject every incoming persistent bitmap, not just read-only ones. Check bdrv_is_read_only() alone. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy CC: John Snow CC: Andrey Drobyshev Message-ID: <20260716112242.3000035-3-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Vladimir Sementsov-Ogievskiy --- migration/block-dirty-bitmap.c | 22 +++++++++---- tests/qemu-iotests/tests/migrate-bitmaps-test | 33 +++++++++++++++++++ .../tests/migrate-bitmaps-test.out | 4 +-- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index cba54e25cd5..1b8f39c12bb 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -812,13 +812,6 @@ static int dirty_bitmap_load_start(QEMUFile *f, DBMLoa= dState *s) error_report("Bitmap with the same name ('%s') already exists on " "destination", bdrv_dirty_bitmap_name(s->bitmap)); return -EINVAL; - } else { - s->bitmap =3D bdrv_create_dirty_bitmap(s->bs, granularity, - s->bitmap_name, &local_err); - if (!s->bitmap) { - error_report_err(local_err); - return -EINVAL; - } } =20 if (flags & DIRTY_BITMAP_MIG_START_FLAG_RESERVED_MASK) { @@ -835,6 +828,21 @@ static int dirty_bitmap_load_start(QEMUFile *f, DBMLoa= dState *s) persistent =3D flags & DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT; } =20 + /* Not bdrv_is_writable(): nodes stay inactive until migration ends. */ + if (persistent && bdrv_is_read_only(s->bs)) { + error_report("Cannot make migrated bitmap '%s' persistent " + "on read-only node '%s'", s->bitmap_name, + bdrv_get_node_name(s->bs)); + return -EINVAL; + } + + s->bitmap =3D bdrv_create_dirty_bitmap(s->bs, granularity, + s->bitmap_name, &local_err); + if (!s->bitmap) { + error_report_err(local_err); + return -EINVAL; + } + if (persistent) { bdrv_dirty_bitmap_set_persistence(s->bitmap, true); } diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test b/tests/qemu-iot= ests/tests/migrate-bitmaps-test index 8fb4099201d..cb9154ca8d7 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test @@ -206,6 +206,39 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): self.vm_b.launch() self.check_bitmap(self.vm_b, sha256 if persistent else False) =20 + def test_migration_to_readonly_destination(self): + granularity =3D 512 + mig_caps =3D [{'capability': 'events', 'state': True}, + {'capability': 'dirty-bitmaps', 'state': True}] + + self.vm_b.add_incoming("defer") + self.vm_b.add_drive(disk_b, 'read-only=3Don') + + self.add_bitmap(self.vm_a, granularity, True) + self.vm_a.hmp_qemu_io('drive0', 'write 0 4096') + + self.vm_a.cmd('migrate-set-capabilities', capabilities=3Dmig_caps) + self.vm_a.cmd('migrate', uri=3Dmig_cmd) + while True: + event =3D self.vm_a.event_wait('MIGRATION') + if event['data']['status'] =3D=3D 'completed': + break + self.vm_a.shutdown() + + self.vm_b.launch() + self.vm_b.cmd('migrate-set-capabilities', capabilities=3Dmig_caps) + self.vm_b.cmd('migrate-incoming', uri=3Dincoming_cmd) + while True: + event =3D self.vm_b.event_wait('MIGRATION') + if event['data']['status'] in ('completed', 'failed'): + break + + self.assert_qmp(event, 'data/status', 'failed') + + # A failed incoming load makes the destination process exit on + # its own; reap it so tearDown()'s shutdown() is a clean no-op. + self.vm_b.wait() + =20 def inject_test_case(klass, suffix, method, *args, **kwargs): mc =3D operator.methodcaller(method, *args, **kwargs) diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test.out b/tests/qemu= -iotests/tests/migrate-bitmaps-test.out index cafb8161f7b..73e375a9d74 100644 --- a/tests/qemu-iotests/tests/migrate-bitmaps-test.out +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test.out @@ -1,5 +1,5 @@ -..................................... +...................................... ---------------------------------------------------------------------- -Ran 37 tests +Ran 38 tests =20 OK --=20 2.43.0 From nobody Mon Sep 28 00:10:33 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1786995180; cv=none; d=zohomail.com; s=zohoarc; b=Za3IwASuk7GXKmijDpn+KreD4wd7u/k8/RH4MdfAEr5ZZE4QybTvvr3B0meuVa0OUSIISMrjEWnF+yhip1dvgbQwbogbcSyBqkWtfqvT0WJj37wkxjC3yPQ44e653yf/JoivdPfYgBF8BZHMYru+6iO5sA9NqlFC2WW1+wsu4jg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1786995180; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=3OpG9sABXUmzwyfNcO80xtzm/M5/oFBiNMWndqUczu0=; b=lJmhPYO5gyE8SurWu/KT6yrHp9rFCcBB9/JBsDMznppiGXybVMh1hh5/svSf/8mPfw77qQdlA3jeQzntLXgtq4NXkDPAX1seJGsS2PbeJGr9HCK2+gdJ2DJTjO5LTTjpr2YpKLGq9CeyLx6CcUpG4G+ru0kAiefVdU1GpaUGDA0= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 178699518002349.206005125971615; Mon, 17 Aug 2026 12:33:00 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ww33N-0003Il-M1; Mon, 17 Aug 2026 15:31:41 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ww331-0003E6-Hn; Mon, 17 Aug 2026 15:31:03 -0400 Received: from forwardcorp1d.mail.yandex.net ([2a02:6b8:c41:1300:1:45:d181:df01]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ww32y-00032i-IB; Mon, 17 Aug 2026 15:31:03 -0400 Received: from mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net [IPv6:2a02:6b8:c42:65a0:0:640:e1de:0]) by forwardcorp1d.mail.yandex.net (postfix) with ESMTPS id 31C5A80B0F; Mon, 17 Aug 2026 22:30:58 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:d2f::1:33]) by mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net (smtpcorp) with ESMTPSA id rUaw3t0XVW20-rL4r7NGk; Mon, 17 Aug 2026 22:30:57 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1786995057; bh=3OpG9sABXUmzwyfNcO80xtzm/M5/oFBiNMWndqUczu0=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=jZQgv3OWB/UZa4UX0cunod557MH57CMQeMMK18hbPGiR6HRzB3PmtCd1RZLkfujxw 5Mh9YoHCvZtMFnKSLnT+yD3J/XhcRFcJB5GZkieNETY0kkmWytnHrM+MJKsfvFaHO8 e3ltgTG/V8NpsNz3Qyd3cUzsnKcMPKLJeOALjwN4= Authentication-Results: mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru, Eric Blake , John Snow , Andrey Drobyshev Subject: [PULL 5/6] block/monitor: allow dropping a bitmap never stored on disk Date: Mon, 17 Aug 2026 22:29:47 +0300 Message-ID: <20260817192948.1743452-6-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260817192948.1743452-1-vsementsov@yandex-team.ru> References: <20260817192948.1743452-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a02:6b8:c41:1300:1:45:d181:df01; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1d.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1786995182559158500 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" block-dirty-bitmap-remove refuses any readonly bitmap outright, via the generic BDRV_BITMAP_RO check in bdrv_dirty_bitmap_check(). That check cannot tell whether the bitmap is actually on disk, so it also blocks dropping one that only ever existed in memory, which needs no write at all. Drop the blanket check and let qcow2 decide: bdrv_remove_persistent_ dirty_bitmap() already treats an absent on-disk entry as a no-op, so such a bitmap is now released with no write attempted. For one that is genuinely stored, qcow2_co_remove_persistent_dirty_bitmap_locked() now checks can_write() before it would update the on-disk directory, so removal still fails there, with a message naming the actual reason instead of just the bitmap's readonly flag. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy CC: John Snow CC: Andrey Drobyshev Message-ID: <20260716112242.3000035-4-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/monitor/bitmap-qmp-cmds.c | 4 ++-- block/qcow2-bitmap.c | 9 +++++++++ tests/qemu-iotests/tests/remove-bitmap-from-backing.out | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmd= s.c index d87ca982aa9..aabf2790b67 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -165,11 +165,11 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char= *node, const char *name, return NULL; } =20 - if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO, - errp)) { + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { return NULL; } =20 + /* Dropping a bitmap needs no write access unless it is actually store= d. */ if (bdrv_dirty_bitmap_get_persistence(bitmap) && bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0) { diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 256ec998788..ac5a7245885 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -1487,6 +1487,15 @@ int coroutine_fn qcow2_co_remove_persistent_dirty_bi= tmap(BlockDriverState *bs, goto out; } =20 + if (!can_write(bs)) { + error_setg(errp, "Cannot remove persistent bitmap '%s': " + "no write access to node '%s'", name, + bdrv_get_node_name(bs)); + ret =3D -EACCES; + bm =3D NULL; + goto out; + } + QSIMPLEQ_REMOVE(bm_list, bm, Qcow2Bitmap, entry); =20 ret =3D update_ext_header_and_dir(bs, bm_list); diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out b/test= s/qemu-iotests/tests/remove-bitmap-from-backing.out index fe105fe0a38..628fa737d97 100644 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing.out @@ -1,6 +1,6 @@ Trying to remove persistent bitmap from r-o base node, should fail: {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", = "node": "base"}} -{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap0' is readonly = and cannot be modified"}} +{"error": {"class": "GenericError", "desc": "Cannot remove persistent bitm= ap 'bitmap0': no write access to node 'base'"}} Remove persistent bitmap from base node reopened to RW: {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", = "node": "base"}} {"return": {}} --=20 2.43.0 From nobody Mon Sep 28 00:10:33 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1786995136; cv=none; d=zohomail.com; s=zohoarc; b=HVaSq4W7S5MKI7BgK2C/nKC+f5ZMicTbrNolHtDkMx/tqJ7aOcHbAkK0zU+tYCl6MFuuH38OLdlnIlYHUQA4rFP+H7SSZfyRLkgjHCtjI6e7ZAS7Ht4tnoaXwBbT4JaQwUEwUHc8jIDe6aejLNlx+O8GEExQPJYZCvbhSh+pDz4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1786995136; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=kVWewRhaI+wizI+FpMDLyADXpjhTI0cfqWKa4vzZw2U=; b=UrM+zQMFNNPUDpeDtuySVFk2oc/HuisLQyHw9pK7ciGpXdMKolCOXDfDR2ZoNcFnvbdGsaNssKLQvRz6N7Tna/UmhCJlbXpXM8Wkk+11/3aFn3kBB9b1w0pf+ybfkytlenhNbAP7rTWiJEjfYvLIIetulIPLYWF1shyK8+hzC3g= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1786995135673488.1343992375505; Mon, 17 Aug 2026 12:32:15 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ww33l-0003OV-Kg; Mon, 17 Aug 2026 15:31:51 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ww332-0003FH-Kv; Mon, 17 Aug 2026 15:31:04 -0400 Received: from forwardcorp1d.mail.yandex.net ([2a02:6b8:c41:1300:1:45:d181:df01]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ww330-00032u-Hb; Mon, 17 Aug 2026 15:31:04 -0400 Received: from mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net [IPv6:2a02:6b8:c42:65a0:0:640:e1de:0]) by forwardcorp1d.mail.yandex.net (postfix) with ESMTPS id BCDA180B10; Mon, 17 Aug 2026 22:30:58 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:d2f::1:33]) by mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net (smtpcorp) with ESMTPSA id rUaw3t0XVW20-hLubazHF; Mon, 17 Aug 2026 22:30:58 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1786995058; bh=kVWewRhaI+wizI+FpMDLyADXpjhTI0cfqWKa4vzZw2U=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=xmOfnRfQqZKmSlOxF6Qox+0JZBmnfNvPcOz/KxRrxVjqkAvF5JITwQGPaYzlomF/8 1Kr67kBGG2sJnxx+tPZCMjWDudCSt4yeMk4V3bzOjkPuoRAtp9CBNBWgklwO6iBK78 ERF5xVtWDgwCrN49TlQAZINDeqeAopr3CClCh9QE= Authentication-Results: mail-nwsmtp-smtp-corp-main-56.klg.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru, Eric Blake , Stefan Hajnoczi , Thomas Huth Subject: [PULL 6/6] dirty-bitmap: fix integer overflow in serialization coverage Date: Mon, 17 Aug 2026 22:29:48 +0300 Message-ID: <20260817192948.1743452-7-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260817192948.1743452-1-vsementsov@yandex-team.ru> References: <20260817192948.1743452-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a02:6b8:c41:1300:1:45:d181:df01; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1d.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1786995140746158500 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" The chunk size is an int and is shifted left by 3 before the result is widened, so a chunk size of 1 << 28 or above overflows. parallels passes s->cluster_size, which parallels_open() lets reach 2 GiB. With a bitmap needing two L1 entries the bogus limit makes the "bm_size - offset" in parallels_load_bitmap_data() underflow; both wrong values slip past the assertions in serialization_chunk() and the resulting index lands outside the hbitmap, so a 128 KiB image memsets unrelated memory through hbitmap_deserialize_ones(). Widen the shift. qcow2, the only other caller, never exceeds a 2 MiB cluster. Fixes: 35f428ba3971 ("qcow2-bitmap: make bytes_covered_by_bitmap_cluster() = public") Cc: Eric Blake Cc: Vladimir Sementsov-Ogievskiy Cc: Stefan Hajnoczi Cc: Thomas Huth Signed-off-by: Denis V. Lunev Message-ID: <20260811173857.396571-4-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/dirty-bitmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 13a1979755d..9fda3a4b983 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -612,7 +612,7 @@ uint64_t bdrv_dirty_bitmap_serialization_coverage(int s= erialized_chunk_size, const BdrvDirtyBitmap *b= itmap) { uint64_t granularity =3D bdrv_dirty_bitmap_granularity(bitmap); - uint64_t limit =3D granularity * (serialized_chunk_size << 3); + uint64_t limit =3D granularity * ((uint64_t)serialized_chunk_size << 3= ); =20 assert(QEMU_IS_ALIGNED(limit, bdrv_dirty_bitmap_serialization_align(bitmap))); --=20 2.43.0