all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [PATCH-SERIES qemu 0/2] stable fixes for QEMU 11.1
@ 2026-10-12 10:51 Fiona Ebner
  2026-10-12 10:51 ` [PATCH qemu 1/2] update submodule and patches to QEMU 11.1.2 Fiona Ebner
  2026-10-12 10:51 ` [PATCH qemu 2/2] more stable fixes for QEMU 11.1 Fiona Ebner
  0 siblings, 2 replies; 3+ messages in thread
From: Fiona Ebner @ 2026-10-12 10:51 UTC (permalink / raw)
  To: pve-devel

Update to QEMU 11.1.2 and pick up some additional fixes from upstream
master and qemu-devel.

Fiona Ebner (2):
  update submodule and patches to QEMU 11.1.2
  more stable fixes for QEMU 11.1

 ...d-support-for-sync-bitmap-mode-never.patch |   6 +-
 ...alidate-primary-surface-stride-again.patch |  81 --
 ...5a-gracefully-handle-re-entrant-DMA.patch} |   0
 ...-Limit-DMA-transfers-to-plain-memory.patch |  85 ++
 ...corruption-from-broken-RMW-atomicity.patch | 124 +++
 ...ix-text-mode-OOB-write-after-a-graph.patch | 164 ----
 ...-redundant-bdrv_wait_serialising_req.patch |  71 ++
 ...-fix-out-of-bound-read-in-WRITE-SAME.patch |  71 --
 ...rv_next-skipping-monitor-owned-nodes.patch | 118 +++
 ...ELECT-block-size-change-behind-a-qui.patch |  89 --
 ...-cases-for-failing-flush-after-qemu-.patch |  88 ++
 ...-the-VAPIC-region-to-0xc0000.0xe0000.patch |  75 --
 ...-MemoryRegion-if-vapic_map_rom_writa.patch |  36 -
 .../0010-iotests.py-add-filter_qemu.patch     |  34 +
 ...report-flush-error-during-bdrv_close.patch | 763 ++++++++++++++++++
 ...t-do-not-treat-a-zero-length-write-a.patch |  54 --
 ...ert-flush-target-image-after-copying.patch |  99 +++
 ...return-success-on-info-without-snaps.patch |   4 +-
 ...dd-add-osize-and-read-from-to-stdin-.patch |  12 +-
 ...E-Up-qemu-img-dd-add-isize-parameter.patch |  14 +-
 ...PVE-Up-qemu-img-dd-add-n-skip_create.patch |  10 +-
 ...-add-l-option-for-loading-a-snapshot.patch |  14 +-
 ...virtio-balloon-improve-query-balloon.patch |   4 +-
 ...async-for-background-state-snapshots.patch |   4 +-
 ...add-the-zeroinit-block-driver-filter.patch |   6 +-
 ...le-posix-make-locking-optiono-on-cre.patch |   6 +-
 ...-Backup-add-backup-dump-block-driver.patch |   4 +-
 ...d-sequential-job-transaction-support.patch |   6 +-
 ...ckup-Proxmox-backup-patches-for-QEMU.patch |   2 +-
 ...k-driver-to-map-backup-archives-into.patch |   8 +-
 ...igrate-dirty-bitmap-state-via-savevm.patch |   2 +-
 ...dirty-bitmap-migrate-other-bitmaps-e.patch |   2 +-
 .../0035-block-add-alloc-track-driver.patch   |   8 +-
 .../0036-PVE-backup-add-fleecing-option.patch |   2 +-
 ...ment-backup-access-setup-and-teardow.patch |   2 +-
 debian/patches/series                         |  17 +-
 qemu                                          |   2 +-
 37 files changed, 1450 insertions(+), 637 deletions(-)
 delete mode 100644 debian/patches/extra/0004-hw-display-qxl-validate-primary-surface-stride-again.patch
 rename debian/patches/extra/{0005-hw-scsi-lsi53c895a-gracefully-handle-re-entrant-DMA.patch => 0004-hw-scsi-lsi53c895a-gracefully-handle-re-entrant-DMA.patch} (100%)
 create mode 100644 debian/patches/extra/0005-hw-usb-hcd-xhci-Limit-DMA-transfers-to-plain-memory.patch
 create mode 100644 debian/patches/extra/0006-block-Fix-corruption-from-broken-RMW-atomicity.patch
 delete mode 100644 debian/patches/extra/0006-hw-display-vga-fix-text-mode-OOB-write-after-a-graph.patch
 create mode 100644 debian/patches/extra/0007-block-Remove-now-redundant-bdrv_wait_serialising_req.patch
 delete mode 100644 debian/patches/extra/0007-scsi-disk-fix-out-of-bound-read-in-WRITE-SAME.patch
 create mode 100644 debian/patches/extra/0008-block-fix-bdrv_next-skipping-monitor-owned-nodes.patch
 delete mode 100644 debian/patches/extra/0008-scsi-hide-MODE-SELECT-block-size-change-behind-a-qui.patch
 create mode 100644 debian/patches/extra/0009-iotests-add-test-cases-for-failing-flush-after-qemu-.patch
 delete mode 100644 debian/patches/extra/0009-vapic-confine-the-VAPIC-region-to-0xc0000.0xe0000.patch
 delete mode 100644 debian/patches/extra/0010-i386-vapic-unref-MemoryRegion-if-vapic_map_rom_writa.patch
 create mode 100644 debian/patches/extra/0010-iotests.py-add-filter_qemu.patch
 create mode 100644 debian/patches/extra/0011-block-report-flush-error-during-bdrv_close.patch
 delete mode 100644 debian/patches/extra/0011-io-channel-socket-do-not-treat-a-zero-length-write-a.patch
 create mode 100644 debian/patches/extra/0012-qemu-img-convert-flush-target-image-after-copying.patch

-- 
2.47.3





^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH qemu 1/2] update submodule and patches to QEMU 11.1.2
  2026-10-12 10:51 [PATCH-SERIES qemu 0/2] stable fixes for QEMU 11.1 Fiona Ebner
@ 2026-10-12 10:51 ` Fiona Ebner
  2026-10-12 10:51 ` [PATCH qemu 2/2] more stable fixes for QEMU 11.1 Fiona Ebner
  1 sibling, 0 replies; 3+ messages in thread
From: Fiona Ebner @ 2026-10-12 10:51 UTC (permalink / raw)
  To: pve-devel

Picking up fixes for x86 and ARM emulation, QXL display, USB
emulation, IDE emulation, VirtIO and VirtIO-net.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
---
 ...d-support-for-sync-bitmap-mode-never.patch |   6 +-
 ...alidate-primary-surface-stride-again.patch |  81 ---------
 ...5a-gracefully-handle-re-entrant-DMA.patch} |   0
 ...ix-text-mode-OOB-write-after-a-graph.patch | 164 ------------------
 ...-fix-out-of-bound-read-in-WRITE-SAME.patch |  71 --------
 ...ELECT-block-size-change-behind-a-qui.patch |  89 ----------
 ...-the-VAPIC-region-to-0xc0000.0xe0000.patch |  75 --------
 ...-MemoryRegion-if-vapic_map_rom_writa.patch |  36 ----
 ...t-do-not-treat-a-zero-length-write-a.patch |  54 ------
 ...virtio-balloon-improve-query-balloon.patch |   4 +-
 ...async-for-background-state-snapshots.patch |   4 +-
 ...add-the-zeroinit-block-driver-filter.patch |   6 +-
 ...le-posix-make-locking-optiono-on-cre.patch |   6 +-
 ...-Backup-add-backup-dump-block-driver.patch |   2 +-
 ...d-sequential-job-transaction-support.patch |   6 +-
 ...ckup-Proxmox-backup-patches-for-QEMU.patch |   2 +-
 ...k-driver-to-map-backup-archives-into.patch |   8 +-
 ...igrate-dirty-bitmap-state-via-savevm.patch |   2 +-
 ...dirty-bitmap-migrate-other-bitmaps-e.patch |   2 +-
 .../0035-block-add-alloc-track-driver.patch   |   8 +-
 .../0036-PVE-backup-add-fleecing-option.patch |   2 +-
 ...ment-backup-access-setup-and-teardow.patch |   2 +-
 debian/patches/series                         |   9 +-
 qemu                                          |   2 +-
 24 files changed, 32 insertions(+), 609 deletions(-)
 delete mode 100644 debian/patches/extra/0004-hw-display-qxl-validate-primary-surface-stride-again.patch
 rename debian/patches/extra/{0005-hw-scsi-lsi53c895a-gracefully-handle-re-entrant-DMA.patch => 0004-hw-scsi-lsi53c895a-gracefully-handle-re-entrant-DMA.patch} (100%)
 delete mode 100644 debian/patches/extra/0006-hw-display-vga-fix-text-mode-OOB-write-after-a-graph.patch
 delete mode 100644 debian/patches/extra/0007-scsi-disk-fix-out-of-bound-read-in-WRITE-SAME.patch
 delete mode 100644 debian/patches/extra/0008-scsi-hide-MODE-SELECT-block-size-change-behind-a-qui.patch
 delete mode 100644 debian/patches/extra/0009-vapic-confine-the-VAPIC-region-to-0xc0000.0xe0000.patch
 delete mode 100644 debian/patches/extra/0010-i386-vapic-unref-MemoryRegion-if-vapic_map_rom_writa.patch
 delete mode 100644 debian/patches/extra/0011-io-channel-socket-do-not-treat-a-zero-length-write-a.patch

diff --git a/debian/patches/bitmap-mirror/0001-drive-mirror-add-support-for-sync-bitmap-mode-never.patch b/debian/patches/bitmap-mirror/0001-drive-mirror-add-support-for-sync-bitmap-mode-never.patch
index 40ec9887d7..8b0740a093 100644
--- a/debian/patches/bitmap-mirror/0001-drive-mirror-add-support-for-sync-bitmap-mode-never.patch
+++ b/debian/patches/bitmap-mirror/0001-drive-mirror-add-support-for-sync-bitmap-mode-never.patch
@@ -333,7 +333,7 @@ index e7c8f1a856..d5aa68caeb 100644
                    BlockdevOnError on_source_error,
                    BlockdevOnError on_target_error,
 diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 1f87b07850..c2f6139dea 100644
+index 199efc1e00..28d9c1e98e 100644
 --- a/qapi/block-core.json
 +++ b/qapi/block-core.json
 @@ -2280,6 +2280,15 @@
@@ -363,7 +363,7 @@ index 1f87b07850..c2f6139dea 100644
              '*speed': 'int', '*granularity': 'uint32',
              '*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
              '*on-target-error': 'BlockdevOnError',
-@@ -2598,6 +2609,15 @@
+@@ -2600,6 +2611,15 @@
  #     destination (all the disk, only the sectors allocated in the
  #     topmost image, or only new I/O).
  #
@@ -379,7 +379,7 @@ index 1f87b07850..c2f6139dea 100644
  # @granularity: granularity of the dirty bitmap, default is 64K if the
  #     image format doesn't have clusters, 4K if the clusters are
  #     smaller than that, else the cluster size.  Must be a power of 2
-@@ -2650,7 +2670,8 @@
+@@ -2652,7 +2672,8 @@
  { 'command': 'blockdev-mirror',
    'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
              '*replaces': 'str',
diff --git a/debian/patches/extra/0004-hw-display-qxl-validate-primary-surface-stride-again.patch b/debian/patches/extra/0004-hw-display-qxl-validate-primary-surface-stride-again.patch
deleted file mode 100644
index 299927cb83..0000000000
--- a/debian/patches/extra/0004-hw-display-qxl-validate-primary-surface-stride-again.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
-Date: Mon, 20 Jul 2026 15:45:13 +0400
-Subject: [PATCH] hw/display/qxl: validate primary surface stride against width
-
-The existing validation in qxl_create_guest_primary() checks that
-abs(stride) * height fits in vgamem_size and that stride is 4-byte
-aligned, but never checks that abs(stride) is large enough to hold one
-row of pixels for the declared width and format.
-
-A malicious guest can create a primary surface with a stride much
-smaller than width * bytes_per_pixel (e.g. stride=4 for a 64-wide 32bpp
-surface). The spice server rejects this via red_validate_surface(), but
-the return is void and QEMU unconditionally proceeds to set up the local
-rendering state. On the next display refresh, VNC or SDL reads width *
-byptes_pp per scanline from a region backed by only stride bytes per
-row, causing a host-side out-of-bounds read.
-
-Add three checks before creating the surface:
- - reject unknown surface formats
- - reject zero width or height
- - reject surfaces where abs(stride) < width * bytes_per_pixel
-
-Fixes: CVE-2026-16271
-Fixes: 3761abb16784 ("hw/display/qxl: fix signed to unsigned comparison")
-Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3637
-Signed-off-by: Marc-Andre Lureau <marcandre.lureau@redhat.com>
-Link: https://lore.kernel.org/qemu-devel/20260720114513.2500370-1-marcandre.lureau@redhat.com
-(picked from qemu-devel)
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
----
- hw/display/qxl.c | 29 +++++++++++++++++++++++++++++
- 1 file changed, 29 insertions(+)
-
-diff --git a/hw/display/qxl.c b/hw/display/qxl.c
-index b7d871b9ee..41673c369b 100644
---- a/hw/display/qxl.c
-+++ b/hw/display/qxl.c
-@@ -1496,6 +1496,7 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
-     QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
-     uint32_t requested_height = le32_to_cpu(sc->height);
-     int requested_stride = le32_to_cpu(sc->stride);
-+    uint32_t bytes_pp;
- 
-     if (requested_stride == INT32_MIN ||
-         abs(requested_stride) * (uint64_t)requested_height
-@@ -1532,6 +1533,34 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
-         return;
-     }
- 
-+    switch (surface.format) {
-+    case SPICE_SURFACE_FMT_16_555:
-+    case SPICE_SURFACE_FMT_16_565:
-+        bytes_pp = 2;
-+        break;
-+    case SPICE_SURFACE_FMT_32_xRGB:
-+    case SPICE_SURFACE_FMT_32_ARGB:
-+        bytes_pp = 4;
-+        break;
-+    default:
-+        qxl_set_guest_bug(qxl, "%s: unhandled format %d",
-+                          __func__, surface.format);
-+        return;
-+    }
-+
-+    if (surface.width == 0 || surface.height == 0) {
-+        qxl_set_guest_bug(qxl, "%s: zero dimension %ux%u",
-+                          __func__, surface.width, surface.height);
-+        return;
-+    }
-+
-+    if ((uint64_t)surface.width * bytes_pp > abs(surface.stride)) {
-+        qxl_set_guest_bug(qxl, "%s: stride too small for width:"
-+                          " stride %d width %u bpp %u",
-+                          __func__, surface.stride, surface.width, bytes_pp);
-+        return;
-+    }
-+
-     surface.mouse_mode = true;
-     surface.group_id   = MEMSLOT_GROUP_GUEST;
-     if (loadvm) {
diff --git a/debian/patches/extra/0005-hw-scsi-lsi53c895a-gracefully-handle-re-entrant-DMA.patch b/debian/patches/extra/0004-hw-scsi-lsi53c895a-gracefully-handle-re-entrant-DMA.patch
similarity index 100%
rename from debian/patches/extra/0005-hw-scsi-lsi53c895a-gracefully-handle-re-entrant-DMA.patch
rename to debian/patches/extra/0004-hw-scsi-lsi53c895a-gracefully-handle-re-entrant-DMA.patch
diff --git a/debian/patches/extra/0006-hw-display-vga-fix-text-mode-OOB-write-after-a-graph.patch b/debian/patches/extra/0006-hw-display-vga-fix-text-mode-OOB-write-after-a-graph.patch
deleted file mode 100644
index 9c7c7f6d32..0000000000
--- a/debian/patches/extra/0006-hw-display-vga-fix-text-mode-OOB-write-after-a-graph.patch
+++ /dev/null
@@ -1,164 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Warisjeet Singh <sinxx198@gmail.com>
-Date: Mon, 24 Aug 2026 12:47:26 -0400
-Subject: [PATCH] hw/display/vga: fix text-mode OOB write after a graphics
- surface switch
-
-vga_draw_text() decides whether the console surface needs a resize from
-its geometry cache, but none of the cache terms observe the graphics
-renderer having replaced the console surface in between:
-
-- last_width/last_height are shared with vga_draw_graphic(), which
-  stores them in pixels while the text path stores characters;
-- last_depth stays 0 for legacy (non-VBE) graphics modes, because
-  vga_get_bpp() only reports a depth when VBE is enabled, so the
-  "s->last_depth" term that normally forces a resize after a graphics
-  frame does not fire.
-
-So a graphics frame that shrinks the console surface (e.g. 80x25
-pixels) followed by a text frame with matching character geometry
-(80x25 chars) skips the resize, and the glyph loop then paints
-width*cw x height*cheight pixels into the smaller surface, out of
-bounds, with guest-controlled (DAC palette) values, on every display
-refresh.
-
-Separate the geometry cache per renderer: text paths (vga_draw_text,
-vga_update_text, and the text handling in vga_invalidate_display /
-vga_common_reset) now only manipulate last_text_{width,height}, in
-characters; last_{width,height} become graphics-only, in pixels.
-Additionally, make the text path compare the pixel size it is about
-to paint against the console surface's actual dimensions.  The
-surface check is the load-bearing term: caches in either unit cannot
-see the other renderer swapping the surface, the surface can.
-
-Fixes: CVE-2026-77913
-Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4215
-Cc: qemu-stable@nongnu.org
-Signed-off-by: Warisjeet Singh (sin99xx) <sinxx198@gmail.com>
-Message-ID: <vga-v3-20260824.sinxx198@gmail.com>
-(cherry picked from commit 418396be8013386a81f8d8d89ac0effcf03a64b6)
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
----
- hw/display/vga.c     | 37 ++++++++++++++++++++++---------------
- hw/display/vga_int.h |  3 ++-
- 2 files changed, 24 insertions(+), 16 deletions(-)
-
-diff --git a/hw/display/vga.c b/hw/display/vga.c
-index da0c331486..cb0e28b79b 100644
---- a/hw/display/vga.c
-+++ b/hw/display/vga.c
-@@ -1241,7 +1241,10 @@ static void vga_draw_text(VGACommonState *s, int full_update)
-         return;
-     }
- 
--    if (width != s->last_width || height != s->last_height ||
-+    if (surface == NULL ||
-+        surface_width(surface) != width * cw ||
-+        surface_height(surface) != height * cheight ||
-+        width != s->last_text_width || height != s->last_text_height ||
-         cw != s->last_cw || cheight != s->last_ch || s->last_depth) {
-         s->last_scr_width = width * cw;
-         s->last_scr_height = height * cheight;
-@@ -1249,8 +1252,8 @@ static void vga_draw_text(VGACommonState *s, int full_update)
-         surface = qemu_console_surface(s->con);
-         qemu_console_text_resize(s->con, width, height);
-         s->last_depth = 0;
--        s->last_width = width;
--        s->last_height = height;
-+        s->last_text_width = width;
-+        s->last_text_height = height;
-         s->last_ch = cheight;
-         s->last_cw = cw;
-         full_update = 1;
-@@ -1845,6 +1848,8 @@ static void vga_invalidate_display(void *opaque)
- 
-     s->last_width = -1;
-     s->last_height = -1;
-+    s->last_text_width = -1;
-+    s->last_text_height = -1;
- }
- 
- void vga_common_reset(VGACommonState *s)
-@@ -1887,6 +1892,8 @@ void vga_common_reset(VGACommonState *s)
-     s->last_ch = 0;
-     s->last_width = 0;
-     s->last_height = 0;
-+    s->last_text_width = 0;
-+    s->last_text_height = 0;
-     s->last_scr_width = 0;
-     s->last_scr_height = 0;
-     s->cursor_start = 0;
-@@ -1938,8 +1945,8 @@ static void vga_update_text(void *opaque, uint32_t *chardata)
-         s->graphic_mode = graphic_mode;
-         full_update = 1;
-     }
--    if (s->last_width == -1) {
--        s->last_width = 0;
-+    if (s->last_text_width == -1) {
-+        s->last_text_width = 0;
-         full_update = 1;
-     }
- 
-@@ -1978,15 +1985,15 @@ static void vga_update_text(void *opaque, uint32_t *chardata)
-             break;
-         }
- 
--        if (width != s->last_width || height != s->last_height ||
-+        if (width != s->last_text_width || height != s->last_text_height ||
-             cw != s->last_cw || cheight != s->last_ch) {
-             s->last_scr_width = width * cw;
-             s->last_scr_height = height * cheight;
-             qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height);
-             qemu_console_text_resize(s->con, width, height);
-             s->last_depth = 0;
--            s->last_width = width;
--            s->last_height = height;
-+            s->last_text_width = width;
-+            s->last_text_height = height;
-             s->last_ch = cheight;
-             s->last_cw = cw;
-             full_update = 1;
-@@ -2071,22 +2078,22 @@ static void vga_update_text(void *opaque, uint32_t *chardata)
-     }
- 
-     /* Display a message */
--    s->last_width = 60;
--    s->last_height = height = 3;
-+    s->last_text_width = 60;
-+    s->last_text_height = height = 3;
-     qemu_console_text_set_cursor(s->con, -1, -1);
--    qemu_console_text_resize(s->con, s->last_width, height);
-+    qemu_console_text_resize(s->con, s->last_text_width, height);
- 
--    for (dst = chardata, i = 0; i < s->last_width * height; i ++)
-+    for (dst = chardata, i = 0; i < s->last_text_width * height; i ++)
-         *dst++ = ' ';
- 
-     size = strlen(msg_buffer);
--    width = (s->last_width - size) / 2;
--    dst = chardata + s->last_width + width;
-+    width = (s->last_text_width - size) / 2;
-+    dst = chardata + s->last_text_width + width;
-     for (i = 0; i < size; i ++)
-         *dst++ = ATTR2CHTYPE(msg_buffer[i], QEMU_COLOR_BLUE,
-                              QEMU_COLOR_BLACK, 1);
- 
--    qemu_console_text_update(s->con, 0, 0, s->last_width, height);
-+    qemu_console_text_update(s->con, 0, 0, s->last_text_width, height);
- }
- 
- static uint64_t vga_mem_read(void *opaque, hwaddr addr,
-diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
-index 5664317ecd..ca69ae9815 100644
---- a/hw/display/vga_int.h
-+++ b/hw/display/vga_int.h
-@@ -122,7 +122,8 @@ typedef struct VGACommonState {
-     uint32_t plane_updated;
-     uint32_t last_line_offset;
-     uint8_t last_cw, last_ch;
--    uint32_t last_width, last_height; /* in chars or pixels */
-+    uint32_t last_width, last_height; /* in pixels (graphics renderer) */
-+    uint32_t last_text_width, last_text_height; /* in chars (text renderer) */
-     uint32_t last_scr_width, last_scr_height; /* in pixels */
-     uint32_t last_depth; /* in bits */
-     bool last_byteswap;
diff --git a/debian/patches/extra/0007-scsi-disk-fix-out-of-bound-read-in-WRITE-SAME.patch b/debian/patches/extra/0007-scsi-disk-fix-out-of-bound-read-in-WRITE-SAME.patch
deleted file mode 100644
index ffab5481da..0000000000
--- a/debian/patches/extra/0007-scsi-disk-fix-out-of-bound-read-in-WRITE-SAME.patch
+++ /dev/null
@@ -1,71 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Paolo Bonzini <pbonzini@redhat.com>
-Date: Wed, 26 Aug 2026 19:22:41 +0200
-Subject: [PATCH] scsi-disk: fix out-of-bound read in WRITE SAME
-
-A guest with an attached scsi-hd can force QEMU's SCSI disk emulation
-to read roughly 60 KiB past the end of a heap buffer, copying that out
-of bounds host memory into the guest's own disk image.
-
-WRITE SAME computes the request transfer length at dev->blocksize
-when the request is prepared and sets cmd->xfer from dev->blocksize.
-scsi_disk_emulate_command() then uses cmd->xfer as the size of the
-request buffer.
-
-However, MODE SELECT can race with the WRITE SAME command and guest raise
-the logical block size to any value whose low bits fit 0xfe00, up to 65024.
-In the presence of this race, scsi_disk_emulate_write_same() will read
-from memory as many bytes as indicated by the *new* dev->blocksize,
-and write it to disk.
-
-The read length in WRITE SAME must be bounded by the buffer that was
-actually allocated, not by the mutable s->qdev.blocksize, so clamp the
-length used against inbuf to r->buflen.  Re-validating req->cmd.xfer
-against the current block size would not work because the race is
-intrinsic in the SCSI protocol.
-
-I am not sure if this is exploitable with virtio-scsi and other SG-capable
-HBAs, because it should process the WRITE SAME input immediately,
-without letting the MODE SELECT command race with it.
-
-Fixes: 356c4c441ec ("scsi-disk: allow MODE SELECT block descriptor to set the block size", 2022-07-13)
-Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4365
-Cc: qemu-stable@nongnu.org
-Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
-Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-(cherry picked from commit d30254aec93141d6b89262986a62764b2ae07177)
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
----
- hw/scsi/scsi-disk.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
-diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
-index 1b0cce128c..5bb7a974d6 100644
---- a/hw/scsi/scsi-disk.c
-+++ b/hw/scsi/scsi-disk.c
-@@ -1911,6 +1911,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
-     SCSIRequest *req = &r->req;
-     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
-     uint32_t nb_sectors = scsi_data_cdb_xfer(r->req.cmd.buf);
-+    uint32_t buflen = MIN(s->qdev.blocksize, r->buflen);
-     WriteSameCBData *data;
-     uint8_t *buf;
-     int i, l;
-@@ -1930,7 +1931,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
-         return;
-     }
- 
--    if ((req->cmd.buf[1] & 0x1) || buffer_is_zero(inbuf, s->qdev.blocksize)) {
-+    if ((req->cmd.buf[1] & 0x1) || buffer_is_zero(inbuf, buflen)) {
-         int flags = (req->cmd.buf[1] & 0x8) ? BDRV_REQ_MAY_UNMAP : 0;
- 
-         /* The request is used as the AIO opaque value, so add a ref.  */
-@@ -1956,7 +1957,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
-     qemu_iovec_init_external(&data->qiov, &data->iov, 1);
- 
-     for (i = 0; i < data->iov.iov_len; i += l) {
--        l = MIN(s->qdev.blocksize, data->iov.iov_len - i);
-+        l = MIN(buflen, data->iov.iov_len - i);
-         memcpy(&buf[i], inbuf, l);
-     }
- 
diff --git a/debian/patches/extra/0008-scsi-hide-MODE-SELECT-block-size-change-behind-a-qui.patch b/debian/patches/extra/0008-scsi-hide-MODE-SELECT-block-size-change-behind-a-qui.patch
deleted file mode 100644
index 28b60a97d2..0000000000
--- a/debian/patches/extra/0008-scsi-hide-MODE-SELECT-block-size-change-behind-a-qui.patch
+++ /dev/null
@@ -1,89 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Paolo Bonzini <pbonzini@redhat.com>
-Date: Wed, 26 Aug 2026 19:53:31 +0200
-Subject: [PATCH] scsi: hide MODE SELECT block size change behind a quirk
-
-This is a dangerous operation in that the block size is not
-protected by a lock, but it can be written concurrently if
-you have a multi-queue virtio-scsi HBA.  Put it behind a quirk
-that is only enabled by the Q800 machine, since the MODE
-SELECT feature was added for A/UX.
-
-Cc: qemu-stable@nongnu.org
-Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
-Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-(cherry picked from commit 0f410adf6b0dead1f09938054da639954bda3bc1)
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
----
- hw/m68k/q800.c         |  2 ++
- hw/scsi/scsi-disk.c    | 12 ++++++++++--
- include/hw/scsi/scsi.h |  1 +
- 3 files changed, 13 insertions(+), 2 deletions(-)
-
-diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c
-index ab64250c47..1ad3f93b29 100644
---- a/hw/m68k/q800.c
-+++ b/hw/m68k/q800.c
-@@ -710,12 +710,14 @@ static void q800_init(Object *obj)
- 
- static GlobalProperty hw_compat_q800[] = {
-     { "scsi-hd", "quirk_mode_page_vendor_specific_apple", "on" },
-+    { "scsi-hd", "quirk_mode_page_set_block_size", "on" },
-     { "scsi-hd", "vendor", " SEAGATE" },
-     { "scsi-hd", "product", "          ST225N" },
-     { "scsi-hd", "ver", "1.0 " },
-     { "scsi-cd", "quirk_mode_page_apple_vendor", "on" },
-     { "scsi-cd", "quirk_mode_sense_rom_use_dbd", "on" },
-     { "scsi-cd", "quirk_mode_page_vendor_specific_apple", "on" },
-+    { "scsi-cd", "quirk_mode_page_set_block_size", "on" },
-     { "scsi-cd", "quirk_mode_page_truncated", "on" },
-     { "scsi-cd", "vendor", "MATSHITA" },
-     { "scsi-cd", "product", "CD-ROM CR-8005" },
-diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
-index 5bb7a974d6..a42f7d8e77 100644
---- a/hw/scsi/scsi-disk.c
-+++ b/hw/scsi/scsi-disk.c
-@@ -1673,8 +1673,12 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf)
-         goto invalid_param;
-     }
- 
--    /* Allow changing the block size */
--    if (bd_len) {
-+    /*
-+     * Allow changing the block size only if the quirk is enabled for it.
-+     * Writing s->qdev.blocksize is not thread safe!
-+     */
-+    if (bd_len && (s->quirks &
-+                   (1 << SCSI_DISK_QUIRK_MODE_PAGE_SET_BLOCK_SIZE))) {
-         bs = p[5] << 16 | p[6] << 8 | p[7];
- 
-         /*
-@@ -3247,6 +3251,8 @@ static const Property scsi_hd_properties[] = {
-     DEFINE_PROP_BIT("quirk_mode_page_vendor_specific_apple", SCSIDiskState,
-                     quirks, SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE,
-                     0),
-+    DEFINE_PROP_BIT("quirk_mode_page_set_block_size", SCSIDiskState,
-+                    quirks, SCSI_DISK_QUIRK_MODE_PAGE_SET_BLOCK_SIZE, 0),
-     DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf),
- };
- 
-@@ -3352,6 +3358,8 @@ static const Property scsi_cd_properties[] = {
-                     0),
-     DEFINE_PROP_BIT("quirk_mode_page_truncated", SCSIDiskState, quirks,
-                     SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED, 0),
-+    DEFINE_PROP_BIT("quirk_mode_page_set_block_size", SCSIDiskState,
-+                    quirks, SCSI_DISK_QUIRK_MODE_PAGE_SET_BLOCK_SIZE, 0),
- };
- 
- static void scsi_cd_class_initfn(ObjectClass *klass, const void *data)
-diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
-index c60c6e8810..3eab339b0d 100644
---- a/include/hw/scsi/scsi.h
-+++ b/include/hw/scsi/scsi.h
-@@ -262,5 +262,6 @@ bool scsi_generic_pr_state_preempt(SCSIDevice *s, Error **errp);
- #define SCSI_DISK_QUIRK_MODE_SENSE_ROM_USE_DBD             1
- #define SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE    2
- #define SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED                3
-+#define SCSI_DISK_QUIRK_MODE_PAGE_SET_BLOCK_SIZE           4
- 
- #endif
diff --git a/debian/patches/extra/0009-vapic-confine-the-VAPIC-region-to-0xc0000.0xe0000.patch b/debian/patches/extra/0009-vapic-confine-the-VAPIC-region-to-0xc0000.0xe0000.patch
deleted file mode 100644
index c0b17c6339..0000000000
--- a/debian/patches/extra/0009-vapic-confine-the-VAPIC-region-to-0xc0000.0xe0000.patch
+++ /dev/null
@@ -1,75 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Paolo Bonzini <pbonzini@redhat.com>
-Date: Wed, 26 Aug 2026 20:02:56 +0200
-Subject: [PATCH] vapic: confine the VAPIC region to 0xc0000..0xe0000
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The VAPIC region is mapped as writable RAM, at very high priority,
-above existing memory.  If the guest is allowed to map it everywhere,
-it can overlap PCI BARs or even SMRAM.  Ensure that the whole
-region first in the 128K of low memory that are reserved to
-option ROMs.
-
-Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4206
-Reported-by: Artem Dinaburg <https://gitlab.com/artem35>
-Cc: qemu-stable@nongnu.org
-Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
-Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-(cherry picked from commit d61c8a6fb7388486353aa267ba0d75b098f16662)
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
----
- hw/i386/vapic.c | 16 +++++++++++++++-
- 1 file changed, 15 insertions(+), 1 deletion(-)
-
-diff --git a/hw/i386/vapic.c b/hw/i386/vapic.c
-index 8dd9188d96..9adce17262 100644
---- a/hw/i386/vapic.c
-+++ b/hw/i386/vapic.c
-@@ -34,6 +34,10 @@
- #define ROM_BLOCK_SIZE          512
- #define ROM_BLOCK_MASK          (~(ROM_BLOCK_SIZE - 1))
- 
-+/* Option ROM window on PC/Q35 machines; the vapic ROM must live in here. */
-+#define OPTION_ROM_START        0xc0000
-+#define OPTION_ROM_END          0xe0000
-+
- typedef enum VAPICMode {
-     VAPIC_INACTIVE = 0,
-     VAPIC_ACTIVE   = 1,
-@@ -592,6 +596,14 @@ static int vapic_map_rom_writable(VAPICROMState *s)
-     size_t rom_size;
-     uint8_t *ram;
- 
-+    /*
-+     * The VAPIC region should be mapped in place, refuse mapping it
-+     * outside of the option ROM window.
-+     */
-+    if (rom_paddr < OPTION_ROM_START || rom_paddr >= OPTION_ROM_END) {
-+        return -1;
-+    }
-+
-     if (s->rom_mapped_writable) {
-         memory_region_del_subregion(mr, &s->rom);
-         object_unparent(OBJECT(&s->rom));
-@@ -606,9 +618,10 @@ static int vapic_map_rom_writable(VAPICROMState *s)
-     }
-     ram = memory_region_get_ram_ptr(section.mr);
-     rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE;
--    if (rom_size == 0) {
-+    if (rom_size == 0 || rom_size > OPTION_ROM_END - rom_paddr) {
-         return -1;
-     }
-+
-     s->rom_size = rom_size;
- 
-     /* We need to round to avoid creating subpages
-@@ -616,6 +629,7 @@ static int vapic_map_rom_writable(VAPICROMState *s)
-     rom_size += rom_paddr & ~TARGET_PAGE_MASK;
-     rom_paddr &= TARGET_PAGE_MASK;
-     rom_size = TARGET_PAGE_ALIGN(rom_size);
-+    assert(rom_paddr >= OPTION_ROM_START && rom_paddr + rom_size <= OPTION_ROM_END);
- 
-     memory_region_init_alias(&s->rom, OBJECT(s), "kvmvapic-rom", section.mr,
-                              rom_paddr, rom_size);
diff --git a/debian/patches/extra/0010-i386-vapic-unref-MemoryRegion-if-vapic_map_rom_writa.patch b/debian/patches/extra/0010-i386-vapic-unref-MemoryRegion-if-vapic_map_rom_writa.patch
deleted file mode 100644
index c232e80930..0000000000
--- a/debian/patches/extra/0010-i386-vapic-unref-MemoryRegion-if-vapic_map_rom_writa.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Paolo Bonzini <pbonzini@redhat.com>
-Date: Wed, 26 Aug 2026 19:47:32 +0200
-Subject: [PATCH] i386/vapic: unref MemoryRegion if vapic_map_rom_writable
- fails
-
-memory_region_find returns the memory region with an elevated
-reference count.  Drop the reference count also if the memory
-region cannot be mapped writable.
-
-Cc: qemu-stable@nongnu.org
-Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-(cherry picked from commit cfa6e2c52ca8f756461fe41f4ce40e61a82fd914)
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
----
- hw/i386/vapic.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/hw/i386/vapic.c b/hw/i386/vapic.c
-index 9adce17262..5c3911cf9c 100644
---- a/hw/i386/vapic.c
-+++ b/hw/i386/vapic.c
-@@ -614,11 +614,13 @@ static int vapic_map_rom_writable(VAPICROMState *s)
- 
-     /* read ROM size from RAM region */
-     if (rom_paddr + 2 >= memory_region_size(section.mr)) {
-+        memory_region_unref(section.mr);
-         return -1;
-     }
-     ram = memory_region_get_ram_ptr(section.mr);
-     rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE;
-     if (rom_size == 0 || rom_size > OPTION_ROM_END - rom_paddr) {
-+        memory_region_unref(section.mr);
-         return -1;
-     }
- 
diff --git a/debian/patches/extra/0011-io-channel-socket-do-not-treat-a-zero-length-write-a.patch b/debian/patches/extra/0011-io-channel-socket-do-not-treat-a-zero-length-write-a.patch
deleted file mode 100644
index 24834b364c..0000000000
--- a/debian/patches/extra/0011-io-channel-socket-do-not-treat-a-zero-length-write-a.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: "Denis V. Lunev" <den@openvz.org>
-Date: Mon, 31 Aug 2026 12:01:46 +0200
-Subject: [PATCH] io/channel-socket: do not treat a zero length write as an
- error
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-qio_channel_socket_writev() checks "ret <= 0" after sendmsg(). A zero
-length iovec is written successfully and returns 0, so the success
-falls into the errno switch, which acts on whatever the last failing
-syscall left in errno. A stale EAGAIN turns it into
-QIO_CHANNEL_ERR_BLOCK with errp untouched, and a caller which treats
-every negative return as fatal then passes a NULL Error to
-error_get_pretty(). The websocket handshake does exactly that, so an
-unauthenticated client crashes QEMU during the greeting.
-
-Returning 0 is safe for callers which loop until everything is
-written. qio_channel_writev_full_all() has no zero progress guard, but
-iov_copy() yields no entries for a zero length write, so that loop is
-never entered. A connected stream socket returns 0 only when there is
-nothing to send.
-
-The WIN32 implementation in the same file uses "ret < 0".
-
-Fixes: CVE-2026-84788
-Fixes: 559607ea173a ("io: add QIOChannelSocket class")
-Cc: qemu-stable@nongnu.org
-Cc: Daniel P. Berrangé <berrange@redhat.com>
-Cc: Marc-André Lureau <marcandre.lureau@redhat.com>
-Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
-Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
-Signed-off-by: Denis V. Lunev <den@openvz.org>
-Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
-(cherry picked from commit a3de21bfa5d0c33110942d407ff5c7903965d3ad)
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
----
- io/channel-socket.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/io/channel-socket.c b/io/channel-socket.c
-index 12773b832c..7920cee639 100644
---- a/io/channel-socket.c
-+++ b/io/channel-socket.c
-@@ -667,7 +667,7 @@ static ssize_t qio_channel_socket_writev(QIOChannel *ioc,
- 
-  retry:
-     ret = sendmsg(sioc->fd, &msg, sflags);
--    if (ret <= 0) {
-+    if (ret < 0) {
-         switch (errno) {
-         case EAGAIN:
-             return QIO_CHANNEL_ERR_BLOCK;
diff --git a/debian/patches/pve/0011-PVE-virtio-balloon-improve-query-balloon.patch b/debian/patches/pve/0011-PVE-virtio-balloon-improve-query-balloon.patch
index c0e337a613..de48b8fe25 100644
--- a/debian/patches/pve/0011-PVE-virtio-balloon-improve-query-balloon.patch
+++ b/debian/patches/pve/0011-PVE-virtio-balloon-improve-query-balloon.patch
@@ -59,10 +59,10 @@ index 686304bafa..474afba207 100644
      qapi_free_BalloonInfo(info);
  }
 diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
-index 4c5f486ba2..58fa33fb4a 100644
+index 07d519cf6d..55c775bfe2 100644
 --- a/hw/virtio/virtio-balloon.c
 +++ b/hw/virtio/virtio-balloon.c
-@@ -793,8 +793,37 @@ static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f,
+@@ -797,8 +797,37 @@ static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f,
  static void virtio_balloon_stat(void *opaque, BalloonInfo *info)
  {
      VirtIOBalloon *dev = opaque;
diff --git a/debian/patches/pve/0015-PVE-add-savevm-async-for-background-state-snapshots.patch b/debian/patches/pve/0015-PVE-add-savevm-async-for-background-state-snapshots.patch
index f04bc2896a..4bc6855b2e 100644
--- a/debian/patches/pve/0015-PVE-add-savevm-async-for-background-state-snapshots.patch
+++ b/debian/patches/pve/0015-PVE-add-savevm-async-for-background-state-snapshots.patch
@@ -883,10 +883,10 @@ index b1eaf7b054..0794a3a7a3 100644
  # @query-migrate:
  #
 diff --git a/qapi/misc.json b/qapi/misc.json
-index 22b7afed9f..786aeb4d3c 100644
+index c71a5fe657..48359a8992 100644
 --- a/qapi/misc.json
 +++ b/qapi/misc.json
-@@ -455,6 +455,31 @@
+@@ -463,6 +463,31 @@
  ##
  { 'command': 'query-fdsets', 'returns': ['FdsetInfo'] }
  
diff --git a/debian/patches/pve/0017-PVE-block-add-the-zeroinit-block-driver-filter.patch b/debian/patches/pve/0017-PVE-block-add-the-zeroinit-block-driver-filter.patch
index 93f0c8d78e..c88df54d03 100644
--- a/debian/patches/pve/0017-PVE-block-add-the-zeroinit-block-driver-filter.patch
+++ b/debian/patches/pve/0017-PVE-block-add-the-zeroinit-block-driver-filter.patch
@@ -247,10 +247,10 @@ index 0000000000..036edb17f5
 +
 +block_init(bdrv_zeroinit_init);
 diff --git a/qapi/block-core.json b/qapi/block-core.json
-index c2f6139dea..c3204f5739 100644
+index 28d9c1e98e..07d16c4a04 100644
 --- a/qapi/block-core.json
 +++ b/qapi/block-core.json
-@@ -3375,7 +3375,7 @@
+@@ -3377,7 +3377,7 @@
              { 'name': 'virtio-blk-vfio-pci', 'if': 'CONFIG_BLKIO' },
              { 'name': 'virtio-blk-vhost-user', 'if': 'CONFIG_BLKIO' },
              { 'name': 'virtio-blk-vhost-vdpa', 'if': 'CONFIG_BLKIO' },
@@ -259,7 +259,7 @@ index c2f6139dea..c3204f5739 100644
  
  ##
  # @BlockdevOptionsFile:
-@@ -4909,7 +4909,8 @@
+@@ -4911,7 +4911,8 @@
                        'if': 'CONFIG_BLKIO' },
        'vmdk':       'BlockdevOptionsGenericCOWFormat',
        'vpc':        'BlockdevOptionsGenericFormat',
diff --git a/debian/patches/pve/0020-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch b/debian/patches/pve/0020-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch
index c52036cfcc..19a9952b75 100644
--- a/debian/patches/pve/0020-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch
+++ b/debian/patches/pve/0020-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch
@@ -119,10 +119,10 @@ index e5b942b6e4..adf9557428 100644
      };
      return raw_co_create(&options, errp);
 diff --git a/qapi/block-core.json b/qapi/block-core.json
-index c3204f5739..338675b98a 100644
+index 07d16c4a04..a12a1a2747 100644
 --- a/qapi/block-core.json
 +++ b/qapi/block-core.json
-@@ -5128,6 +5128,10 @@
+@@ -5130,6 +5130,10 @@
  # @extent-size-hint: Extent size hint to add to the image file; 0 for
  #     not adding an extent size hint (default: 1 MB, since 5.1)
  #
@@ -133,7 +133,7 @@ index c3204f5739..338675b98a 100644
  # Since: 2.12
  ##
  { 'struct': 'BlockdevCreateOptionsFile',
-@@ -5135,7 +5139,8 @@
+@@ -5137,7 +5141,8 @@
              'size':                 'size',
              '*preallocation':       'PreallocMode',
              '*nocow':               'bool',
diff --git a/debian/patches/pve/0025-PVE-Backup-add-backup-dump-block-driver.patch b/debian/patches/pve/0025-PVE-Backup-add-backup-dump-block-driver.patch
index 98e5c0eee7..e23026c00b 100644
--- a/debian/patches/pve/0025-PVE-Backup-add-backup-dump-block-driver.patch
+++ b/debian/patches/pve/0025-PVE-Backup-add-backup-dump-block-driver.patch
@@ -312,7 +312,7 @@ index 147c08155f..41c82fc1ab 100644
      BDRV_TRACKED_READ,
      BDRV_TRACKED_WRITE,
 diff --git a/job.c b/job.c
-index e747908472..5120a01265 100644
+index d7220aaf30..40dd820fba 100644
 --- a/job.c
 +++ b/job.c
 @@ -337,7 +337,8 @@ static bool job_started_locked(Job *job)
diff --git a/debian/patches/pve/0026-PVE-Add-sequential-job-transaction-support.patch b/debian/patches/pve/0026-PVE-Add-sequential-job-transaction-support.patch
index 8f1b20151c..2f1c31a750 100644
--- a/debian/patches/pve/0026-PVE-Add-sequential-job-transaction-support.patch
+++ b/debian/patches/pve/0026-PVE-Add-sequential-job-transaction-support.patch
@@ -34,7 +34,7 @@ index 0e40e8b457..f4fff5fa0d 100644
   * Release a reference that was previously acquired with job_txn_add_job or
   * job_txn_new. If it's the last reference to the object, it will be freed.
 diff --git a/job.c b/job.c
-index 5120a01265..353a0216cd 100644
+index 40dd820fba..6de4c7cfdb 100644
 --- a/job.c
 +++ b/job.c
 @@ -94,6 +94,8 @@ struct JobTxn {
@@ -72,7 +72,7 @@ index 5120a01265..353a0216cd 100644
  /* Called with job_mutex held. */
  static void job_txn_ref_locked(JobTxn *txn)
  {
-@@ -1048,6 +1069,12 @@ static void job_completed_txn_success_locked(Job *job)
+@@ -1055,6 +1076,12 @@ static void job_completed_txn_success_locked(Job *job)
       */
      QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
          if (!job_is_completed_locked(other_job)) {
@@ -85,7 +85,7 @@ index 5120a01265..353a0216cd 100644
              return;
          }
          assert(other_job->ret == 0);
-@@ -1259,6 +1286,13 @@ int job_finish_sync_locked(Job *job,
+@@ -1266,6 +1293,13 @@ int job_finish_sync_locked(Job *job,
          return -EBUSY;
      }
  
diff --git a/debian/patches/pve/0027-PVE-Backup-Proxmox-backup-patches-for-QEMU.patch b/debian/patches/pve/0027-PVE-Backup-Proxmox-backup-patches-for-QEMU.patch
index 67bb0c2f10..e5d76c54c0 100644
--- a/debian/patches/pve/0027-PVE-Backup-Proxmox-backup-patches-for-QEMU.patch
+++ b/debian/patches/pve/0027-PVE-Backup-Proxmox-backup-patches-for-QEMU.patch
@@ -1685,7 +1685,7 @@ index 0000000000..177fb851b4
 +    return ret;
 +}
 diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 338675b98a..2a0cc0fd2a 100644
+index a12a1a2747..e66c455f6e 100644
 --- a/qapi/block-core.json
 +++ b/qapi/block-core.json
 @@ -952,6 +952,248 @@
diff --git a/debian/patches/pve/0029-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch b/debian/patches/pve/0029-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch
index 1af35e10b7..0dbc77f807 100644
--- a/debian/patches/pve/0029-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch
+++ b/debian/patches/pve/0029-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch
@@ -361,10 +361,10 @@ index 91de400570..b1cbaa100e 100644
  summary_info += {'libdaxctl support': libdaxctl}
  summary_info += {'libcbor support':   libcbor}
 diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 2a0cc0fd2a..d3349661ba 100644
+index e66c455f6e..ca18144902 100644
 --- a/qapi/block-core.json
 +++ b/qapi/block-core.json
-@@ -3613,6 +3613,7 @@
+@@ -3615,6 +3615,7 @@
              'parallels', 'preallocate', 'qcow', 'qcow2', 'qed', 'quorum',
              'raw', 'rbd',
              { 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
@@ -372,7 +372,7 @@ index 2a0cc0fd2a..d3349661ba 100644
              'ssh', 'throttle', 'vdi', 'vhdx',
              { 'name': 'virtio-blk-vfio-pci', 'if': 'CONFIG_BLKIO' },
              { 'name': 'virtio-blk-vhost-user', 'if': 'CONFIG_BLKIO' },
-@@ -3699,6 +3700,33 @@
+@@ -3701,6 +3702,33 @@
  { 'struct': 'BlockdevOptionsNull',
    'data': { '*size': 'int', '*latency-ns': 'uint64', '*read-zeroes': 'bool' } }
  
@@ -406,7 +406,7 @@ index 2a0cc0fd2a..d3349661ba 100644
  ##
  # @BlockdevOptionsNVMe:
  #
-@@ -5122,6 +5150,7 @@
+@@ -5124,6 +5152,7 @@
        'nfs':        'BlockdevOptionsNfs',
        'null-aio':   'BlockdevOptionsNull',
        'null-co':    'BlockdevOptionsNull',
diff --git a/debian/patches/pve/0031-PVE-Migrate-dirty-bitmap-state-via-savevm.patch b/debian/patches/pve/0031-PVE-Migrate-dirty-bitmap-state-via-savevm.patch
index 407c9a7e2e..f14c756faa 100644
--- a/debian/patches/pve/0031-PVE-Migrate-dirty-bitmap-state-via-savevm.patch
+++ b/debian/patches/pve/0031-PVE-Migrate-dirty-bitmap-state-via-savevm.patch
@@ -200,7 +200,7 @@ index 177fb851b4..7575abab7c 100644
      ret->pbs_masterkey = true;
      ret->backup_max_workers = true;
 diff --git a/qapi/block-core.json b/qapi/block-core.json
-index d3349661ba..5965a3c1f3 100644
+index ca18144902..91efb9922e 100644
 --- a/qapi/block-core.json
 +++ b/qapi/block-core.json
 @@ -1112,6 +1112,11 @@
diff --git a/debian/patches/pve/0032-migration-block-dirty-bitmap-migrate-other-bitmaps-e.patch b/debian/patches/pve/0032-migration-block-dirty-bitmap-migrate-other-bitmaps-e.patch
index d331b20701..b5394a53b0 100644
--- a/debian/patches/pve/0032-migration-block-dirty-bitmap-migrate-other-bitmaps-e.patch
+++ b/debian/patches/pve/0032-migration-block-dirty-bitmap-migrate-other-bitmaps-e.patch
@@ -19,7 +19,7 @@ Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
  1 file changed, 5 insertions(+), 1 deletion(-)
 
 diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c
-index cba54e25cd..23eb5f40b3 100644
+index 1b8f39c12b..00e35179e6 100644
 --- a/migration/block-dirty-bitmap.c
 +++ b/migration/block-dirty-bitmap.c
 @@ -539,7 +539,11 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs,
diff --git a/debian/patches/pve/0035-block-add-alloc-track-driver.patch b/debian/patches/pve/0035-block-add-alloc-track-driver.patch
index 1f566747f8..b54317dd3e 100644
--- a/debian/patches/pve/0035-block-add-alloc-track-driver.patch
+++ b/debian/patches/pve/0035-block-add-alloc-track-driver.patch
@@ -449,10 +449,10 @@ index d023753091..a777c8079c 100644
  
  out:
 diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 5965a3c1f3..5a9ab23d5d 100644
+index 91efb9922e..80ffcc0547 100644
 --- a/qapi/block-core.json
 +++ b/qapi/block-core.json
-@@ -3606,7 +3606,8 @@
+@@ -3608,7 +3608,8 @@
  # Since: 2.9
  ##
  { 'enum': 'BlockdevDriver',
@@ -462,7 +462,7 @@ index 5965a3c1f3..5a9ab23d5d 100644
              'cloop', 'compress', 'copy-before-write', 'copy-on-read', 'dmg',
              'file', 'snapshot-access', 'ftp', 'ftps',
              {'name': 'host_cdrom', 'if': 'HAVE_HOST_BLOCK_DEVICE' },
-@@ -3706,6 +3707,21 @@
+@@ -3708,6 +3709,21 @@
  { 'struct': 'BlockdevOptionsNull',
    'data': { '*size': 'int', '*latency-ns': 'uint64', '*read-zeroes': 'bool' } }
  
@@ -484,7 +484,7 @@ index 5965a3c1f3..5a9ab23d5d 100644
  ##
  # @BlockdevOptionsPbs:
  #
-@@ -5129,6 +5145,7 @@
+@@ -5131,6 +5147,7 @@
              '*detect-zeroes': 'BlockdevDetectZeroesOptions' },
    'discriminator': 'driver',
    'data': {
diff --git a/debian/patches/pve/0036-PVE-backup-add-fleecing-option.patch b/debian/patches/pve/0036-PVE-backup-add-fleecing-option.patch
index 0368018156..dbcf3bb65f 100644
--- a/debian/patches/pve/0036-PVE-backup-add-fleecing-option.patch
+++ b/debian/patches/pve/0036-PVE-backup-add-fleecing-option.patch
@@ -429,7 +429,7 @@ index 7575abab7c..8b83465ebd 100644
      return ret;
  }
 diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 5a9ab23d5d..2dd422aa03 100644
+index 80ffcc0547..62d7fb4e7a 100644
 --- a/qapi/block-core.json
 +++ b/qapi/block-core.json
 @@ -1054,6 +1054,10 @@
diff --git a/debian/patches/pve/0042-PVE-backup-implement-backup-access-setup-and-teardow.patch b/debian/patches/pve/0042-PVE-backup-implement-backup-access-setup-and-teardow.patch
index f74e82017a..598ed3e441 100644
--- a/debian/patches/pve/0042-PVE-backup-implement-backup-access-setup-and-teardow.patch
+++ b/debian/patches/pve/0042-PVE-backup-implement-backup-access-setup-and-teardow.patch
@@ -740,7 +740,7 @@ index 0000000000..9ebeef7c8f
 +
 +#endif /* PVE_BACKUP_H */
 diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 2dd422aa03..7ef1dd337c 100644
+index 62d7fb4e7a..5e50776664 100644
 --- a/qapi/block-core.json
 +++ b/qapi/block-core.json
 @@ -1128,6 +1128,9 @@
diff --git a/debian/patches/series b/debian/patches/series
index a13019354d..3140ed6230 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,14 +1,7 @@
 extra/0001-monitor-qmp-fix-race-with-clients-disconnecting-earl.patch
 extra/0002-fdmon-io_uring-avoid-idle-event-loop-being-accounted.patch
 extra/0003-block-vmdk-tolerate-known-seSparse-reserved1-bit.patch
-extra/0004-hw-display-qxl-validate-primary-surface-stride-again.patch
-extra/0005-hw-scsi-lsi53c895a-gracefully-handle-re-entrant-DMA.patch
-extra/0006-hw-display-vga-fix-text-mode-OOB-write-after-a-graph.patch
-extra/0007-scsi-disk-fix-out-of-bound-read-in-WRITE-SAME.patch
-extra/0008-scsi-hide-MODE-SELECT-block-size-change-behind-a-qui.patch
-extra/0009-vapic-confine-the-VAPIC-region-to-0xc0000.0xe0000.patch
-extra/0010-i386-vapic-unref-MemoryRegion-if-vapic_map_rom_writa.patch
-extra/0011-io-channel-socket-do-not-treat-a-zero-length-write-a.patch
+extra/0004-hw-scsi-lsi53c895a-gracefully-handle-re-entrant-DMA.patch
 bitmap-mirror/0001-drive-mirror-add-support-for-sync-bitmap-mode-never.patch
 bitmap-mirror/0002-drive-mirror-add-support-for-conditional-and-always-.patch
 bitmap-mirror/0003-mirror-add-check-for-bitmap-mode-without-bitmap.patch
diff --git a/qemu b/qemu
index c3d48b7d1e..4fc49f46dc 160000
--- a/qemu
+++ b/qemu
@@ -1 +1 @@
-Subproject commit c3d48b7d1e89604920e5b81b91140c2ad39a1943
+Subproject commit 4fc49f46dc95d4a27de2509e7fceb2931e91faeb
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH qemu 2/2] more stable fixes for QEMU 11.1
  2026-10-12 10:51 [PATCH-SERIES qemu 0/2] stable fixes for QEMU 11.1 Fiona Ebner
  2026-10-12 10:51 ` [PATCH qemu 1/2] update submodule and patches to QEMU 11.1.2 Fiona Ebner
@ 2026-10-12 10:51 ` Fiona Ebner
  1 sibling, 0 replies; 3+ messages in thread
From: Fiona Ebner @ 2026-10-12 10:51 UTC (permalink / raw)
  To: pve-devel

Pick up a corruption fix for the block layer, fixes to improve
flush error reporting upon close and for qemu-img, a fix for iterating
block nodes, and a hardening for USB emulation.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
---
 ...-Limit-DMA-transfers-to-plain-memory.patch |  85 ++
 ...corruption-from-broken-RMW-atomicity.patch | 124 +++
 ...-redundant-bdrv_wait_serialising_req.patch |  71 ++
 ...rv_next-skipping-monitor-owned-nodes.patch | 118 +++
 ...-cases-for-failing-flush-after-qemu-.patch |  88 ++
 .../0010-iotests.py-add-filter_qemu.patch     |  34 +
 ...report-flush-error-during-bdrv_close.patch | 763 ++++++++++++++++++
 ...ert-flush-target-image-after-copying.patch |  99 +++
 ...return-success-on-info-without-snaps.patch |   4 +-
 ...dd-add-osize-and-read-from-to-stdin-.patch |  12 +-
 ...E-Up-qemu-img-dd-add-isize-parameter.patch |  14 +-
 ...PVE-Up-qemu-img-dd-add-n-skip_create.patch |  10 +-
 ...-add-l-option-for-loading-a-snapshot.patch |  14 +-
 ...-Backup-add-backup-dump-block-driver.patch |   2 +-
 debian/patches/series                         |   8 +
 15 files changed, 1418 insertions(+), 28 deletions(-)
 create mode 100644 debian/patches/extra/0005-hw-usb-hcd-xhci-Limit-DMA-transfers-to-plain-memory.patch
 create mode 100644 debian/patches/extra/0006-block-Fix-corruption-from-broken-RMW-atomicity.patch
 create mode 100644 debian/patches/extra/0007-block-Remove-now-redundant-bdrv_wait_serialising_req.patch
 create mode 100644 debian/patches/extra/0008-block-fix-bdrv_next-skipping-monitor-owned-nodes.patch
 create mode 100644 debian/patches/extra/0009-iotests-add-test-cases-for-failing-flush-after-qemu-.patch
 create mode 100644 debian/patches/extra/0010-iotests.py-add-filter_qemu.patch
 create mode 100644 debian/patches/extra/0011-block-report-flush-error-during-bdrv_close.patch
 create mode 100644 debian/patches/extra/0012-qemu-img-convert-flush-target-image-after-copying.patch

diff --git a/debian/patches/extra/0005-hw-usb-hcd-xhci-Limit-DMA-transfers-to-plain-memory.patch b/debian/patches/extra/0005-hw-usb-hcd-xhci-Limit-DMA-transfers-to-plain-memory.patch
new file mode 100644
index 0000000000..d30c86d8b2
--- /dev/null
+++ b/debian/patches/extra/0005-hw-usb-hcd-xhci-Limit-DMA-transfers-to-plain-memory.patch
@@ -0,0 +1,85 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Thomas Huth <thuth@redhat.com>
+Date: Tue, 15 Sep 2026 14:32:42 +0200
+Subject: [PATCH] hw/usb/hcd-xhci: Limit DMA transfers to plain memory
+
+XHCI is a complex controller, involving lots of descriptors that
+are written and read via DMA transfers. As some recent bugs like
+https://gitlab.com/qemu-project/qemu/-/work_items/3926 revealed,
+this can sometimes be exploited from the guest side to crash or
+stall QEMU.
+
+The code currently does DMA writes with the MEMTXATTRS_UNSPECIFIED
+attribute, i.e. the controller is allowed to write to other MMIO
+regions, too. However, in normal operation, this should not be
+necessary, all descriptors should reside in normal memory. So let's
+decrease the attack surface a little bit and limit the DMA writes
+to normal memory here.
+
+Signed-off-by: Thomas Huth <thuth@redhat.com>
+Message-ID: <20260915123242.549150-1-thuth@redhat.com>
+(cherry picked from commit 89e653b77369ad2d3e579139b1fa2567daa86a79)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ hw/usb/hcd-xhci.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 624ce4ee75..376bc00264 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -518,6 +518,7 @@ static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr,
+     int i;
+     uint32_t tmp[5];
+     uint32_t n = len / sizeof(uint32_t);
++    const MemTxAttrs memtx_attrs = { .memory = true };
+ 
+     assert((len % sizeof(uint32_t)) == 0);
+     assert(n <= ARRAY_SIZE(tmp));
+@@ -525,8 +526,7 @@ static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr,
+     for (i = 0; i < n; i++) {
+         tmp[i] = cpu_to_le32(buf[i]);
+     }
+-    if (dma_memory_write(xhci->as, addr, tmp, len,
+-                         MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
++    if (dma_memory_write(xhci->as, addr, tmp, len, memtx_attrs) != MEMTX_OK) {
+         qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n",
+                       __func__);
+         xhci_die(xhci);
+@@ -613,6 +613,7 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
+     XHCIInterrupter *intr = &xhci->intr[v];
+     XHCITRB ev_trb;
+     dma_addr_t addr;
++    const MemTxAttrs memtx_attrs = { .memory = true };
+ 
+     ev_trb.parameter = cpu_to_le64(event->ptr);
+     ev_trb.status = cpu_to_le32(event->length | (event->ccode << 24));
+@@ -629,7 +630,7 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
+ 
+     addr = intr->er_start + TRB_SIZE*intr->er_ep_idx;
+     if (dma_memory_write(xhci->as, addr, &ev_trb, TRB_SIZE,
+-                         MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
++                         memtx_attrs) != MEMTX_OK) {
+         qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n",
+                       __func__);
+         xhci_die(xhci);
+@@ -2440,6 +2441,7 @@ static void xhci_detach_slot(XHCIState *xhci, USBPort *uport)
+ static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
+ {
+     dma_addr_t ctx;
++    const MemTxAttrs memtx_attrs = { .memory = true };
+ 
+     DPRINTF("xhci_get_port_bandwidth()\n");
+ 
+@@ -2448,9 +2450,9 @@ static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
+     DPRINTF("xhci: bandwidth context at "DMA_ADDR_FMT"\n", ctx);
+ 
+     /* TODO: actually implement real values here. This is 80% for all ports. */
+-    if (stb_dma(xhci->as, ctx, 0, MEMTXATTRS_UNSPECIFIED) != MEMTX_OK ||
++    if (stb_dma(xhci->as, ctx, 0, memtx_attrs) != MEMTX_OK ||
+         dma_memory_set(xhci->as, ctx + 1, 80, xhci->numports,
+-                       MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
++                       memtx_attrs) != MEMTX_OK) {
+         qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory write failed!\n",
+                       __func__);
+         return CC_TRB_ERROR;
diff --git a/debian/patches/extra/0006-block-Fix-corruption-from-broken-RMW-atomicity.patch b/debian/patches/extra/0006-block-Fix-corruption-from-broken-RMW-atomicity.patch
new file mode 100644
index 0000000000..d34d79bb26
--- /dev/null
+++ b/debian/patches/extra/0006-block-Fix-corruption-from-broken-RMW-atomicity.patch
@@ -0,0 +1,124 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Kevin Wolf <kwolf@redhat.com>
+Date: Fri, 2 Oct 2026 15:21:34 +0200
+Subject: [PATCH] block: Fix corruption from broken RMW atomicity
+
+When emulating 512 byte sector devices on top of 4k native host storage,
+QEMU performs a read-modify-write cycle for misaligned write requests.
+These requests are marked as serialising so that any other writes to the
+same 4k block will have to wait and can't overwrite the block in the
+middle, which would make the RMW write back stale data.
+
+In principle, this mechanism works fine, but there is a race condition
+in the implementation: bdrv_find_conflicting_request() assumes that
+req->waiting_for == NULL means that the request is the active owner of
+the data blocks it touches and everyone else has to wait for this
+request. However, there is a window between tracked_request_begin() and
+bdrv_wait_serialising_requests_locked() where any new request has
+req->waiting_for == NULL even if it conflicts with an already running
+serialising request. So at this point, we have seemingly two active
+owners at the same time until the new request checks for conflicts.
+
+For a third request coming in, this isn't a problem, it just might wait
+for a little longer than necessary. However, if the original RMW request
+calls bdrv_wait_serialising_requests_locked() again (which it does in
+bdrv_aligned_pwritev() -> bdrv_co_write_req_prepare() before writing
+back the RMW buffer), it will now wait on the other request that snuck
+in, which is fatal and makes the RMW buffer stale. This scenario causes
+data corruption.
+
+Fix this by checking for conflicts already in tracked_request_begin()
+while holding the bs->reqs_lock mutex, so that the race window is closed
+and req->waiting_for == NULL really has the intended meaning.
+
+For good measure, add an active_owner boolean that is set when a request
+is marked serialising and has completed waiting for other requests, and
+assert that it is not set when waiting for another request, so that in
+the case of another bug, we'd crash instead of causing data corruption.
+
+Cc: qemu-stable@nongnu.org
+Buglink: https://redhat.atlassian.net/browse/RHEL-272836
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+Message-ID: <20261002132135.93345-2-kwolf@redhat.com>
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+(cherry picked from commit 0bd5a1b3d60cca595e19bb73eec83307259be671)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ block/io.c                       | 23 +++++++++++++++++++++++
+ include/block/block_int-common.h |  1 +
+ 2 files changed, 24 insertions(+)
+
+diff --git a/block/io.c b/block/io.c
+index a916b236c3..bd562d472f 100644
+--- a/block/io.c
++++ b/block/io.c
+@@ -50,6 +50,9 @@
+ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
+     int64_t offset, int64_t bytes, BdrvRequestFlags flags);
+ 
++static void coroutine_fn
++bdrv_wait_serialising_requests_locked(BdrvTrackedRequest *self);
++
+ static void GRAPH_RDLOCK
+ bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
+ {
+@@ -616,6 +619,7 @@ static void coroutine_fn tracked_request_begin(BdrvTrackedRequest *req,
+         .type           = type,
+         .co             = qemu_coroutine_self(),
+         .serialising    = false,
++        .active_owner   = false,
+         .overlap_offset = offset,
+         .overlap_bytes  = bytes,
+     };
+@@ -624,6 +628,15 @@ static void coroutine_fn tracked_request_begin(BdrvTrackedRequest *req,
+ 
+     qemu_mutex_lock(&bs->reqs_lock);
+     QLIST_INSERT_HEAD(&bs->tracked_requests, req, list);
++
++    /*
++     * Set .waiting_for while we're holding the lock, otherwise we'd open a race
++     * window where another request in the middle of its RMW cycle starts
++     * waiting for us, leading to potential data corruption if this request
++     * overwrites a block the RMW request has already read.
++     */
++    bdrv_wait_serialising_requests_locked(req);
++
+     qemu_mutex_unlock(&bs->reqs_lock);
+ }
+ 
+@@ -684,6 +697,15 @@ bdrv_wait_serialising_requests_locked(BdrvTrackedRequest *self)
+     BdrvTrackedRequest *req;
+ 
+     while ((req = bdrv_find_conflicting_request(self))) {
++        /*
++         * If this request is the active owner of the data blocks it accesses,
++         * no other conflicting request may exist; other requests have to wait
++         * for this one, not the other way around. If this condition is
++         * violated, RMW operations may be interrupted in the middle, operate on
++         * stale data and introduce data corruption.
++         */
++        assert(!self->active_owner);
++
+         self->waiting_for = req;
+         qemu_co_queue_wait(&req->wait_queue, &self->bs->reqs_lock);
+         self->waiting_for = NULL;
+@@ -803,6 +825,7 @@ void coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req,
+ 
+     tracked_request_set_serialising(req, align);
+     bdrv_wait_serialising_requests_locked(req);
++    req->active_owner = true;
+ 
+     qemu_mutex_unlock(&req->bs->reqs_lock);
+ }
+diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
+index 147c08155f..46428b034d 100644
+--- a/include/block/block_int-common.h
++++ b/include/block/block_int-common.h
+@@ -80,6 +80,7 @@ typedef struct BdrvTrackedRequest {
+     enum BdrvTrackedRequestType type;
+ 
+     bool serialising;
++    bool active_owner;
+     int64_t overlap_offset;
+     int64_t overlap_bytes;
+ 
diff --git a/debian/patches/extra/0007-block-Remove-now-redundant-bdrv_wait_serialising_req.patch b/debian/patches/extra/0007-block-Remove-now-redundant-bdrv_wait_serialising_req.patch
new file mode 100644
index 0000000000..c85c3beac5
--- /dev/null
+++ b/debian/patches/extra/0007-block-Remove-now-redundant-bdrv_wait_serialising_req.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Kevin Wolf <kwolf@redhat.com>
+Date: Fri, 2 Oct 2026 15:21:35 +0200
+Subject: [PATCH] block: Remove now redundant bdrv_wait_serialising_requests()
+ calls
+
+All callers of bdrv_wait_serialising_requests() (as opposed to
+bdrv_wait_serialising_requests_locked()) are just duplicating the check
+that tracked_request_begin() already performs now. After removing them,
+bdrv_wait_serialising_requests() has no callers left and can go as well.
+
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+Message-ID: <20261002132135.93345-3-kwolf@redhat.com>
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+(cherry picked from commit cf4f672c11d663f87462cfd99a92124b30804e99)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ block/io.c | 19 -------------------
+ 1 file changed, 19 deletions(-)
+
+diff --git a/block/io.c b/block/io.c
+index bd562d472f..1c05dd106d 100644
+--- a/block/io.c
++++ b/block/io.c
+@@ -802,20 +802,6 @@ void bdrv_dec_in_flight(BlockDriverState *bs)
+     bdrv_wakeup(bs);
+ }
+ 
+-static void coroutine_fn
+-bdrv_wait_serialising_requests(BdrvTrackedRequest *self)
+-{
+-    BlockDriverState *bs = self->bs;
+-
+-    if (!qatomic_read(&bs->serialising_in_flight)) {
+-        return;
+-    }
+-
+-    qemu_mutex_lock(&bs->reqs_lock);
+-    bdrv_wait_serialising_requests_locked(self);
+-    qemu_mutex_unlock(&bs->reqs_lock);
+-}
+-
+ void coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req,
+                                                 uint64_t align)
+ {
+@@ -1383,8 +1369,6 @@ bdrv_aligned_preadv(BdrvChild *child, BdrvTrackedRequest *req,
+          * it ensures that the CoR read and write operations are atomic and
+          * guest writes cannot interleave between them. */
+         bdrv_make_request_serialising(req, bdrv_get_cluster_size(bs));
+-    } else {
+-        bdrv_wait_serialising_requests(req);
+     }
+ 
+     if (flags & BDRV_REQ_COPY_ON_READ) {
+@@ -2021,8 +2005,6 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes,
+         }
+ 
+         bdrv_wait_serialising_requests_locked(req);
+-    } else {
+-        bdrv_wait_serialising_requests(req);
+     }
+ 
+     assert(req->overlap_offset <= offset);
+@@ -3530,7 +3512,6 @@ static int coroutine_fn GRAPH_RDLOCK bdrv_co_copy_range_internal(
+ 
+         /* BDRV_REQ_SERIALISING is only for write operation */
+         assert(!(read_flags & BDRV_REQ_SERIALISING));
+-        bdrv_wait_serialising_requests(&req);
+ 
+         ret = src->bs->drv->bdrv_co_copy_range_from(src->bs,
+                                                     src, src_offset,
diff --git a/debian/patches/extra/0008-block-fix-bdrv_next-skipping-monitor-owned-nodes.patch b/debian/patches/extra/0008-block-fix-bdrv_next-skipping-monitor-owned-nodes.patch
new file mode 100644
index 0000000000..09bc3f3d7d
--- /dev/null
+++ b/debian/patches/extra/0008-block-fix-bdrv_next-skipping-monitor-owned-nodes.patch
@@ -0,0 +1,118 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: "Denis V. Lunev" <den@openvz.org>
+Date: Sat, 29 Aug 2026 00:20:05 +0200
+Subject: [PATCH] block: fix bdrv_next() skipping monitor-owned nodes
+
+it->bs is the cursor into monitor_bdrv_states for the second phase of
+bdrv_next(), so it has to be NULL when that phase starts. Commit
+f6d38c9f6d made the first phase store the node it returns there, to
+unreference the right one when the graph changes underneath. The cursor
+is now left pointing at the last BlockBackend root, so the second phase
+resumes from there instead of from the head of the list and never
+returns a node added before it.
+
+A skipped node drops out of the vm_stop and migration handover paths:
+not flushed, not inactivated, not snapshotted. bdrv_inactivate_all()
+still reports success, so the node keeps its image lock and the
+migration target cannot open the image. Only detached nodes are hit in
+practice, as these callers also recurse into children.
+
+Reset the cursor when the second phase starts. old_bs is taken at the
+top of the function, so f6d38c9f6d keeps working.
+
+Cc: qemu-stable@nongnu.org
+Fixes: f6d38c9f6d ("block-backend: fix edge case in bdrv_next() where BDS associated to BB changes")
+Signed-off-by: Denis V. Lunev <den@openvz.org>
+Cc: Fiona Ebner <f.ebner@proxmox.com>
+Cc: Kevin Wolf <kwolf@redhat.com>
+Message-ID: <20260828222005.2888213-1-den@openvz.org>
+Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
+Tested-by: Fiona Ebner <f.ebner@proxmox.com>
+Reviewed-by: Kevin Wolf <kwolf@redhat.com>
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+(cherry picked from commit 8daaf8d3b1c5f2ef98e526e036277083bc5fe34b)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ block/block-backend.c                          |  1 +
+ tests/qemu-iotests/tests/inactive-node-nbd     | 16 ++++++++++++++++
+ tests/qemu-iotests/tests/inactive-node-nbd.out |  8 ++++++++
+ 3 files changed, 25 insertions(+)
+
+diff --git a/block/block-backend.c b/block/block-backend.c
+index 37ba7e9fc4..55497c4551 100644
+--- a/block/block-backend.c
++++ b/block/block-backend.c
+@@ -625,6 +625,7 @@ BlockDriverState *bdrv_next(BdrvNextIterator *it)
+             return bs;
+         }
+         it->phase = BDRV_NEXT_MONITOR_OWNED;
++        it->bs = NULL;
+     }
+ 
+     /* Then return the monitor-owned BDSes without a BB attached. Ignore all
+diff --git a/tests/qemu-iotests/tests/inactive-node-nbd b/tests/qemu-iotests/tests/inactive-node-nbd
+index 664157bfd0..ef876ae6be 100755
+--- a/tests/qemu-iotests/tests/inactive-node-nbd
++++ b/tests/qemu-iotests/tests/inactive-node-nbd
+@@ -47,6 +47,7 @@ def node_is_active(_vm, node_name):
+     return node['active']
+ 
+ with iotests.FilePath('disk.img') as path, \
++     iotests.FilePath('detached.img') as detached_path, \
+      iotests.FilePath('snap.qcow2') as snap_path, \
+      iotests.FilePath('snap2.qcow2') as snap2_path, \
+      iotests.FilePath('target.img') as target_path, \
+@@ -58,6 +59,7 @@ with iotests.FilePath('disk.img') as path, \
+     iotests.log('Preparing disk...')
+     iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size)
+     iotests.qemu_img_create('-f', iotests.imgfmt, target_path, img_size)
++    iotests.qemu_img_create('-f', iotests.imgfmt, detached_path, img_size)
+ 
+     iotests.qemu_img_create('-f', 'qcow2', '-b', path, '-F', iotests.imgfmt,
+                             snap_path)
+@@ -68,6 +70,9 @@ with iotests.FilePath('disk.img') as path, \
+     vm.add_blockdev(f'file,node-name=disk-file,filename={path}')
+     vm.add_blockdev(f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt,'
+                      'active=off')
++    vm.add_blockdev(f'file,node-name=detached-file,filename={detached_path}')
++    vm.add_blockdev(f'{iotests.imgfmt},file=detached-file,'
++                     'node-name=detached-fmt')
+     vm.add_blockdev(f'file,node-name=target-file,filename={target_path}')
+     vm.add_blockdev(f'{iotests.imgfmt},file=target-file,node-name=target-fmt')
+     vm.add_blockdev(f'file,node-name=snap-file,filename={snap_path}')
+@@ -297,6 +302,17 @@ with iotests.FilePath('disk.img') as path, \
+     iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt'))
+     iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt'))
+ 
++    iotests.log('\n=== Inactivating all nodes at once ===')
++
++    # detached-fmt has no parent and no BlockBackend, so nothing can reach it
++    # by recursion. It is only inactivated if bdrv_next() actually returns it.
++    vm.qmp_log('stop')
++    vm.qmp_log('blockdev-set-active', active=False)
++
++    iotests.log('detached-fmt active: %s' % node_is_active(vm, 'detached-fmt'))
++    iotests.log('detached-file active: %s'
++                % node_is_active(vm, 'detached-file'))
++
+     iotests.log('\nShutting down...')
+     vm.shutdown()
+     log = vm.get_log()
+diff --git a/tests/qemu-iotests/tests/inactive-node-nbd.out b/tests/qemu-iotests/tests/inactive-node-nbd.out
+index a458b4fc05..48451dc0c5 100644
+--- a/tests/qemu-iotests/tests/inactive-node-nbd.out
++++ b/tests/qemu-iotests/tests/inactive-node-nbd.out
+@@ -235,5 +235,13 @@ snap-fmt active: True
+ snap2-fmt active: True
+ target-fmt active: True
+ 
++=== Inactivating all nodes at once ===
++{"execute": "stop", "arguments": {}}
++{"return": {}}
++{"execute": "blockdev-set-active", "arguments": {"active": false}}
++{"return": {}}
++detached-fmt active: False
++detached-file active: False
++
+ Shutting down...
+ 
diff --git a/debian/patches/extra/0009-iotests-add-test-cases-for-failing-flush-after-qemu-.patch b/debian/patches/extra/0009-iotests-add-test-cases-for-failing-flush-after-qemu-.patch
new file mode 100644
index 0000000000..b25ebdf1b9
--- /dev/null
+++ b/debian/patches/extra/0009-iotests-add-test-cases-for-failing-flush-after-qemu-.patch
@@ -0,0 +1,88 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fiona Ebner <f.ebner@proxmox.com>
+Date: Thu, 8 Oct 2026 08:11:42 -0500
+Subject: [PATCH] iotests: add test cases for failing flush after qemu-img
+ convert
+
+In preparation to add more checks in the following commits and show
+how the behavior changes.
+
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+Message-ID: <20260930132140.2342574-2-f.ebner@proxmox.com>
+Reviewed-by: Eric Blake <eblake@redhat.com>
+Signed-off-by: Eric Blake <eblake@redhat.com>
+Link: https://lore.kernel.org/qemu-devel/20261008131216.2437896-20-eblake@redhat.com
+(pick from qemu-devel)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ tests/qemu-iotests/122     | 33 +++++++++++++++++++++++++++++++++
+ tests/qemu-iotests/122.out | 14 ++++++++++++++
+ 2 files changed, 47 insertions(+)
+
+diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122
+index be0f6b79e5..488649f1bd 100755
+--- a/tests/qemu-iotests/122
++++ b/tests/qemu-iotests/122
+@@ -334,6 +334,39 @@ echo
+ $QEMU_IMG convert -O $IMGFMT -o preallocation=metadata -n \
+ 	  "$TEST_IMG" "$TEST_IMG".orig && echo "unexpected success"
+ 
++echo
++echo "=== failing flush after convert with default ('unsafe') cache mode ==="
++echo
++
++TEST_IMG="$TEST_IMG".orig _make_test_img 1M
++TEST_IMG="$TEST_IMG" _make_test_img 1M
++
++$QEMU_IO -c "write -P 42 0 1M" "$TEST_IMG".orig | _filter_qemu_io
++
++target_fails_flush="json:{
++    'driver': '$IMGFMT',
++    'file': {
++        'driver': 'blkdebug',
++        'inject-error': [{
++            'event': 'flush_to_disk'
++        }],
++        'image': {
++            'driver': 'file',
++            'filename': '$TEST_IMG'
++        }
++    }
++}"
++
++$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG".orig "$target_fails_flush"
++echo "qemu-img convert exited with status $?"
++
++echo
++echo "=== failing flush after convert with 'writeback' cache mode ==="
++echo
++
++$QEMU_IMG convert -t writeback -f $IMGFMT -O $IMGFMT -n "$TEST_IMG".orig "$target_fails_flush"
++echo "qemu-img convert exited with status $?"
++
+ # success, all done
+ echo '*** done'
+ rm -f $seq.full
+diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out
+index 6a1aa3fe2b..f3e28f33bc 100644
+--- a/tests/qemu-iotests/122.out
++++ b/tests/qemu-iotests/122.out
+@@ -256,4 +256,18 @@ Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864
+ === -n incompatible with -o ===
+ 
+ qemu-img: -o has no effect when skipping image creation
++
++=== failing flush after convert with default ('unsafe') cache mode ===
++
++Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=1048576
++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
++wrote 1048576/1048576 bytes at offset 0
++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++qemu-img convert exited with status 0
++
++=== failing flush after convert with 'writeback' cache mode ===
++
++qemu-img: Failed to flush the L2 table cache: Input/output error
++qemu-img: Failed to flush the refcount block cache: Input/output error
++qemu-img convert exited with status 0
+ *** done
diff --git a/debian/patches/extra/0010-iotests.py-add-filter_qemu.patch b/debian/patches/extra/0010-iotests.py-add-filter_qemu.patch
new file mode 100644
index 0000000000..73f0cfab1f
--- /dev/null
+++ b/debian/patches/extra/0010-iotests.py-add-filter_qemu.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fiona Ebner <f.ebner@proxmox.com>
+Date: Thu, 8 Oct 2026 08:11:43 -0500
+Subject: [PATCH] iotests.py: add filter_qemu()
+
+Add a filter_qemu() helper function for replacing the QEMU program
+name with 'QEMU_PROG' in test's log messages.
+
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+Message-ID: <20260930132140.2342574-3-f.ebner@proxmox.com>
+Reviewed-by: Eric Blake <eblake@redhat.com>
+Signed-off-by: Eric Blake <eblake@redhat.com>
+Link: https://lore.kernel.org/qemu-devel/20261008131216.2437896-21-eblake@redhat.com
+(pick from qemu-devel)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ tests/qemu-iotests/iotests.py | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
+index 072be80e07..ed41f88d2a 100644
+--- a/tests/qemu-iotests/iotests.py
++++ b/tests/qemu-iotests/iotests.py
+@@ -596,6 +596,10 @@ def filter_qemu_io(msg):
+     return qemu_io_re.sub("X ops; XX:XX:XX.X "
+                           "(XXX YYY/sec and XXX ops/sec)", msg)
+ 
++qemu_re = re.compile(r"%s" % os.path.basename(qemu_prog))
++def filter_qemu(msg):
++    return qemu_re.sub("QEMU_PROG", msg)
++
+ chown_re = re.compile(r"chown [0-9]+:[0-9]+")
+ def filter_chown(msg):
+     return chown_re.sub("chown UID:GID", msg)
diff --git a/debian/patches/extra/0011-block-report-flush-error-during-bdrv_close.patch b/debian/patches/extra/0011-block-report-flush-error-during-bdrv_close.patch
new file mode 100644
index 0000000000..0d1d3d3df0
--- /dev/null
+++ b/debian/patches/extra/0011-block-report-flush-error-during-bdrv_close.patch
@@ -0,0 +1,763 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fiona Ebner <f.ebner@proxmox.com>
+Date: Thu, 8 Oct 2026 08:11:44 -0500
+Subject: [PATCH] block: report flush error during bdrv_close()
+
+Flush during bdrv_close() was introduced back in 2012 in commit
+80ccf93b88 ("qemu-img: let 'qemu-img convert' flush data"). However, a
+failing flush was quietly ignored [0]. Report the error instead, since
+this could mean that the image contents are not as expected.
+
+Adapt the iotests accordingly. For all tests except '080' and
+'qsd-migrate' the failures come from blkdebug configurations. For
+'080', the failures are also directly expected, because of invalid L1
+table size and offset. For the 'qsd-migrate' test, the error happens,
+because the QSD node which is attached via NBD to the source VM was
+inactivated.
+
+[0]: Note that the errors reported for qcow2 come from
+qcow2_inactivate() which is only called as part of
+bs->drv->bdrv_close() after bdrv_flush(). In particular, adding
+> 'once': 'on'
+to the blkdebug driver in the relevant test case in the 122 iotest
+shows that the failure can be silent in case of qcow2 too.
+
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+Message-ID: <20260930132140.2342574-4-f.ebner@proxmox.com>
+Reviewed-by: Eric Blake <eblake@redhat.com>
+Signed-off-by: Eric Blake <eblake@redhat.com>
+Link: https://lore.kernel.org/qemu-devel/20261008131216.2437896-22-eblake@redhat.com
+(pick from qemu-devel)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ block.c                                    | 13 +++-
+ tests/qemu-iotests/026                     | 10 +--
+ tests/qemu-iotests/026.out                 | 84 ++++++++++++++++++++++
+ tests/qemu-iotests/071                     |  2 +-
+ tests/qemu-iotests/071.out                 |  2 +
+ tests/qemu-iotests/080.out                 |  2 +
+ tests/qemu-iotests/098.out                 |  4 ++
+ tests/qemu-iotests/122                     |  8 +--
+ tests/qemu-iotests/122.out                 |  2 +
+ tests/qemu-iotests/244                     |  2 +-
+ tests/qemu-iotests/244.out                 |  1 +
+ tests/qemu-iotests/tests/file-io-error     |  2 +-
+ tests/qemu-iotests/tests/file-io-error.out |  2 +
+ tests/qemu-iotests/tests/qsd-migrate       |  5 +-
+ tests/qemu-iotests/tests/qsd-migrate.out   |  1 +
+ 15 files changed, 125 insertions(+), 15 deletions(-)
+
+diff --git a/block.c b/block.c
+index f0a6042e61..c05ad01f25 100644
+--- a/block.c
++++ b/block.c
+@@ -5162,12 +5162,23 @@ static void GRAPH_UNLOCKED bdrv_close(BlockDriverState *bs)
+ {
+     BdrvAioNotifier *ban, *ban_next;
+     BdrvChild *child, *next;
++    int ret;
+ 
+     GLOBAL_STATE_CODE();
+     assert(!bs->refcnt);
+ 
+     bdrv_drained_begin(bs); /* complete I/O */
+-    bdrv_flush(bs);
++    ret = bdrv_flush(bs);
++    if (ret < 0) {
++        const char *name;
++        if (bs->filename[0]) {
++            name = bs->filename;
++        } else {
++            name = bdrv_get_node_name(bs);
++        }
++        error_report("Failed flush for '%s' during close: %s", name,
++                     strerror(-ret));
++    }
+     bdrv_drain(bs); /* in case flush left pending I/O */
+ 
+     if (bs->drv) {
+diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026
+index d37e266dad..88cb4dd934 100755
+--- a/tests/qemu-iotests/026
++++ b/tests/qemu-iotests/026
+@@ -103,13 +103,13 @@ if [ "$event" == "l2_update" ]; then
+     $QEMU_IO -c "write $vmstate 0 512" "$TEST_IMG" > /dev/null 2>&1
+ fi
+ 
+-$QEMU_IO -c "write $vmstate 0 128k " "$BLKDBG_TEST_IMG" | _filter_qemu_io
++$QEMU_IO -c "write $vmstate 0 128k " "$BLKDBG_TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+ 
+ # l2_load is not called on allocation, so issue a second write
+ # Reads are another path to trigger l2_load, so do a read, too
+ if [ "$event" == "l2_load" ]; then
+-    $QEMU_IO -c "write $vmstate 0 128k " "$BLKDBG_TEST_IMG" | _filter_qemu_io
+-    $QEMU_IO -c "read $vmstate 0 128k " "$BLKDBG_TEST_IMG" | _filter_qemu_io
++    $QEMU_IO -c "write $vmstate 0 128k " "$BLKDBG_TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
++    $QEMU_IO -c "read $vmstate 0 128k " "$BLKDBG_TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+ fi
+ 
+ _check_test_img_ignore_leaks 2>&1 | grep -v "refcount=1 reference=0"
+@@ -155,7 +155,7 @@ _make_test_img 1G
+ 
+ echo
+ echo "Event: $event; errno: $errno; imm: $imm; once: $once; write $vmstate"
+-$QEMU_IO -c "write $vmstate 0 64M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
++$QEMU_IO -c "write $vmstate 0 64M" "$BLKDBG_TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+ 
+ _check_test_img_ignore_leaks 2>&1 | grep -v "refcount=1 reference=0"
+ 
+@@ -194,7 +194,7 @@ _make_test_img 1G
+ 
+ echo
+ echo "Event: $event; errno: $errno; imm: $imm; once: $once"
+-$QEMU_IO -c "write -b 0 64k" "$BLKDBG_TEST_IMG" | _filter_qemu_io
++$QEMU_IO -c "write -b 0 64k" "$BLKDBG_TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+ 
+ _check_test_img_ignore_leaks 2>&1 | grep -v "refcount=1 reference=0"
+ 
+diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out
+index 83989996ff..a92286fab5 100644
+--- a/tests/qemu-iotests/026.out
++++ b/tests/qemu-iotests/026.out
+@@ -14,15 +14,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l1_update; errno: 5; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l1_update; errno: 5; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -38,15 +42,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l1_update; errno: 28; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l1_update; errno: 28; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -126,15 +134,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l2_update; errno: 5; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l2_update; errno: 5; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -150,15 +162,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l2_update; errno: 28; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l2_update; errno: 28; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -174,15 +190,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l2_alloc_write; errno: 5; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l2_alloc_write; errno: 5; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -198,15 +218,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l2_alloc_write; errno: 28; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l2_alloc_write; errno: 28; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -222,15 +246,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: write_aio; errno: 5; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: write_aio; errno: 5; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -246,15 +274,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: write_aio; errno: 28; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: write_aio; errno: 28; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -270,15 +302,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_load; errno: 5; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_load; errno: 5; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -294,15 +330,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_load; errno: 28; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_load; errno: 28; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -318,15 +358,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_update_part; errno: 5; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_update_part; errno: 5; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -342,15 +386,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_update_part; errno: 28; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_update_part; errno: 28; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -366,15 +414,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc; errno: 5; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc; errno: 5; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -390,15 +442,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc; errno: 28; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc; errno: 28; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -457,15 +513,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -481,15 +541,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc_write; errno: 28; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc_write; errno: 28; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -505,15 +569,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -529,15 +597,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -553,15 +625,19 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write -b
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ 
+@@ -595,8 +671,10 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l1_grow_write_table; errno: 5; imm: off; once: off
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -607,8 +685,10 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l1_grow_write_table; errno: 28; imm: off; once: off
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -619,8 +699,10 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l1_grow_activate_table; errno: 5; imm: off; once: off
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ qemu-io: Failed to flush the L2 table cache: Input/output error
+ qemu-io: Failed to flush the refcount block cache: Input/output error
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: Input/output error
+ write failed: Input/output error
+ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+@@ -631,8 +713,10 @@ No errors were found on the image.
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+ 
+ Event: l1_grow_activate_table; errno: 28; imm: off; once: off
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ qemu-io: Failed to flush the L2 table cache: No space left on device
+ qemu-io: Failed to flush the refcount block cache: No space left on device
++qemu-io: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.qcow2' during close: No space left on device
+ write failed: No space left on device
+ No errors were found on the image.
+ 
+diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071
+index 331f8cfddc..3058db5bce 100755
+--- a/tests/qemu-iotests/071
++++ b/tests/qemu-iotests/071
+@@ -53,7 +53,7 @@ do_run_qemu()
+ 
+ run_qemu()
+ {
+-    do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp | _filter_qemu_io
++    do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp | _filter_qemu_io | _filter_json_filename
+ }
+ 
+ IMG_SIZE=64M
+diff --git a/tests/qemu-iotests/071.out b/tests/qemu-iotests/071.out
+index a2923b05c2..4014ae2bb3 100644
+--- a/tests/qemu-iotests/071.out
++++ b/tests/qemu-iotests/071.out
+@@ -85,8 +85,10 @@ wrote 512/512 bytes at offset 0
+ read failed: Input/output error
+ {"return": ""}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++QEMU_PROG: Failed flush for 'json:{ /* filtered */ }' during close: Input/output error
+ QEMU_PROG: Failed to flush the L2 table cache: Input/output error
+ QEMU_PROG: Failed to flush the refcount block cache: Input/output error
++QEMU_PROG: Failed flush for 'json:{ /* filtered */ }' during close: Input/output error
+ {"return": {}}
+ 
+ *** done
+diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out
+index d8acb3e723..4534ca1219 100644
+--- a/tests/qemu-iotests/080.out
++++ b/tests/qemu-iotests/080.out
+@@ -66,6 +66,7 @@ wrote 512/512 bytes at offset 0
+ qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid
+ qemu-img: Snapshot L1 table offset invalid
+ qemu-img: Failed to turn zero into data clusters: Invalid argument
++qemu-io: Failed flush for 'TEST_DIR/t.qcow2' during close: Invalid argument
+ qemu-io: Failed to flush the refcount block cache: Invalid argument
+ write failed: Invalid argument
+ qemu-img: Snapshot L1 table offset invalid
+@@ -89,6 +90,7 @@ wrote 512/512 bytes at offset 0
+ qemu-img: Failed to load snapshot: Snapshot L1 table too large
+ qemu-img: Snapshot L1 table too large
+ qemu-img: Failed to turn zero into data clusters: File too large
++qemu-io: Failed flush for 'TEST_DIR/t.qcow2' during close: File too large
+ qemu-io: Failed to flush the refcount block cache: File too large
+ write failed: File too large
+ qemu-img: Snapshot L1 table too large
+diff --git a/tests/qemu-iotests/098.out b/tests/qemu-iotests/098.out
+index 6c73dd0977..912b0290af 100644
+--- a/tests/qemu-iotests/098.out
++++ b/tests/qemu-iotests/098.out
+@@ -6,6 +6,7 @@ Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+ wrote 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++qemu-img: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT' during close: Input/output error
+ qemu-img: Failed to empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
+ No errors were found on the image.
+ 
+@@ -15,6 +16,7 @@ Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+ wrote 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++qemu-img: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT' during close: Input/output error
+ qemu-img: Failed to empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
+ Leaked cluster 4 refcount=1 reference=0
+ Leaked cluster 5 refcount=1 reference=0
+@@ -28,6 +30,7 @@ Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+ wrote 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++qemu-img: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT' during close: Input/output error
+ qemu-img: Failed to empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
+ ERROR cluster 0 refcount=0 reference=1
+ ERROR cluster 1 refcount=0 reference=1
+@@ -42,6 +45,7 @@ Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+ wrote 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++qemu-img: Failed flush for 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT' during close: Input/output error
+ qemu-img: Failed to empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
+ ERROR cluster 0 refcount=0 reference=1
+ ERROR cluster 1 refcount=0 reference=1
+diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122
+index 488649f1bd..9da1246f09 100755
+--- a/tests/qemu-iotests/122
++++ b/tests/qemu-iotests/122
+@@ -357,15 +357,15 @@ target_fails_flush="json:{
+     }
+ }"
+ 
+-$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG".orig "$target_fails_flush"
+-echo "qemu-img convert exited with status $?"
++$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG".orig "$target_fails_flush" 2>&1 | _filter_json_filename
++echo "qemu-img convert exited with status ${PIPESTATUS[0]}"
+ 
+ echo
+ echo "=== failing flush after convert with 'writeback' cache mode ==="
+ echo
+ 
+-$QEMU_IMG convert -t writeback -f $IMGFMT -O $IMGFMT -n "$TEST_IMG".orig "$target_fails_flush"
+-echo "qemu-img convert exited with status $?"
++$QEMU_IMG convert -t writeback -f $IMGFMT -O $IMGFMT -n "$TEST_IMG".orig "$target_fails_flush" 2>&1 | _filter_json_filename
++echo "qemu-img convert exited with status ${PIPESTATUS[0]}"
+ 
+ # success, all done
+ echo '*** done'
+diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out
+index f3e28f33bc..84283f2434 100644
+--- a/tests/qemu-iotests/122.out
++++ b/tests/qemu-iotests/122.out
+@@ -267,7 +267,9 @@ qemu-img convert exited with status 0
+ 
+ === failing flush after convert with 'writeback' cache mode ===
+ 
++qemu-img: Failed flush for 'json:{ /* filtered */ }' during close: Input/output error
+ qemu-img: Failed to flush the L2 table cache: Input/output error
+ qemu-img: Failed to flush the refcount block cache: Input/output error
++qemu-img: Failed flush for 'json:{ /* filtered */ }' during close: Input/output error
+ qemu-img convert exited with status 0
+ *** done
+diff --git a/tests/qemu-iotests/244 b/tests/qemu-iotests/244
+index 2a98ad58f5..ef32f8ec03 100755
+--- a/tests/qemu-iotests/244
++++ b/tests/qemu-iotests/244
+@@ -264,7 +264,7 @@ $QEMU_IO -c 'write 0 512' -c flush \
+              }
+          }
+      }" \
+-    | _filter_qemu_io
++    2>&1 | _filter_qemu_io | _filter_generated_node_ids
+ 
+ result=${PIPESTATUS[0]}
+ echo
+diff --git a/tests/qemu-iotests/244.out b/tests/qemu-iotests/244.out
+index c62f5aec25..8935f544cd 100644
+--- a/tests/qemu-iotests/244.out
++++ b/tests/qemu-iotests/244.out
+@@ -134,6 +134,7 @@ Images are identical.
+ 
+ === Flushing should flush the data file ===
+ 
++qemu-io: Failed flush for 'NODE_NAME' during close: Input/output error
+ wrote 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ 
+diff --git a/tests/qemu-iotests/tests/file-io-error b/tests/qemu-iotests/tests/file-io-error
+index 0d970c102f..3885b741d9 100755
+--- a/tests/qemu-iotests/tests/file-io-error
++++ b/tests/qemu-iotests/tests/file-io-error
+@@ -94,7 +94,7 @@ echo "$output"
+ 
+ echo
+ # This should fail, but gracefully, i.e. just print an I/O error, not crash.
+-$QEMU_IO -f file -c 'write 0 64M' "$TEST_DIR/fuse-export" | _filter_qemu_io
++$QEMU_IO -f file -c 'write 0 64M' "$TEST_DIR/fuse-export" 2>&1 | _filter_qemu_io | _filter_testdir
+ echo
+ 
+ capture_events=BLOCK_EXPORT_DELETED _send_qemu_cmd $QEMU_HANDLE \
+diff --git a/tests/qemu-iotests/tests/file-io-error.out b/tests/qemu-iotests/tests/file-io-error.out
+index 0f46455a94..b5ac8d3db1 100644
+--- a/tests/qemu-iotests/tests/file-io-error.out
++++ b/tests/qemu-iotests/tests/file-io-error.out
+@@ -21,6 +21,7 @@ QA output created by file-io-error
+       }}
+ {"return": {}}
+ 
++qemu-io: Failed flush for 'TEST_DIR/fuse-export' during close: Input/output error
+ write failed: Input/output error
+ 
+ {'execute': 'block-export-del',
+@@ -29,5 +30,6 @@ write failed: Input/output error
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "exp0"}}
+ {'execute': 'blockdev-del',
+       'arguments': {'node-name': 'node0'}}
++QEMU_PROG: Failed flush for 'node0' during close: Input/output error
+ {"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/tests/qsd-migrate b/tests/qemu-iotests/tests/qsd-migrate
+index a4c6592420..3fb5b2e475 100755
+--- a/tests/qemu-iotests/tests/qsd-migrate
++++ b/tests/qemu-iotests/tests/qsd-migrate
+@@ -20,7 +20,7 @@
+ 
+ import iotests
+ 
+-from iotests import filter_qemu_io, filter_qtest
++from iotests import filter_qemu, filter_qemu_io, filter_qtest
+ 
+ iotests.script_initialize(supported_fmts=['qcow2', 'qed', 'raw'],
+                           supported_protocols=['file'],
+@@ -125,11 +125,12 @@ with iotests.FilePath('disk.img') as path, \
+ 
+     iotests.log('\nDone')
+ 
++    # NOTE: Flush during close fails here, since the QSD node was inactivated.
+     vm_src.shutdown()
+     iotests.log('\n--- vm_src log ---')
+     log = vm_src.get_log()
+     if log:
+-        iotests.log(log, [filter_qtest, filter_qemu_io])
++        iotests.log(log, [filter_qtest, filter_qemu_io, filter_qemu])
+     qsd_src.stop()
+ 
+     vm_dst.shutdown()
+diff --git a/tests/qemu-iotests/tests/qsd-migrate.out b/tests/qemu-iotests/tests/qsd-migrate.out
+index 4a5241e5d4..894b232b4a 100644
+--- a/tests/qemu-iotests/tests/qsd-migrate.out
++++ b/tests/qemu-iotests/tests/qsd-migrate.out
+@@ -47,6 +47,7 @@ read 4096/4096 bytes at offset 0
+ 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ read 4096/4096 bytes at offset 0
+ 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++QEMU_PROG: Failed flush for 'disk' during close: Operation not permitted
+ 
+ --- vm_dst log ---
+ read 4096/4096 bytes at offset 0
diff --git a/debian/patches/extra/0012-qemu-img-convert-flush-target-image-after-copying.patch b/debian/patches/extra/0012-qemu-img-convert-flush-target-image-after-copying.patch
new file mode 100644
index 0000000000..b7962dfd96
--- /dev/null
+++ b/debian/patches/extra/0012-qemu-img-convert-flush-target-image-after-copying.patch
@@ -0,0 +1,99 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fiona Ebner <f.ebner@proxmox.com>
+Date: Thu, 8 Oct 2026 08:11:45 -0500
+Subject: [PATCH] qemu-img: convert: flush target image after copying
+
+With 'qemu-img convert', when using a cache mode with 'cache.no-flush
+= off', the expectation is that the data is flushed to the disk, which
+happens since commit 80ccf93b88 ("qemu-img: let 'qemu-img convert'
+flush data"). This expectation also encompasses that failure to do so
+is properly reported, so ensure that 'qemu-img convert' exits with a
+non-zero status code when the target image cannot be flushed.
+
+In particular, the reproducer below [0], based on a report by
+Ing. Alfonso Kuen Arroyo [1], would result in
+> rc=0
+> Content mismatch at offset XYZ!
+before this change, and in
+> rc=1
+> Content mismatch at offset XYZ!
+after this change.
+
+Note that this change does not affect the (default) 'unsafe' cache
+mode, because bdrv_co_flush() still skips flush to disk for that cache
+mode.
+
+[0]:
+> #!/bin/bash -x
+> dd if=/dev/urandom of=src.raw bs=1M count=500
+> truncate -s 500M dst.img
+> LOOP=$(losetup --find --show dst.img)
+> SIZE=$(blockdev --getsz $LOOP)
+> dd if=/dev/zero of=$LOOP bs=1M count=500 conv=fsync status=none
+> sync
+> echo 3 > /proc/sys/vm/drop_caches
+> dmsetup create flk --table "0 $SIZE flakey $LOOP 0 5 5"
+> qemu-img convert -t writeback -p -r 100M -f raw -O raw src.raw /dev/mapper/flk
+> echo rc=$?
+> sleep 5
+> dmsetup remove flk
+> qemu-img compare src.raw $LOOP
+> losetup -d $LOOP
+> rm src.raw dst.img
+
+[1]: https://lore.proxmox.com/pve-devel/178961792132.43804.17173233935297903311@idkmanager.com/
+
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+Message-ID: <20260930132140.2342574-5-f.ebner@proxmox.com>
+Reviewed-by: Eric Blake <eblake@redhat.com>
+Signed-off-by: Eric Blake <eblake@redhat.com>
+Link: https://lore.kernel.org/qemu-devel/20261008131216.2437896-23-eblake@redhat.com
+(pick from qemu-devel)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ qemu-img.c                 | 8 +++++++-
+ tests/qemu-iotests/122.out | 3 ++-
+ 2 files changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/qemu-img.c b/qemu-img.c
+index 2f63d31141..edf75e43ab 100644
+--- a/qemu-img.c
++++ b/qemu-img.c
+@@ -2245,7 +2245,7 @@ static int img_convert(const img_cmd_t *ccmd, int argc, char **argv)
+     Error *local_err = NULL;
+     bool writethrough, src_writethrough, image_opts = false,
+          skip_create = false, progress = false, tgt_image_opts = false;
+-    int64_t ret = -EINVAL;
++    int64_t ret = -EINVAL, ret2;
+     bool force_share = false;
+     bool explict_min_sparse = false;
+     bool bitmaps = false;
+@@ -2872,6 +2872,12 @@ static int img_convert(const img_cmd_t *ccmd, int argc, char **argv)
+         ret = convert_copy_bitmaps(blk_bs(s.src[0]), out_bs, skip_broken);
+     }
+ 
++    ret2 = blk_flush(s.target);
++    if (ret2 < 0) {
++        error_report("Failed to flush target image: %s", strerror(-ret2));
++        ret = ret != 0 ? ret : ret2;
++    }
++
+ out:
+     if (!ret) {
+         qemu_progress_print(100, 0);
+diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out
+index 84283f2434..38794f96ac 100644
+--- a/tests/qemu-iotests/122.out
++++ b/tests/qemu-iotests/122.out
+@@ -267,9 +267,10 @@ qemu-img convert exited with status 0
+ 
+ === failing flush after convert with 'writeback' cache mode ===
+ 
++qemu-img: Failed to flush target image: Input/output error
+ qemu-img: Failed flush for 'json:{ /* filtered */ }' during close: Input/output error
+ qemu-img: Failed to flush the L2 table cache: Input/output error
+ qemu-img: Failed to flush the refcount block cache: Input/output error
+ qemu-img: Failed flush for 'json:{ /* filtered */ }' during close: Input/output error
+-qemu-img convert exited with status 0
++qemu-img convert exited with status 1
+ *** done
diff --git a/debian/patches/pve/0006-PVE-Up-qemu-img-return-success-on-info-without-snaps.patch b/debian/patches/pve/0006-PVE-Up-qemu-img-return-success-on-info-without-snaps.patch
index e872eb361d..0c61285b6f 100644
--- a/debian/patches/pve/0006-PVE-Up-qemu-img-return-success-on-info-without-snaps.patch
+++ b/debian/patches/pve/0006-PVE-Up-qemu-img-return-success-on-info-without-snaps.patch
@@ -9,10 +9,10 @@ Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
  1 file changed, 2 insertions(+), 1 deletion(-)
 
 diff --git a/qemu-img.c b/qemu-img.c
-index 2f63d31141..2f9e8e77b8 100644
+index edf75e43ab..43f86aa773 100644
 --- a/qemu-img.c
 +++ b/qemu-img.c
-@@ -3185,7 +3185,8 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -3191,7 +3191,8 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv)
      list = collect_image_info_list(image_opts, filename, fmt, cache, chain,
                                     limits, force_share);
      if (!list) {
diff --git a/debian/patches/pve/0007-PVE-Up-qemu-img-dd-add-osize-and-read-from-to-stdin-.patch b/debian/patches/pve/0007-PVE-Up-qemu-img-dd-add-osize-and-read-from-to-stdin-.patch
index 9184458394..355c333078 100644
--- a/debian/patches/pve/0007-PVE-Up-qemu-img-dd-add-osize-and-read-from-to-stdin-.patch
+++ b/debian/patches/pve/0007-PVE-Up-qemu-img-dd-add-osize-and-read-from-to-stdin-.patch
@@ -55,10 +55,10 @@ index 6bc8265cfb..85458ea2d8 100644
  
  DEF("info", img_info,
 diff --git a/qemu-img.c b/qemu-img.c
-index 2f9e8e77b8..dad04c865e 100644
+index 43f86aa773..82856a38a8 100644
 --- a/qemu-img.c
 +++ b/qemu-img.c
-@@ -5320,10 +5320,12 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5326,10 +5326,12 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
  #define C_IF      04
  #define C_OF      010
  #define C_SKIP    020
@@ -71,7 +71,7 @@ index 2f9e8e77b8..dad04c865e 100644
  };
  
  struct DdIo {
-@@ -5399,6 +5401,19 @@ static int img_dd_skip(const char *arg,
+@@ -5405,6 +5407,19 @@ static int img_dd_skip(const char *arg,
      return 0;
  }
  
@@ -91,7 +91,7 @@ index 2f9e8e77b8..dad04c865e 100644
  static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
  {
      int ret = 0;
-@@ -5439,6 +5454,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5445,6 +5460,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
          { "if", img_dd_if, C_IF },
          { "of", img_dd_of, C_OF },
          { "skip", img_dd_skip, C_SKIP },
@@ -99,7 +99,7 @@ index 2f9e8e77b8..dad04c865e 100644
          { NULL, NULL, 0 }
      };
      const struct option long_options[] = {
-@@ -5536,91 +5552,112 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5542,91 +5558,112 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
          arg = NULL;
      }
  
@@ -276,7 +276,7 @@ index 2f9e8e77b8..dad04c865e 100644
      }
  
      if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz ||
-@@ -5637,20 +5674,43 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5643,20 +5680,43 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
      in.buf = g_new(uint8_t, in.bsz);
  
      for (out_pos = 0; in_pos < size; ) {
diff --git a/debian/patches/pve/0008-PVE-Up-qemu-img-dd-add-isize-parameter.patch b/debian/patches/pve/0008-PVE-Up-qemu-img-dd-add-isize-parameter.patch
index db3a1bfaa8..72dbb64ab3 100644
--- a/debian/patches/pve/0008-PVE-Up-qemu-img-dd-add-isize-parameter.patch
+++ b/debian/patches/pve/0008-PVE-Up-qemu-img-dd-add-isize-parameter.patch
@@ -17,10 +17,10 @@ Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
  1 file changed, 25 insertions(+), 3 deletions(-)
 
 diff --git a/qemu-img.c b/qemu-img.c
-index dad04c865e..f64a8cb3d3 100644
+index 82856a38a8..eaa143fe48 100644
 --- a/qemu-img.c
 +++ b/qemu-img.c
-@@ -5321,11 +5321,13 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5327,11 +5327,13 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
  #define C_OF      010
  #define C_SKIP    020
  #define C_OSIZE   040
@@ -34,7 +34,7 @@ index dad04c865e..f64a8cb3d3 100644
  };
  
  struct DdIo {
-@@ -5414,6 +5416,19 @@ static int img_dd_osize(const char *arg,
+@@ -5420,6 +5422,19 @@ static int img_dd_osize(const char *arg,
      return 0;
  }
  
@@ -54,7 +54,7 @@ index dad04c865e..f64a8cb3d3 100644
  static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
  {
      int ret = 0;
-@@ -5428,12 +5443,14 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5434,12 +5449,14 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
      int c, i;
      const char *out_fmt = "raw";
      const char *fmt = NULL;
@@ -70,7 +70,7 @@ index dad04c865e..f64a8cb3d3 100644
      };
      struct DdIo in = {
          .bsz = 512, /* Block size is by default 512 bytes */
-@@ -5455,6 +5472,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5461,6 +5478,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
          { "of", img_dd_of, C_OF },
          { "skip", img_dd_skip, C_SKIP },
          { "osize", img_dd_osize, C_OSIZE },
@@ -78,7 +78,7 @@ index dad04c865e..f64a8cb3d3 100644
          { NULL, NULL, 0 }
      };
      const struct option long_options[] = {
-@@ -5673,9 +5691,10 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5679,9 +5697,10 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
  
      in.buf = g_new(uint8_t, in.bsz);
  
@@ -91,7 +91,7 @@ index dad04c865e..f64a8cb3d3 100644
          if (blk1) {
              in_ret = blk_pread(blk1, in_pos, bytes, in.buf, 0);
              if (in_ret == 0) {
-@@ -5684,6 +5703,9 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5690,6 +5709,9 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
          } else {
              in_ret = read(STDIN_FILENO, in.buf, bytes);
              if (in_ret == 0) {
diff --git a/debian/patches/pve/0009-PVE-Up-qemu-img-dd-add-n-skip_create.patch b/debian/patches/pve/0009-PVE-Up-qemu-img-dd-add-n-skip_create.patch
index 5684d3c819..d42e207565 100644
--- a/debian/patches/pve/0009-PVE-Up-qemu-img-dd-add-n-skip_create.patch
+++ b/debian/patches/pve/0009-PVE-Up-qemu-img-dd-add-n-skip_create.patch
@@ -66,10 +66,10 @@ index 85458ea2d8..f97c2fa396 100644
  
  DEF("info", img_info,
 diff --git a/qemu-img.c b/qemu-img.c
-index f64a8cb3d3..e8e2b0eb68 100644
+index eaa143fe48..25cf0babe4 100644
 --- a/qemu-img.c
 +++ b/qemu-img.c
-@@ -5445,7 +5445,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5451,7 +5451,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
      const char *fmt = NULL;
      int64_t size = 0, readsize = 0;
      int64_t out_pos, in_pos;
@@ -78,7 +78,7 @@ index f64a8cb3d3..e8e2b0eb68 100644
      struct DdInfo dd = {
          .flags = 0,
          .count = 0,
-@@ -5485,7 +5485,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5491,7 +5491,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
          { 0, 0, 0, 0 }
      };
  
@@ -87,7 +87,7 @@ index f64a8cb3d3..e8e2b0eb68 100644
          if (c == EOF) {
              break;
          }
-@@ -5526,6 +5526,9 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5532,6 +5532,9 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
          case 'O':
              out_fmt = optarg;
              break;
@@ -97,7 +97,7 @@ index f64a8cb3d3..e8e2b0eb68 100644
          case 'U':
              force_share = true;
              break;
-@@ -5655,13 +5658,15 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5661,13 +5664,15 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
                                  size - in.bsz * in.offset, &error_abort);
          }
  
diff --git a/debian/patches/pve/0010-qemu-img-dd-add-l-option-for-loading-a-snapshot.patch b/debian/patches/pve/0010-qemu-img-dd-add-l-option-for-loading-a-snapshot.patch
index f3bd76dd6a..00a77964b6 100644
--- a/debian/patches/pve/0010-qemu-img-dd-add-l-option-for-loading-a-snapshot.patch
+++ b/debian/patches/pve/0010-qemu-img-dd-add-l-option-for-loading-a-snapshot.patch
@@ -48,10 +48,10 @@ index f97c2fa396..206ca7381c 100644
  
  DEF("info", img_info,
 diff --git a/qemu-img.c b/qemu-img.c
-index e8e2b0eb68..8da92a63c0 100644
+index 25cf0babe4..21ec5615e2 100644
 --- a/qemu-img.c
 +++ b/qemu-img.c
-@@ -5437,6 +5437,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5443,6 +5443,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
      BlockDriver *drv = NULL, *proto_drv = NULL;
      BlockBackend *blk1 = NULL, *blk2 = NULL;
      QemuOpts *opts = NULL;
@@ -59,7 +59,7 @@ index e8e2b0eb68..8da92a63c0 100644
      QemuOptsList *create_opts = NULL;
      Error *local_err = NULL;
      bool image_opts = false;
-@@ -5446,6 +5447,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5452,6 +5453,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
      int64_t size = 0, readsize = 0;
      int64_t out_pos, in_pos;
      bool force_share = false, skip_create = false;
@@ -67,7 +67,7 @@ index e8e2b0eb68..8da92a63c0 100644
      struct DdInfo dd = {
          .flags = 0,
          .count = 0,
-@@ -5485,7 +5487,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5491,7 +5493,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
          { 0, 0, 0, 0 }
      };
  
@@ -76,7 +76,7 @@ index e8e2b0eb68..8da92a63c0 100644
          if (c == EOF) {
              break;
          }
-@@ -5529,6 +5531,19 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5535,6 +5537,19 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
          case 'n':
              skip_create = true;
              break;
@@ -96,7 +96,7 @@ index e8e2b0eb68..8da92a63c0 100644
          case 'U':
              force_share = true;
              break;
-@@ -5587,11 +5602,24 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5593,11 +5608,24 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
      if (dd.flags & C_IF) {
          blk1 = img_open(image_opts, in.filename, fmt, 0, false, false,
                          force_share);
@@ -122,7 +122,7 @@ index e8e2b0eb68..8da92a63c0 100644
      }
  
      if (dd.flags & C_OSIZE) {
-@@ -5746,6 +5774,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
+@@ -5752,6 +5780,7 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv)
  out:
      g_free(arg);
      qemu_opts_del(opts);
diff --git a/debian/patches/pve/0025-PVE-Backup-add-backup-dump-block-driver.patch b/debian/patches/pve/0025-PVE-Backup-add-backup-dump-block-driver.patch
index e23026c00b..ba7324d481 100644
--- a/debian/patches/pve/0025-PVE-Backup-add-backup-dump-block-driver.patch
+++ b/debian/patches/pve/0025-PVE-Backup-add-backup-dump-block-driver.patch
@@ -259,7 +259,7 @@ index 83f858669f..d7ab3cb78f 100644
    'blklogwrites.c',
    'blkverify.c',
 diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
-index 147c08155f..41c82fc1ab 100644
+index 46428b034d..94b1dd347d 100644
 --- a/include/block/block_int-common.h
 +++ b/include/block/block_int-common.h
 @@ -25,6 +25,7 @@
diff --git a/debian/patches/series b/debian/patches/series
index 3140ed6230..327621af89 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -2,6 +2,14 @@ extra/0001-monitor-qmp-fix-race-with-clients-disconnecting-earl.patch
 extra/0002-fdmon-io_uring-avoid-idle-event-loop-being-accounted.patch
 extra/0003-block-vmdk-tolerate-known-seSparse-reserved1-bit.patch
 extra/0004-hw-scsi-lsi53c895a-gracefully-handle-re-entrant-DMA.patch
+extra/0005-hw-usb-hcd-xhci-Limit-DMA-transfers-to-plain-memory.patch
+extra/0006-block-Fix-corruption-from-broken-RMW-atomicity.patch
+extra/0007-block-Remove-now-redundant-bdrv_wait_serialising_req.patch
+extra/0008-block-fix-bdrv_next-skipping-monitor-owned-nodes.patch
+extra/0009-iotests-add-test-cases-for-failing-flush-after-qemu-.patch
+extra/0010-iotests.py-add-filter_qemu.patch
+extra/0011-block-report-flush-error-during-bdrv_close.patch
+extra/0012-qemu-img-convert-flush-target-image-after-copying.patch
 bitmap-mirror/0001-drive-mirror-add-support-for-sync-bitmap-mode-never.patch
 bitmap-mirror/0002-drive-mirror-add-support-for-conditional-and-always-.patch
 bitmap-mirror/0003-mirror-add-check-for-bitmap-mode-without-bitmap.patch
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-10-12 11:38 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-12 10:51 [PATCH-SERIES qemu 0/2] stable fixes for QEMU 11.1 Fiona Ebner
2026-10-12 10:51 ` [PATCH qemu 1/2] update submodule and patches to QEMU 11.1.2 Fiona Ebner
2026-10-12 10:51 ` [PATCH qemu 2/2] more stable fixes for QEMU 11.1 Fiona Ebner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal