From: Fiona Ebner <f.ebner@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH qemu 2/2] stable fixes for QEMU 10.2.1
Date: Thu, 12 Mar 2026 12:44:03 +0100 [thread overview]
Message-ID: <20260312114417.82984-3-f.ebner@proxmox.com> (raw)
In-Reply-To: <20260312114417.82984-1-f.ebner@proxmox.com>
Fixes for a very bad performance regression for io_uring that could
happen in combination with 'ide-hd'.
Fix for a long-standing race in mirror startup when block allocation
status change could be missed.
Fix for an out-of-bounds read with maliciously crafted VMDK images.
Fixes for newly-in-10.2.1 regressions in CPU migration,
VirtIO-GPU-Virgl and ARM emulation.
Fixes for fuse, throttle groups and block error handling.
Note that Proxmox VE does not expose virtio-snd, but the issues look
pretty bad, so still include them for people who manually use that
via custom args.
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
---
...d-support-for-sync-bitmap-mode-never.patch | 42 +--
...-support-for-conditional-and-always-.patch | 10 +-
...-to-bdrv_dirty_bitmap_merge_internal.patch | 6 +-
.../0006-mirror-move-some-checks-to-qmp.patch | 4 +-
...ck-range-when-setting-zero-bitmap-fo.patch | 7 +-
...mdk-fix-OOB-read-in-vmdk_read_extent.patch | 38 +++
...roups-fix-deadlock-with-iolimits-and.patch | 133 +++++++++
...-BLOCK_IO_ERROR-with-action-stop-for.patch | 88 ++++++
...d-dirty-bitmap-writes-during-startup.patch | 152 +++++++++++
...-Add-virtio-gpu-virgl-hostmem-region.patch | 174 ++++++++++++
...e-BHs-are-invoked-only-from-main-loo.patch | 123 +++++++++
...c-Fix-out-of-bounds-read-in-I2C-MMIO.patch | 136 +++++++++
...nt-for-SME-in-aarch64_sve_narrow_vq-.patch | 62 +++++
...eature-check-in-DO_SVE2_RRX-DO_SVE2_.patch | 47 ++++
...llow-SVE-RAX1-in-SME2p1-streaming-mo.patch | 44 +++
...t-arm-Don-t-let-sme-on-downgrade-SME.patch | 98 +++++++
...t-the-correct-TI-bits-for-WFIT-traps.patch | 35 +++
...otify-main-loop-when-SQEs-are-queued.patch | 119 ++++++++
...heck-CQ-ring-directly-in-gsource_che.patch | 49 ++++
...-add-compat-for-migrating-error-code.patch | 75 +++++
| 93 +++++++
...andle-5.14.6.2-for-PCM_INFO-properly.patch | 89 ++++++
...ix-max_size-bounds-check-in-input-cb.patch | 44 +++
...tio-snd-tighten-read-amount-in-in_cb.patch | 51 ++++
...l-Fix-incorrect-trace-event-in-read-.patch | 41 +++
...ate-x86_decode-Actually-use-stream-i.patch | 52 ++++
...ing-of-tasks-from-marking-them-as-co.patch | 258 ++++++++++++++++++
...or-TLS-I-O-source-data-on-cancellati.patch | 176 ++++++++++++
...or-websock-I-O-source-data-on-cancel.patch | 143 ++++++++++
..._printable_name-consistently-return-.patch | 142 ++++++++++
...-write-buffer-content-before-polling.patch | 114 ++++++++
...he-CPU-model-to-kvm64-32-instead-of-.patch | 4 +-
...add-the-zeroinit-block-driver-filter.patch | 2 +-
...le-posix-make-locking-optiono-on-cre.patch | 2 +-
...ckup-Proxmox-backup-patches-for-QEMU.patch | 2 +-
...k-driver-to-map-backup-archives-into.patch | 2 +-
...igrate-dirty-bitmap-state-via-savevm.patch | 2 +-
.../0038-block-add-alloc-track-driver.patch | 2 +-
.../0039-PVE-backup-add-fleecing-option.patch | 2 +-
...ment-backup-access-setup-and-teardow.patch | 2 +-
...se-migration-blocker-check-for-snaps.patch | 2 +-
debian/patches/series | 26 ++
42 files changed, 2650 insertions(+), 43 deletions(-)
create mode 100644 debian/patches/extra/0005-block-vmdk-fix-OOB-read-in-vmdk_read_extent.patch
create mode 100644 debian/patches/extra/0006-block-throttle-groups-fix-deadlock-with-iolimits-and.patch
create mode 100644 debian/patches/extra/0007-block-Never-drop-BLOCK_IO_ERROR-with-action-stop-for.patch
create mode 100644 debian/patches/extra/0008-mirror-Fix-missed-dirty-bitmap-writes-during-startup.patch
create mode 100644 debian/patches/extra/0009-virtio-gpu-virgl-Add-virtio-gpu-virgl-hostmem-region.patch
create mode 100644 debian/patches/extra/0010-virtio-gpu-Ensure-BHs-are-invoked-only-from-main-loo.patch
create mode 100644 debian/patches/extra/0011-hw-i2c-aspeed_i2c-Fix-out-of-bounds-read-in-I2C-MMIO.patch
create mode 100644 debian/patches/extra/0012-target-arm-Account-for-SME-in-aarch64_sve_narrow_vq-.patch
create mode 100644 debian/patches/extra/0013-target-arm-Fix-feature-check-in-DO_SVE2_RRX-DO_SVE2_.patch
create mode 100644 debian/patches/extra/0014-target-arm-tcg-Allow-SVE-RAX1-in-SME2p1-streaming-mo.patch
create mode 100644 debian/patches/extra/0015-target-arm-Don-t-let-sme-on-downgrade-SME.patch
create mode 100644 debian/patches/extra/0016-target-arm-set-the-correct-TI-bits-for-WFIT-traps.patch
create mode 100644 debian/patches/extra/0017-aio-posix-notify-main-loop-when-SQEs-are-queued.patch
create mode 100644 debian/patches/extra/0018-fdmon-io_uring-check-CQ-ring-directly-in-gsource_che.patch
create mode 100644 debian/patches/extra/0019-target-i386-add-compat-for-migrating-error-code.patch
create mode 100644 debian/patches/extra/0020-virtio-snd-remove-TODO-comments.patch
create mode 100644 debian/patches/extra/0021-virtio-snd-handle-5.14.6.2-for-PCM_INFO-properly.patch
create mode 100644 debian/patches/extra/0022-virtio-snd-fix-max_size-bounds-check-in-input-cb.patch
create mode 100644 debian/patches/extra/0023-virtio-snd-tighten-read-amount-in-in_cb.patch
create mode 100644 debian/patches/extra/0024-hw-misc-virt_ctrl-Fix-incorrect-trace-event-in-read-.patch
create mode 100644 debian/patches/extra/0025-target-i386-emulate-x86_decode-Actually-use-stream-i.patch
create mode 100644 debian/patches/extra/0026-io-separate-freeing-of-tasks-from-marking-them-as-co.patch
create mode 100644 debian/patches/extra/0027-io-fix-cleanup-for-TLS-I-O-source-data-on-cancellati.patch
create mode 100644 debian/patches/extra/0028-io-fix-cleanup-for-websock-I-O-source-data-on-cancel.patch
create mode 100644 debian/patches/extra/0029-hw-Make-qdev_get_printable_name-consistently-return-.patch
create mode 100644 debian/patches/extra/0030-fuse-Copy-write-buffer-content-before-polling.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 b3906bd..81ca0fa 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
@@ -27,7 +27,7 @@ Signed-off-by: Ma Haocong <mahaocong@didichuxing.com>
Signed-off-by: John Snow <jsnow@redhat.com>
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
-[FE: rebased for 10.1.0]
+[FE: rebased for 10.2.1]
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
---
block/mirror.c | 87 +++++++++++++++++++++-----
@@ -38,7 +38,7 @@ Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
5 files changed, 135 insertions(+), 21 deletions(-)
diff --git a/block/mirror.c b/block/mirror.c
-index bc982cb99a..99805e7a9d 100644
+index fa1d975eb9..d09479789f 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -74,6 +74,8 @@ typedef struct MirrorBlockJob {
@@ -50,7 +50,7 @@ index bc982cb99a..99805e7a9d 100644
BdrvDirtyBitmap *dirty_bitmap;
BdrvDirtyBitmapIter *dbi;
uint8_t *buf;
-@@ -871,6 +873,16 @@ static void mirror_abort(Job *job)
+@@ -872,6 +874,16 @@ static void mirror_abort(Job *job)
assert(ret == 0);
}
@@ -67,7 +67,7 @@ index bc982cb99a..99805e7a9d 100644
static void coroutine_fn mirror_throttle(MirrorBlockJob *s)
{
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
-@@ -1110,7 +1122,8 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
+@@ -1111,7 +1123,8 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
mirror_free_init(s);
s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
@@ -77,7 +77,7 @@ index bc982cb99a..99805e7a9d 100644
ret = mirror_dirty_init(s);
if (ret < 0 || job_is_cancelled(&s->common.job)) {
goto immediate_exit;
-@@ -1400,6 +1413,7 @@ static const BlockJobDriver mirror_job_driver = {
+@@ -1401,6 +1414,7 @@ static const BlockJobDriver mirror_job_driver = {
.run = mirror_run,
.prepare = mirror_prepare,
.abort = mirror_abort,
@@ -85,7 +85,7 @@ index bc982cb99a..99805e7a9d 100644
.pause = mirror_pause,
.complete = mirror_complete,
.cancel = mirror_cancel,
-@@ -1418,6 +1432,7 @@ static const BlockJobDriver commit_active_job_driver = {
+@@ -1419,6 +1433,7 @@ static const BlockJobDriver commit_active_job_driver = {
.run = mirror_run,
.prepare = mirror_prepare,
.abort = mirror_abort,
@@ -93,7 +93,7 @@ index bc982cb99a..99805e7a9d 100644
.pause = mirror_pause,
.complete = mirror_complete,
.cancel = commit_active_cancel,
-@@ -1838,6 +1853,8 @@ static BlockJob *mirror_start_job(
+@@ -1841,6 +1856,8 @@ static BlockJob *mirror_start_job(
BlockCompletionFunc *cb,
void *opaque,
const BlockJobDriver *driver,
@@ -102,7 +102,7 @@ index bc982cb99a..99805e7a9d 100644
BlockDriverState *base,
bool auto_complete, const char *filter_node_name,
bool is_mirror, MirrorCopyMode copy_mode,
-@@ -1853,10 +1870,39 @@ static BlockJob *mirror_start_job(
+@@ -1856,10 +1873,39 @@ static BlockJob *mirror_start_job(
GLOBAL_STATE_CODE();
@@ -144,7 +144,7 @@ index bc982cb99a..99805e7a9d 100644
assert(is_power_of_2(granularity));
if (buf_size < 0) {
-@@ -1998,6 +2044,8 @@ static BlockJob *mirror_start_job(
+@@ -2023,6 +2069,8 @@ static BlockJob *mirror_start_job(
s->on_source_error = on_source_error;
s->on_target_error = on_target_error;
s->sync_mode = sync_mode;
@@ -153,9 +153,9 @@ index bc982cb99a..99805e7a9d 100644
s->backing_mode = backing_mode;
s->target_is_zero = target_is_zero;
qatomic_set(&s->copy_mode, copy_mode);
-@@ -2023,6 +2071,18 @@ static BlockJob *mirror_start_job(
- */
- bdrv_disable_dirty_bitmap(s->dirty_bitmap);
+@@ -2037,6 +2085,18 @@ static BlockJob *mirror_start_job(
+ }
+ bdrv_graph_rdunlock_main_loop();
+ if (s->sync_bitmap) {
+ bdrv_dirty_bitmap_set_busy(s->sync_bitmap, true);
@@ -172,17 +172,17 @@ index bc982cb99a..99805e7a9d 100644
bdrv_graph_wrlock_drained();
ret = block_job_add_bdrv(&s->common, "source", bs, 0,
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE |
-@@ -2105,6 +2165,9 @@ fail:
- if (s->dirty_bitmap) {
- bdrv_release_dirty_bitmap(s->dirty_bitmap);
- }
+@@ -2116,6 +2176,9 @@ fail:
+ g_free(s->replaces);
+ blk_unref(s->target);
+ bs_opaque->job = NULL;
+ if (s->sync_bitmap) {
+ bdrv_dirty_bitmap_set_busy(s->sync_bitmap, false);
+ }
job_early_fail(&s->common.job);
}
-@@ -2127,7 +2190,10 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
+@@ -2139,7 +2202,10 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, const char *replaces,
int creation_flags, int64_t speed,
uint32_t granularity, int64_t buf_size,
@@ -194,7 +194,7 @@ index bc982cb99a..99805e7a9d 100644
bool target_is_zero,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
-@@ -2138,13 +2204,6 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
+@@ -2150,13 +2216,6 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
GLOBAL_STATE_CODE();
@@ -208,7 +208,7 @@ index bc982cb99a..99805e7a9d 100644
bdrv_graph_rdlock_main_loop();
base = mode == MIRROR_SYNC_MODE_TOP ? bdrv_backing_chain_next(bs) : NULL;
bdrv_graph_rdunlock_main_loop();
-@@ -2152,8 +2211,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
+@@ -2164,8 +2223,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
mirror_start_job(job_id, bs, creation_flags, target, replaces,
speed, granularity, buf_size, mode, backing_mode,
target_is_zero, on_source_error, on_target_error, unmap,
@@ -219,7 +219,7 @@ index bc982cb99a..99805e7a9d 100644
}
BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
-@@ -2180,7 +2239,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
+@@ -2192,7 +2251,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
job_id, bs, creation_flags, base, NULL, speed, 0, 0,
MIRROR_SYNC_MODE_TOP, MIRROR_LEAVE_BACKING_CHAIN, false,
on_error, on_error, true, cb, opaque,
@@ -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 b82af74256..64f2befdf5 100644
+index 4118d884f4..d4a5765dc4 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2275,6 +2275,15 @@
diff --git a/debian/patches/bitmap-mirror/0002-drive-mirror-add-support-for-conditional-and-always-.patch b/debian/patches/bitmap-mirror/0002-drive-mirror-add-support-for-conditional-and-always-.patch
index 8258adc..f413554 100644
--- a/debian/patches/bitmap-mirror/0002-drive-mirror-add-support-for-conditional-and-always-.patch
+++ b/debian/patches/bitmap-mirror/0002-drive-mirror-add-support-for-conditional-and-always-.patch
@@ -24,10 +24,10 @@ Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/block/mirror.c b/block/mirror.c
-index 99805e7a9d..7dae4d6e8a 100644
+index d09479789f..0050d4372f 100644
--- a/block/mirror.c
+++ b/block/mirror.c
-@@ -734,8 +734,6 @@ static int mirror_exit_common(Job *job)
+@@ -735,8 +735,6 @@ static int mirror_exit_common(Job *job)
bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
}
@@ -36,7 +36,7 @@ index 99805e7a9d..7dae4d6e8a 100644
/* Make sure that the source BDS doesn't go away during bdrv_replace_node,
* before we can call bdrv_drained_end */
bdrv_ref(src);
-@@ -851,6 +849,18 @@ static int mirror_exit_common(Job *job)
+@@ -852,6 +850,18 @@ static int mirror_exit_common(Job *job)
bdrv_drained_end(target_bs);
bdrv_unref(target_bs);
@@ -55,7 +55,7 @@ index 99805e7a9d..7dae4d6e8a 100644
bs_opaque->job = NULL;
bdrv_drained_end(src);
-@@ -1880,10 +1890,6 @@ static BlockJob *mirror_start_job(
+@@ -1883,10 +1893,6 @@ static BlockJob *mirror_start_job(
" sync mode",
MirrorSyncMode_str(sync_mode));
return NULL;
@@ -66,7 +66,7 @@ index 99805e7a9d..7dae4d6e8a 100644
}
} else if (bitmap) {
error_setg(errp,
-@@ -1900,6 +1906,12 @@ static BlockJob *mirror_start_job(
+@@ -1903,6 +1909,12 @@ static BlockJob *mirror_start_job(
return NULL;
}
granularity = bdrv_dirty_bitmap_granularity(bitmap);
diff --git a/debian/patches/bitmap-mirror/0004-mirror-switch-to-bdrv_dirty_bitmap_merge_internal.patch b/debian/patches/bitmap-mirror/0004-mirror-switch-to-bdrv_dirty_bitmap_merge_internal.patch
index 4f9cdbd..6023b78 100644
--- a/debian/patches/bitmap-mirror/0004-mirror-switch-to-bdrv_dirty_bitmap_merge_internal.patch
+++ b/debian/patches/bitmap-mirror/0004-mirror-switch-to-bdrv_dirty_bitmap_merge_internal.patch
@@ -16,10 +16,10 @@ Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/block/mirror.c b/block/mirror.c
-index 7dae4d6e8a..0f96c8b5ce 100644
+index 0050d4372f..ee745f8ec4 100644
--- a/block/mirror.c
+++ b/block/mirror.c
-@@ -855,8 +855,8 @@ static int mirror_exit_common(Job *job)
+@@ -856,8 +856,8 @@ static int mirror_exit_common(Job *job)
job->ret == 0 && ret == 0)) {
/* Success; synchronize copy back to sync. */
bdrv_clear_dirty_bitmap(s->sync_bitmap, NULL);
@@ -30,7 +30,7 @@ index 7dae4d6e8a..0f96c8b5ce 100644
}
}
bdrv_release_dirty_bitmap(s->dirty_bitmap);
-@@ -2088,11 +2088,8 @@ static BlockJob *mirror_start_job(
+@@ -2102,11 +2102,8 @@ static BlockJob *mirror_start_job(
}
if (s->sync_mode == MIRROR_SYNC_MODE_BITMAP) {
diff --git a/debian/patches/bitmap-mirror/0006-mirror-move-some-checks-to-qmp.patch b/debian/patches/bitmap-mirror/0006-mirror-move-some-checks-to-qmp.patch
index 687a256..76b9d40 100644
--- a/debian/patches/bitmap-mirror/0006-mirror-move-some-checks-to-qmp.patch
+++ b/debian/patches/bitmap-mirror/0006-mirror-move-some-checks-to-qmp.patch
@@ -21,10 +21,10 @@ Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
3 files changed, 70 insertions(+), 59 deletions(-)
diff --git a/block/mirror.c b/block/mirror.c
-index 0f96c8b5ce..5340a695b1 100644
+index ee745f8ec4..e71f8f3cdf 100644
--- a/block/mirror.c
+++ b/block/mirror.c
-@@ -1880,31 +1880,13 @@ static BlockJob *mirror_start_job(
+@@ -1883,31 +1883,13 @@ static BlockJob *mirror_start_job(
GLOBAL_STATE_CODE();
diff --git a/debian/patches/extra/0003-block-mirror-check-range-when-setting-zero-bitmap-fo.patch b/debian/patches/extra/0003-block-mirror-check-range-when-setting-zero-bitmap-fo.patch
index 908d721..9af7ac5 100644
--- a/debian/patches/extra/0003-block-mirror-check-range-when-setting-zero-bitmap-fo.patch
+++ b/debian/patches/extra/0003-block-mirror-check-range-when-setting-zero-bitmap-fo.patch
@@ -1,6 +1,6 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Fiona Ebner <f.ebner@proxmox.com>
-Date: Mon, 12 Jan 2026 15:32:52 +0100
+Date: Mon, 12 Jan 2026 16:23:51 +0100
Subject: [PATCH] block/mirror: check range when setting zero bitmap for sync
write
@@ -32,6 +32,11 @@ bitmap too, which uses the same narrower range.
Cc: qemu-stable@nongnu.org
Fixes: 7e277545b9 ("mirror: Skip writing zeroes when target is already zero")
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+Message-ID: <20260112152544.261923-1-f.ebner@proxmox.com>
+Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
+Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
+(cherry picked from commit 4a7b1bd18d2e1a6b3796e177ae5df9b198264a0b)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
---
block/mirror.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/debian/patches/extra/0005-block-vmdk-fix-OOB-read-in-vmdk_read_extent.patch b/debian/patches/extra/0005-block-vmdk-fix-OOB-read-in-vmdk_read_extent.patch
new file mode 100644
index 0000000..c458de4
--- /dev/null
+++ b/debian/patches/extra/0005-block-vmdk-fix-OOB-read-in-vmdk_read_extent.patch
@@ -0,0 +1,38 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Oblivionsage <cookieandcream560@gmail.com>
+Date: Tue, 10 Feb 2026 13:33:25 +0100
+Subject: [PATCH] block/vmdk: fix OOB read in vmdk_read_extent()
+
+Bounds check for marker.size doesn't account for the 12-byte marker
+header, allowing zlib to read past the allocated buffer.
+
+Move the check inside the has_marker block and subtract the marker size.
+
+Fixes: CVE-2026-2243
+Reported-by: Halil Oktay (oblivionsage) <cookieandcream560@gmail.com>
+Signed-off-by: Halil Oktay (oblivionsage) <cookieandcream560@gmail.com>
+(picked from https://lore.kernel.org/qemu-devel/CAJ9qJssSwxkmEVethg57-Ph6maEfButSaV-r07ma9_x1sp6wYg@mail.gmail.com/ )
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ block/vmdk.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/block/vmdk.c b/block/vmdk.c
+index 89e89cd10e..cd8b4ec7c8 100644
+--- a/block/vmdk.c
++++ b/block/vmdk.c
+@@ -1951,10 +1951,10 @@ vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset,
+ marker = (VmdkGrainMarker *)cluster_buf;
+ compressed_data = marker->data;
+ data_len = le32_to_cpu(marker->size);
+- }
+- if (!data_len || data_len > buf_bytes) {
+- ret = -EINVAL;
+- goto out;
++ if (!data_len || data_len > buf_bytes - sizeof(VmdkGrainMarker)) {
++ ret = -EINVAL;
++ goto out;
++ }
+ }
+ ret = uncompress(uncomp_buf, &buf_len, compressed_data, data_len);
+ if (ret != Z_OK) {
diff --git a/debian/patches/extra/0006-block-throttle-groups-fix-deadlock-with-iolimits-and.patch b/debian/patches/extra/0006-block-throttle-groups-fix-deadlock-with-iolimits-and.patch
new file mode 100644
index 0000000..1485bfd
--- /dev/null
+++ b/debian/patches/extra/0006-block-throttle-groups-fix-deadlock-with-iolimits-and.patch
@@ -0,0 +1,133 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Dmitry Guryanov <dmitry.guryanov@gmail.com>
+Date: Mon, 8 Dec 2025 11:55:28 +0300
+Subject: [PATCH] block/throttle-groups: fix deadlock with iolimits and muliple
+ iothreads
+
+Details: https://gitlab.com/qemu-project/qemu/-/issues/3144
+
+The function schedule_next_request is called with tg->lock held and
+it may call throttle_group_co_restart_queue, which takes
+tgm->throttled_reqs_lock, qemu_co_mutex_lock may leave current
+coroutine if other iothread has taken the lock. If the next
+coroutine will call throttle_group_co_io_limits_intercept - it
+will try to take the mutex tg->lock which will never be released.
+
+Here is the backtrace of the iothread:
+Thread 30 (Thread 0x7f8aad1fd6c0 (LWP 24240) "IO iothread2"):
+ #0 futex_wait (futex_word=0x5611adb7d828, expected=2, private=0) at ../sysdeps/nptl/futex-internal.h:146
+ #1 __GI___lll_lock_wait (futex=futex@entry=0x5611adb7d828, private=0) at lowlevellock.c:49
+ #2 0x00007f8ab5a97501 in lll_mutex_lock_optimized (mutex=0x5611adb7d828) at pthread_mutex_lock.c:48
+ #3 ___pthread_mutex_lock (mutex=0x5611adb7d828) at pthread_mutex_lock.c:93
+ #4 0x00005611823f5482 in qemu_mutex_lock_impl (mutex=0x5611adb7d828, file=0x56118289daca "../block/throttle-groups.c", line=372) at ../util/qemu-thread-posix.c:94
+ #5 0x00005611822b0b39 in throttle_group_co_io_limits_intercept (tgm=0x5611af1bb4d8, bytes=4096, direction=THROTTLE_READ) at ../block/throttle-groups.c:372
+ #6 0x00005611822473b1 in blk_co_do_preadv_part (blk=0x5611af1bb490, offset=15972311040, bytes=4096, qiov=0x7f8aa4000f98, qiov_offset=0, flags=BDRV_REQ_REGISTERED_BUF) at ../block/block-backend.c:1354
+ #7 0x0000561182247fa0 in blk_aio_read_entry (opaque=0x7f8aa4005910) at ../block/block-backend.c:1619
+ #8 0x000056118241952e in coroutine_trampoline (i0=-1543497424, i1=32650) at ../util/coroutine-ucontext.c:175
+ #9 0x00007f8ab5a56f70 in ?? () at ../sysdeps/unix/sysv/linux/x86_64/__start_context.S:66 from target:/lib64/libc.so.6
+ #10 0x00007f8aad1ef190 in ?? ()
+ #11 0x0000000000000000 in ?? ()
+
+The lock is taken in line 386:
+(gdb) p tg.lock
+$1 = {lock = {__data = {__lock = 2, __count = 0, __owner = 24240, __nusers = 1, __kind = 0, __spins = 0, __elision = 0, __list = {__prev = 0x0, __next = 0x0}},
+ __size = "\002\000\000\000\000\000\000\000\260^\000\000\001", '\000' <repeats 26 times>, __align = 2}, file = 0x56118289daca "../block/throttle-groups.c",
+ line = 386, initialized = true}
+
+The solution is to use tg->lock to protect both ThreadGroup fields and
+ThrottleGroupMember.throttled_reqs. It doesn't seem to be possible
+to use separate locks because we need to first manipulate ThrottleGroup
+fields, then schedule next coroutine using throttled_reqs and after than
+update token field from ThrottleGroup depending on the throttled_reqs
+state.
+
+Signed-off-by: Dmitry Guryanov <dmitry.guryanov@gmail.com>
+Message-ID: <20251208085528.890098-1-dmitry.guryanov@gmail.com>
+Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+---
+ block/throttle-groups.c | 21 ++++++---------------
+ include/block/throttle-groups.h | 3 +--
+ 2 files changed, 7 insertions(+), 17 deletions(-)
+
+diff --git a/block/throttle-groups.c b/block/throttle-groups.c
+index 66fdce9a90..5329ff1fdb 100644
+--- a/block/throttle-groups.c
++++ b/block/throttle-groups.c
+@@ -295,19 +295,15 @@ static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm,
+ /* Start the next pending I/O request for a ThrottleGroupMember. Return whether
+ * any request was actually pending.
+ *
++ * This assumes that tg->lock is held.
++ *
+ * @tgm: the current ThrottleGroupMember
+ * @direction: the ThrottleDirection
+ */
+ static bool coroutine_fn throttle_group_co_restart_queue(ThrottleGroupMember *tgm,
+ ThrottleDirection direction)
+ {
+- bool ret;
+-
+- qemu_co_mutex_lock(&tgm->throttled_reqs_lock);
+- ret = qemu_co_queue_next(&tgm->throttled_reqs[direction]);
+- qemu_co_mutex_unlock(&tgm->throttled_reqs_lock);
+-
+- return ret;
++ return qemu_co_queue_next(&tgm->throttled_reqs[direction]);
+ }
+
+ /* Look for the next pending I/O request and schedule it.
+@@ -378,12 +374,8 @@ void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm
+ /* Wait if there's a timer set or queued requests of this type */
+ if (must_wait || tgm->pending_reqs[direction]) {
+ tgm->pending_reqs[direction]++;
+- qemu_mutex_unlock(&tg->lock);
+- qemu_co_mutex_lock(&tgm->throttled_reqs_lock);
+ qemu_co_queue_wait(&tgm->throttled_reqs[direction],
+- &tgm->throttled_reqs_lock);
+- qemu_co_mutex_unlock(&tgm->throttled_reqs_lock);
+- qemu_mutex_lock(&tg->lock);
++ &tg->lock);
+ tgm->pending_reqs[direction]--;
+ }
+
+@@ -410,15 +402,15 @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
+ ThrottleDirection direction = data->direction;
+ bool empty_queue;
+
++ qemu_mutex_lock(&tg->lock);
+ empty_queue = !throttle_group_co_restart_queue(tgm, direction);
+
+ /* If the request queue was empty then we have to take care of
+ * scheduling the next one */
+ if (empty_queue) {
+- qemu_mutex_lock(&tg->lock);
+ schedule_next_request(tgm, direction);
+- qemu_mutex_unlock(&tg->lock);
+ }
++ qemu_mutex_unlock(&tg->lock);
+
+ g_free(data);
+
+@@ -569,7 +561,6 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
+ read_timer_cb,
+ write_timer_cb,
+ tgm);
+- qemu_co_mutex_init(&tgm->throttled_reqs_lock);
+ }
+
+ /* Unregister a ThrottleGroupMember from its group, removing it from the list,
+diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
+index 2355e8d9de..7dfc81f7b5 100644
+--- a/include/block/throttle-groups.h
++++ b/include/block/throttle-groups.h
+@@ -35,8 +35,7 @@
+
+ typedef struct ThrottleGroupMember {
+ AioContext *aio_context;
+- /* throttled_reqs_lock protects the CoQueues for throttled requests. */
+- CoMutex throttled_reqs_lock;
++ /* Protected by ThrottleGroup.lock */
+ CoQueue throttled_reqs[THROTTLE_MAX];
+
+ /* Nonzero if the I/O limits are currently being ignored; generally
diff --git a/debian/patches/extra/0007-block-Never-drop-BLOCK_IO_ERROR-with-action-stop-for.patch b/debian/patches/extra/0007-block-Never-drop-BLOCK_IO_ERROR-with-action-stop-for.patch
new file mode 100644
index 0000000..b530219
--- /dev/null
+++ b/debian/patches/extra/0007-block-Never-drop-BLOCK_IO_ERROR-with-action-stop-for.patch
@@ -0,0 +1,88 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Kevin Wolf <kwolf@redhat.com>
+Date: Wed, 4 Mar 2026 13:28:00 +0100
+Subject: [PATCH] block: Never drop BLOCK_IO_ERROR with action=stop for rate
+ limiting
+
+Commit 2155d2dd introduced rate limiting for BLOCK_IO_ERROR to emit an
+event only once a second. This makes sense for cases in which the guest
+keeps running and can submit more requests that would possibly also fail
+because there is a problem with the backend.
+
+However, if the error policy is configured so that the VM is stopped on
+errors, this is both unnecessary because stopping the VM means that the
+guest can't issue more requests and in fact harmful because stopping the
+VM is an important state change that management tools need to keep track
+of even if it happens more than once in a given second. If an event is
+dropped, the management tool would see a VM randomly going to paused
+state without an associated error, so it has a hard time deciding how to
+handle the situation.
+
+This patch disables rate limiting for action=stop by not relying on the
+event type alone any more in monitor_qapi_event_queue_no_reenter(), but
+checking action for BLOCK_IO_ERROR, too. If the error is reported to the
+guest or ignored, the rate limiting stays in place.
+
+Fixes: 2155d2dd7f73 ('block-backend: per-device throttling of BLOCK_IO_ERROR reports')
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+Message-ID: <20260304122800.51923-1-kwolf@redhat.com>
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+(cherry picked from commit 544ddbb6373d61292a0e2dc269809cd6bd5edec6)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ monitor/monitor.c | 21 ++++++++++++++++++++-
+ qapi/block-core.json | 2 +-
+ 2 files changed, 21 insertions(+), 2 deletions(-)
+
+diff --git a/monitor/monitor.c b/monitor/monitor.c
+index 07775784d4..58ddee50d8 100644
+--- a/monitor/monitor.c
++++ b/monitor/monitor.c
+@@ -378,14 +378,33 @@ monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict)
+ {
+ MonitorQAPIEventConf *evconf;
+ MonitorQAPIEventState *evstate;
++ bool throttled;
+
+ assert(event < QAPI_EVENT__MAX);
+ evconf = &monitor_qapi_event_conf[event];
+ trace_monitor_protocol_event_queue(event, qdict, evconf->rate);
++ throttled = evconf->rate;
++
++ /*
++ * Rate limit BLOCK_IO_ERROR only for action != "stop".
++ *
++ * If the VM is stopped after an I/O error, this is important information
++ * for the management tool to keep track of the state of QEMU and we can't
++ * merge any events. At the same time, stopping the VM means that the guest
++ * can't send additional requests and the number of events is already
++ * limited, so we can do without rate limiting.
++ */
++ if (event == QAPI_EVENT_BLOCK_IO_ERROR) {
++ QDict *data = qobject_to(QDict, qdict_get(qdict, "data"));
++ const char *action = qdict_get_str(data, "action");
++ if (!strcmp(action, "stop")) {
++ throttled = false;
++ }
++ }
+
+ QEMU_LOCK_GUARD(&monitor_lock);
+
+- if (!evconf->rate) {
++ if (!throttled) {
+ /* Unthrottled event */
+ monitor_qapi_event_emit(event, qdict);
+ } else {
+diff --git a/qapi/block-core.json b/qapi/block-core.json
+index b82af74256..4118d884f4 100644
+--- a/qapi/block-core.json
++++ b/qapi/block-core.json
+@@ -5789,7 +5789,7 @@
+ # .. note:: If action is "stop", a `STOP` event will eventually follow
+ # the `BLOCK_IO_ERROR` event.
+ #
+-# .. note:: This event is rate-limited.
++# .. note:: This event is rate-limited, except if action is "stop".
+ #
+ # Since: 0.13
+ #
diff --git a/debian/patches/extra/0008-mirror-Fix-missed-dirty-bitmap-writes-during-startup.patch b/debian/patches/extra/0008-mirror-Fix-missed-dirty-bitmap-writes-during-startup.patch
new file mode 100644
index 0000000..b8ee24b
--- /dev/null
+++ b/debian/patches/extra/0008-mirror-Fix-missed-dirty-bitmap-writes-during-startup.patch
@@ -0,0 +1,152 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Kevin Wolf <kwolf@redhat.com>
+Date: Thu, 19 Feb 2026 21:24:46 +0100
+Subject: [PATCH] mirror: Fix missed dirty bitmap writes during startup
+
+Currently, mirror disables the block layer's dirty bitmap before its own
+replacement is working. This means that during startup, there is a
+window in which the allocation status of blocks in the source has
+already been checked, but new writes coming in aren't tracked yet,
+resulting in a corrupted copy:
+
+1. Dirty bitmap is disabled in mirror_start_job()
+2. Some request are started in mirror_top_bs while s->job == NULL
+3. mirror_dirty_init() -> bdrv_co_is_allocated_above() runs and because
+ the request hasn't completed yet, the block isn't allocated
+4. The request completes, still sees s->job == NULL and skips the
+ bitmap, and nothing else will mark it dirty either
+
+One ingredient is that mirror_top_opaque->job is only set after the
+job is fully initialized. For the rationale, see commit 32125b1460
+("mirror: Fix access of uninitialised fields during start").
+
+Fix this by giving mirror_top_bs access to dirty_bitmap and enabling it
+to track writes from the beginning. Disabling the block layer's tracking
+and enabling the mirror_top_bs one happens in a drained section, so
+there is no danger of races with in-flight requests any more. All of
+this happens well before the block allocation status is checked, so we
+can be sure that no writes will be missed.
+
+Cc: qemu-stable@nongnu.org
+Closes: https://gitlab.com/qemu-project/qemu/-/issues/3273
+Fixes: 32125b14606a ('mirror: Fix access of uninitialised fields during start')
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+Message-ID: <20260219202446.312493-1-kwolf@redhat.com>
+Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
+Tested-by: Jean-Louis Dupond <jean-louis@dupond.be>
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+(cherry picked from commit 167ef239fbdcc4bde126e47668bfc4839b873b19)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ block/mirror.c | 52 +++++++++++++++++++++++++++++++-------------------
+ 1 file changed, 32 insertions(+), 20 deletions(-)
+
+diff --git a/block/mirror.c b/block/mirror.c
+index bc982cb99a..fa1d975eb9 100644
+--- a/block/mirror.c
++++ b/block/mirror.c
+@@ -99,6 +99,7 @@ typedef struct MirrorBlockJob {
+
+ typedef struct MirrorBDSOpaque {
+ MirrorBlockJob *job;
++ BdrvDirtyBitmap *dirty_bitmap;
+ bool stop;
+ bool is_commit;
+ } MirrorBDSOpaque;
+@@ -1675,9 +1676,11 @@ bdrv_mirror_top_do_write(BlockDriverState *bs, MirrorMethod method,
+ abort();
+ }
+
+- if (!copy_to_target && s->job && s->job->dirty_bitmap) {
+- qatomic_set(&s->job->actively_synced, false);
+- bdrv_set_dirty_bitmap(s->job->dirty_bitmap, offset, bytes);
++ if (!copy_to_target) {
++ if (s->job) {
++ qatomic_set(&s->job->actively_synced, false);
++ }
++ bdrv_set_dirty_bitmap(s->dirty_bitmap, offset, bytes);
+ }
+
+ if (ret < 0) {
+@@ -1904,13 +1907,35 @@ static BlockJob *mirror_start_job(
+
+ bdrv_drained_begin(bs);
+ ret = bdrv_append(mirror_top_bs, bs, errp);
+- bdrv_drained_end(bs);
+-
+ if (ret < 0) {
++ bdrv_drained_end(bs);
++ bdrv_unref(mirror_top_bs);
++ return NULL;
++ }
++
++ bs_opaque->dirty_bitmap = bdrv_create_dirty_bitmap(mirror_top_bs,
++ granularity,
++ NULL, errp);
++ if (!bs_opaque->dirty_bitmap) {
++ bdrv_drained_end(bs);
+ bdrv_unref(mirror_top_bs);
+ return NULL;
+ }
+
++ /*
++ * The mirror job doesn't use the block layer's dirty tracking because it
++ * needs to be able to switch seemlessly between background copy mode (which
++ * does need dirty tracking) and write blocking mode (which doesn't) and
++ * doing that would require draining the node. Instead, mirror_top_bs takes
++ * care of updating the dirty bitmap as appropriate.
++ *
++ * Note that write blocking mode only becomes effective after mirror_run()
++ * sets mirror_top_opaque->job (see should_copy_to_target()). Until then,
++ * we're still in background copy mode irrespective of @copy_mode.
++ */
++ bdrv_disable_dirty_bitmap(bs_opaque->dirty_bitmap);
++ bdrv_drained_end(bs);
++
+ /* Make sure that the source is not resized while the job is running */
+ s = block_job_create(job_id, driver, NULL, mirror_top_bs,
+ BLK_PERM_CONSISTENT_READ,
+@@ -2005,24 +2030,13 @@ static BlockJob *mirror_start_job(
+ s->base_overlay = bdrv_find_overlay(bs, base);
+ s->granularity = granularity;
+ s->buf_size = ROUND_UP(buf_size, granularity);
++ s->dirty_bitmap = bs_opaque->dirty_bitmap;
+ s->unmap = unmap;
+ if (auto_complete) {
+ s->should_complete = true;
+ }
+ bdrv_graph_rdunlock_main_loop();
+
+- s->dirty_bitmap = bdrv_create_dirty_bitmap(s->mirror_top_bs, granularity,
+- NULL, errp);
+- if (!s->dirty_bitmap) {
+- goto fail;
+- }
+-
+- /*
+- * The dirty bitmap is set by bdrv_mirror_top_do_write() when not in active
+- * mode.
+- */
+- bdrv_disable_dirty_bitmap(s->dirty_bitmap);
+-
+ bdrv_graph_wrlock_drained();
+ ret = block_job_add_bdrv(&s->common, "source", bs, 0,
+ BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE |
+@@ -2102,9 +2116,6 @@ fail:
+ g_free(s->replaces);
+ blk_unref(s->target);
+ bs_opaque->job = NULL;
+- if (s->dirty_bitmap) {
+- bdrv_release_dirty_bitmap(s->dirty_bitmap);
+- }
+ job_early_fail(&s->common.job);
+ }
+
+@@ -2118,6 +2129,7 @@ fail:
+ bdrv_graph_wrunlock();
+ bdrv_drained_end(bs);
+
++ bdrv_release_dirty_bitmap(bs_opaque->dirty_bitmap);
+ bdrv_unref(mirror_top_bs);
+
+ return NULL;
diff --git a/debian/patches/extra/0009-virtio-gpu-virgl-Add-virtio-gpu-virgl-hostmem-region.patch b/debian/patches/extra/0009-virtio-gpu-virgl-Add-virtio-gpu-virgl-hostmem-region.patch
new file mode 100644
index 0000000..10fbda0
--- /dev/null
+++ b/debian/patches/extra/0009-virtio-gpu-virgl-Add-virtio-gpu-virgl-hostmem-region.patch
@@ -0,0 +1,174 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
+Date: Sat, 14 Feb 2026 13:33:36 +0900
+Subject: [PATCH] virtio-gpu-virgl: Add virtio-gpu-virgl-hostmem-region type
+
+Commit e27194e087ae ("virtio-gpu-virgl: correct parent for blob memory
+region") made the name member of MemoryRegion unset, causing a NULL
+pointer dereference[1]:
+> Thread 2 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
+> (gdb) bt
+> #0 0x00007ffff56565e2 in __strcmp_evex () at /lib64/libc.so.6
+> #1 0x0000555555841bdb in find_fd (head=0x5555572337d0 <cpr_state>,
+> name=0x0, id=0) at ../migration/cpr.c:68
+> #2 cpr_delete_fd (name=name@entry=0x0, id=id@entry=0) at
+> ../migration/cpr.c:77
+> #3 0x000055555582290a in qemu_ram_free (block=0x7ff7e93aa7f0) at
+> ../system/physmem.c:2615
+> #4 0x000055555581ae02 in memory_region_finalize (obj=<optimized out>)
+> at ../system/memory.c:1816
+> #5 0x0000555555a70ab9 in object_deinit (obj=<optimized out>,
+> type=<optimized out>) at ../qom/object.c:715
+> #6 object_finalize (data=0x7ff7e936eff0) at ../qom/object.c:729
+> #7 object_unref (objptr=0x7ff7e936eff0) at ../qom/object.c:1232
+> #8 0x0000555555814fae in memory_region_unref (mr=<optimized out>) at
+> ../system/memory.c:1848
+> #9 flatview_destroy (view=0x555559ed6c40) at ../system/memory.c:301
+> #10 0x0000555555bfc122 in call_rcu_thread (opaque=<optimized out>) at
+> ../util/rcu.c:324
+> #11 0x0000555555bf17a7 in qemu_thread_start (args=0x555557b99520) at
+> ../util/qemu-thread-posix.c:393
+> #12 0x00007ffff556f464 in start_thread () at /lib64/libc.so.6
+> #13 0x00007ffff55f25ac in __clone3 () at /lib64/libc.so.6
+
+The intention of the aforementioned commit is to prevent a MemoryRegion
+from parenting itself while its references is counted indendependently
+of the device. To achieve the same goal, add a type of QOM objects that
+count references and parent MemoryRegions.
+
+[1] https://lore.kernel.org/qemu-devel/4eb93d7a-1fa9-4b3c-8ad7-a2eb64f025a0@collabora.com/
+
+Cc: qemu-stable@nongnu.org
+Fixes: e27194e087ae ("virtio-gpu-virgl: correct parent for blob memory region")
+Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
+Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
+Tested-by: Joelle van Dyne <j@getutm.app>
+Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Message-Id: <20260214-region-v1-1-229f00ae1f38@rsg.ci.i.u-tokyo.ac.jp>
+(cherry picked from commit b2a279094c3b86667969cc645f7fb1087e08dd19)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ hw/display/virtio-gpu-virgl.c | 54 +++++++++++++++++++++++++----------
+ 1 file changed, 39 insertions(+), 15 deletions(-)
+
+diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c
+index 741728cabb..4e515c4ef6 100644
+--- a/hw/display/virtio-gpu-virgl.c
++++ b/hw/display/virtio-gpu-virgl.c
+@@ -52,11 +52,17 @@ virgl_get_egl_display(G_GNUC_UNUSED void *cookie)
+
+ #if VIRGL_VERSION_MAJOR >= 1
+ struct virtio_gpu_virgl_hostmem_region {
++ Object parent_obj;
+ MemoryRegion mr;
+ struct VirtIOGPU *g;
+ bool finish_unmapping;
+ };
+
++#define TYPE_VIRTIO_GPU_VIRGL_HOSTMEM_REGION "virtio-gpu-virgl-hostmem-region"
++
++OBJECT_DECLARE_SIMPLE_TYPE(virtio_gpu_virgl_hostmem_region,
++ VIRTIO_GPU_VIRGL_HOSTMEM_REGION)
++
+ static struct virtio_gpu_virgl_hostmem_region *
+ to_hostmem_region(MemoryRegion *mr)
+ {
+@@ -70,14 +76,22 @@ static void virtio_gpu_virgl_resume_cmdq_bh(void *opaque)
+ virtio_gpu_process_cmdq(g);
+ }
+
+-static void virtio_gpu_virgl_hostmem_region_free(void *obj)
++/*
++ * MR could outlive the resource if MR's reference is held outside of
++ * virtio-gpu. In order to prevent unmapping resource while MR is alive,
++ * and thus, making the data pointer invalid, we will block virtio-gpu
++ * command processing until MR is fully unreferenced and freed.
++ */
++static void virtio_gpu_virgl_hostmem_region_finalize(Object *obj)
+ {
+- MemoryRegion *mr = MEMORY_REGION(obj);
+- struct virtio_gpu_virgl_hostmem_region *vmr;
++ struct virtio_gpu_virgl_hostmem_region *vmr = VIRTIO_GPU_VIRGL_HOSTMEM_REGION(obj);
+ VirtIOGPUBase *b;
+ VirtIOGPUGL *gl;
+
+- vmr = to_hostmem_region(mr);
++ if (!vmr->g) {
++ return;
++ }
++
+ vmr->finish_unmapping = true;
+
+ b = VIRTIO_GPU_BASE(vmr->g);
+@@ -92,11 +106,26 @@ static void virtio_gpu_virgl_hostmem_region_free(void *obj)
+ qemu_bh_schedule(gl->cmdq_resume_bh);
+ }
+
++static const TypeInfo virtio_gpu_virgl_hostmem_region_info = {
++ .parent = TYPE_OBJECT,
++ .name = TYPE_VIRTIO_GPU_VIRGL_HOSTMEM_REGION,
++ .instance_size = sizeof(struct virtio_gpu_virgl_hostmem_region),
++ .instance_finalize = virtio_gpu_virgl_hostmem_region_finalize
++};
++
++static void virtio_gpu_virgl_types(void)
++{
++ type_register_static(&virtio_gpu_virgl_hostmem_region_info);
++}
++
++type_init(virtio_gpu_virgl_types)
++
+ static int
+ virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g,
+ struct virtio_gpu_virgl_resource *res,
+ uint64_t offset)
+ {
++ g_autofree char *name = NULL;
+ struct virtio_gpu_virgl_hostmem_region *vmr;
+ VirtIOGPUBase *b = VIRTIO_GPU_BASE(g);
+ MemoryRegion *mr;
+@@ -117,21 +146,16 @@ virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g,
+ }
+
+ vmr = g_new0(struct virtio_gpu_virgl_hostmem_region, 1);
++ name = g_strdup_printf("blob[%" PRIu32 "]", res->base.resource_id);
++ object_initialize_child(OBJECT(g), name, vmr,
++ TYPE_VIRTIO_GPU_VIRGL_HOSTMEM_REGION);
+ vmr->g = g;
+
+ mr = &vmr->mr;
+- memory_region_init_ram_ptr(mr, OBJECT(mr), NULL, size, data);
++ memory_region_init_ram_ptr(mr, OBJECT(vmr), "mr", size, data);
+ memory_region_add_subregion(&b->hostmem, offset, mr);
+ memory_region_set_enabled(mr, true);
+
+- /*
+- * MR could outlive the resource if MR's reference is held outside of
+- * virtio-gpu. In order to prevent unmapping resource while MR is alive,
+- * and thus, making the data pointer invalid, we will block virtio-gpu
+- * command processing until MR is fully unreferenced and freed.
+- */
+- OBJECT(mr)->free = virtio_gpu_virgl_hostmem_region_free;
+-
+ res->mr = mr;
+
+ trace_virtio_gpu_cmd_res_map_blob(res->base.resource_id, vmr, mr);
+@@ -163,7 +187,7 @@ virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g,
+ * 1. Begin async unmapping with memory_region_del_subregion()
+ * and suspend/block cmd processing.
+ * 2. Wait for res->mr to be freed and cmd processing resumed
+- * asynchronously by virtio_gpu_virgl_hostmem_region_free().
++ * asynchronously by virtio_gpu_virgl_hostmem_region_finalize().
+ * 3. Finish the unmapping with final virgl_renderer_resource_unmap().
+ */
+ if (vmr->finish_unmapping) {
+@@ -186,7 +210,7 @@ virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g,
+ /* memory region owns self res->mr object and frees it by itself */
+ memory_region_set_enabled(mr, false);
+ memory_region_del_subregion(&b->hostmem, mr);
+- object_unref(OBJECT(mr));
++ object_unparent(OBJECT(vmr));
+ }
+
+ return 0;
diff --git a/debian/patches/extra/0010-virtio-gpu-Ensure-BHs-are-invoked-only-from-main-loo.patch b/debian/patches/extra/0010-virtio-gpu-Ensure-BHs-are-invoked-only-from-main-loo.patch
new file mode 100644
index 0000000..dc53ad1
--- /dev/null
+++ b/debian/patches/extra/0010-virtio-gpu-Ensure-BHs-are-invoked-only-from-main-loo.patch
@@ -0,0 +1,123 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Dmitry Osipenko <dmitry.osipenko@collabora.com>
+Date: Wed, 4 Mar 2026 16:50:31 +0000
+Subject: [PATCH] virtio-gpu: Ensure BHs are invoked only from main-loop thread
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+QEMU's display GL core is tied to main-loop thread and virtio-gpu
+interacts with display while processing GPU commands. Virtio-gpu BHs
+work in generic AIO context that can be invoked on vCPU thread, while
+GL and UI toolkits are bound to the main-loop thread.
+
+Make virtio-gpu BHs use iohandler AIO context that is handled in a
+main-loop thread only.
+
+ 0 SDL_GL_MakeCurrent() (libSDL3)
+ 1 SDL_GL_MakeCurrent_REAL() (libSDL2)
+ 2 sdl2_gl_make_context_current() (ui/sdl2-gl.c:201)
+ 3 make_current() (virglrenderer.c:639)
+ 4 vrend_finish_context_switch() (vrend_renderer.c:11630)
+ 5 vrend_hw_switch_context() (vrend_renderer.c:11613)
+ 6 vrend_renderer_force_ctx_0() (vrend_renderer.c:12986)
+ 7 virgl_renderer_force_ctx_0() (virglrenderer.c:460)
+ 8 virtio_gpu_virgl_process_cmd() (virtio-gpu-virgl.c:1013)
+ 9 virtio_gpu_process_cmdq() (virtio-gpu.c:1050)
+ 10 virtio_gpu_gl_handle_ctrl() (virtio-gpu-gl.c:86)
+ 11 aio_bh_poll() (util/async.c)
+ 12 aio_poll() (util/aio-posix.c)
+ 13 blk_pwrite() (block/block-gen.c:1985)
+ 14 pflash_update() (pflash_cfi01.c:396)
+ 15 pflash_write() (pflash_cfi01.c:541)
+ 16 memory_region_dispatch_write() (system/memory.c:1554)
+ 17 flatview_write() (system/physmem.c:3333)
+ 18 address_space_write() (system/physmem.c:3453)
+ 19 kvm_cpu_exec() (accel/kvm/kall-all.c:3248)
+ 20 kvm_vcpu_thread_fn() (accel/kvm/kaccel-ops.c:53)
+
+Cc: qemu-stable@nongnu.org
+Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
+Message-ID: <20260303151422.977399-8-dmitry.osipenko@collabora.com>
+Message-ID: <20260304165043.1437519-10-alex.bennee@linaro.org>
+Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
+(cherry picked from commit 235f9b36383e4cc7a790bca51eddbe38edd5438c)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ hw/display/virtio-gpu-virgl.c | 6 +++---
+ hw/display/virtio-gpu.c | 6 +++---
+ hw/virtio/virtio.c | 10 ++++++++++
+ include/hw/virtio/virtio.h | 10 ++++++++++
+ 4 files changed, 26 insertions(+), 6 deletions(-)
+
+diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c
+index 4e515c4ef6..1129301d91 100644
+--- a/hw/display/virtio-gpu-virgl.c
++++ b/hw/display/virtio-gpu-virgl.c
+@@ -1203,9 +1203,9 @@ int virtio_gpu_virgl_init(VirtIOGPU *g)
+ }
+
+ #if VIRGL_VERSION_MAJOR >= 1
+- gl->cmdq_resume_bh = aio_bh_new(qemu_get_aio_context(),
+- virtio_gpu_virgl_resume_cmdq_bh,
+- g);
++ gl->cmdq_resume_bh = virtio_bh_io_new_guarded(DEVICE(g),
++ virtio_gpu_virgl_resume_cmdq_bh,
++ g);
+ #endif
+
+ return 0;
+diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
+index 43e88a4daf..ad1ebc0fcd 100644
+--- a/hw/display/virtio-gpu.c
++++ b/hw/display/virtio-gpu.c
+@@ -1526,9 +1526,9 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
+
+ g->ctrl_vq = virtio_get_queue(vdev, 0);
+ g->cursor_vq = virtio_get_queue(vdev, 1);
+- g->ctrl_bh = virtio_bh_new_guarded(qdev, virtio_gpu_ctrl_bh, g);
+- g->cursor_bh = virtio_bh_new_guarded(qdev, virtio_gpu_cursor_bh, g);
+- g->reset_bh = qemu_bh_new(virtio_gpu_reset_bh, g);
++ g->ctrl_bh = virtio_bh_io_new_guarded(qdev, virtio_gpu_ctrl_bh, g);
++ g->cursor_bh = virtio_bh_io_new_guarded(qdev, virtio_gpu_cursor_bh, g);
++ g->reset_bh = virtio_bh_io_new_guarded(qdev, virtio_gpu_reset_bh, g);
+ qemu_cond_init(&g->reset_cond);
+ QTAILQ_INIT(&g->reslist);
+ QTAILQ_INIT(&g->cmdq);
+diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
+index 257cda506a..683026adc4 100644
+--- a/hw/virtio/virtio.c
++++ b/hw/virtio/virtio.c
+@@ -4475,3 +4475,13 @@ QEMUBH *virtio_bh_new_guarded_full(DeviceState *dev,
+ return qemu_bh_new_full(cb, opaque, name,
+ &transport->mem_reentrancy_guard);
+ }
++
++QEMUBH *virtio_bh_io_new_guarded_full(DeviceState *dev,
++ QEMUBHFunc *cb, void *opaque,
++ const char *name)
++{
++ DeviceState *transport = qdev_get_parent_bus(dev)->parent;
++
++ return aio_bh_new_full(iohandler_get_aio_context(), cb, opaque, name,
++ &transport->mem_reentrancy_guard);
++}
+diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
+index d97529c3f1..d5bd921581 100644
+--- a/include/hw/virtio/virtio.h
++++ b/include/hw/virtio/virtio.h
+@@ -547,4 +547,14 @@ QEMUBH *virtio_bh_new_guarded_full(DeviceState *dev,
+ #define virtio_bh_new_guarded(dev, cb, opaque) \
+ virtio_bh_new_guarded_full((dev), (cb), (opaque), (stringify(cb)))
+
++/*
++ * The "_io" variant runs BH only on a main-loop thread, while generic BH
++ * may run on a vCPU thread.
++ */
++QEMUBH *virtio_bh_io_new_guarded_full(DeviceState *dev,
++ QEMUBHFunc *cb, void *opaque,
++ const char *name);
++#define virtio_bh_io_new_guarded(dev, cb, opaque) \
++ virtio_bh_io_new_guarded_full((dev), (cb), (opaque), (stringify(cb)))
++
+ #endif
diff --git a/debian/patches/extra/0011-hw-i2c-aspeed_i2c-Fix-out-of-bounds-read-in-I2C-MMIO.patch b/debian/patches/extra/0011-hw-i2c-aspeed_i2c-Fix-out-of-bounds-read-in-I2C-MMIO.patch
new file mode 100644
index 0000000..3fdc98c
--- /dev/null
+++ b/debian/patches/extra/0011-hw-i2c-aspeed_i2c-Fix-out-of-bounds-read-in-I2C-MMIO.patch
@@ -0,0 +1,136 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jamin Lin <jamin_lin@aspeedtech.com>
+Date: Tue, 10 Feb 2026 02:43:32 +0000
+Subject: [PATCH] hw/i2c/aspeed_i2c: Fix out-of-bounds read in I2C MMIO
+ handlers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The ASPEED I2C controller exposes a per-bus MMIO window of 0x80 bytes on
+AST2600/AST1030/AST2700, but the backing regs[] array was sized for only
+28 dwords (0x70 bytes). This allows guest reads in the range [0x70..0x7f]
+to index past the end of regs[].
+
+Fix this by:
+- Sizing ASPEED_I2C_NEW_NUM_REG to match the 0x80-byte window
+ (0x80 >> 2 = 32 dwords).
+- Avoiding an unconditional pre-read from regs[] in the legacy/new read
+ handlers. Initialize the return value to -1 and only read regs[] for
+ offsets that are explicitly handled/valid, leaving invalid offsets to
+ return -1 with a guest error log.
+
+Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
+Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3290
+Reviewed-by: Cédric Le Goater <clg@redhat.com>
+Link: https://lore.kernel.org/qemu-devel/20260210024331.3984696-2-jamin_lin@aspeedtech.com
+Signed-off-by: Cédric Le Goater <clg@redhat.com>
+(cherry picked from commit c2c5beec42bf9872b37e78b9e259132df7435cb5)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ hw/i2c/aspeed_i2c.c | 22 ++++++++++------------
+ include/hw/i2c/aspeed_i2c.h | 3 +--
+ 2 files changed, 11 insertions(+), 14 deletions(-)
+
+diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c
+index c48fa2050b..c455c3eb7c 100644
+--- a/hw/i2c/aspeed_i2c.c
++++ b/hw/i2c/aspeed_i2c.c
+@@ -94,7 +94,7 @@ static uint64_t aspeed_i2c_bus_old_read(AspeedI2CBus *bus, hwaddr offset,
+ unsigned size)
+ {
+ AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller);
+- uint64_t value = bus->regs[offset / sizeof(*bus->regs)];
++ uint64_t value = -1;
+
+ switch (offset) {
+ case A_I2CD_FUN_CTRL:
+@@ -105,7 +105,7 @@ static uint64_t aspeed_i2c_bus_old_read(AspeedI2CBus *bus, hwaddr offset,
+ case A_I2CD_DEV_ADDR:
+ case A_I2CD_POOL_CTRL:
+ case A_I2CD_BYTE_BUF:
+- /* Value is already set, don't do anything. */
++ value = bus->regs[offset / sizeof(*bus->regs)];
+ break;
+ case A_I2CD_CMD:
+ value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus));
+@@ -113,21 +113,20 @@ static uint64_t aspeed_i2c_bus_old_read(AspeedI2CBus *bus, hwaddr offset,
+ case A_I2CD_DMA_ADDR:
+ if (!aic->has_dma) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__);
+- value = -1;
+ break;
+ }
++ value = bus->regs[offset / sizeof(*bus->regs)];
+ break;
+ case A_I2CD_DMA_LEN:
+ if (!aic->has_dma) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__);
+- value = -1;
++ break;
+ }
++ value = bus->regs[offset / sizeof(*bus->regs)];
+ break;
+-
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset);
+- value = -1;
+ break;
+ }
+
+@@ -139,7 +138,7 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset,
+ unsigned size)
+ {
+ AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller);
+- uint64_t value = bus->regs[offset / sizeof(*bus->regs)];
++ uint64_t value = -1;
+
+ switch (offset) {
+ case A_I2CC_FUN_CTRL:
+@@ -159,13 +158,12 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset,
+ case A_I2CS_CMD:
+ case A_I2CS_INTR_CTRL:
+ case A_I2CS_DMA_LEN_STS:
+- /* Value is already set, don't do anything. */
++ case A_I2CS_INTR_STS:
++ value = bus->regs[offset / sizeof(*bus->regs)];
+ break;
+ case A_I2CC_DMA_ADDR:
+ value = extract64(bus->dma_dram_offset, 0, 32);
+ break;
+- case A_I2CS_INTR_STS:
+- break;
+ case A_I2CM_CMD:
+ value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus));
+ break;
+@@ -176,13 +174,13 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset,
+ if (!aic->has_dma64) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA 64 bits support\n",
+ __func__);
+- value = -1;
++ break;
+ }
++ value = bus->regs[offset / sizeof(*bus->regs)];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset);
+- value = -1;
+ break;
+ }
+
+diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h
+index 2daacc10ce..efe8b1a0c5 100644
+--- a/include/hw/i2c/aspeed_i2c.h
++++ b/include/hw/i2c/aspeed_i2c.h
+@@ -36,8 +36,7 @@ OBJECT_DECLARE_TYPE(AspeedI2CState, AspeedI2CClass, ASPEED_I2C)
+ #define ASPEED_I2C_NR_BUSSES 16
+ #define ASPEED_I2C_SHARE_POOL_SIZE 0x800
+ #define ASPEED_I2C_BUS_POOL_SIZE 0x20
+-#define ASPEED_I2C_OLD_NUM_REG 11
+-#define ASPEED_I2C_NEW_NUM_REG 28
++#define ASPEED_I2C_NEW_NUM_REG (0x80 >> 2)
+
+ #define A_I2CD_M_STOP_CMD BIT(5)
+ #define A_I2CD_M_RX_CMD BIT(3)
diff --git a/debian/patches/extra/0012-target-arm-Account-for-SME-in-aarch64_sve_narrow_vq-.patch b/debian/patches/extra/0012-target-arm-Account-for-SME-in-aarch64_sve_narrow_vq-.patch
new file mode 100644
index 0000000..ed8c4ae
--- /dev/null
+++ b/debian/patches/extra/0012-target-arm-Account-for-SME-in-aarch64_sve_narrow_vq-.patch
@@ -0,0 +1,62 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Maydell <peter.maydell@linaro.org>
+Date: Wed, 18 Feb 2026 18:40:13 +0000
+Subject: [PATCH] target/arm: Account for SME in aarch64_sve_narrow_vq()
+ assertion
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In aarch64_sve_narrow_vq() we assert that the new VQ is within
+the maximum supported range for the CPU. We forgot to update
+this to account for SME, which might have a different maximum.
+
+Update the assert to permit any VQ which is valid for either
+SVE or SME.
+
+Cc: qemu-stable@nongnu.org
+Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
+Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
+Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
+Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
+Message-id: 20260202133353.2231685-2-peter.maydell@linaro.org
+(cherry picked from commit 42eab40a12f12f044a5ca7b7d889d9a1f0d172ee)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ target/arm/helper.c | 2 +-
+ target/arm/internals.h | 9 +++++++++
+ 2 files changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/target/arm/helper.c b/target/arm/helper.c
+index 633d314edf..5d31c551e1 100644
+--- a/target/arm/helper.c
++++ b/target/arm/helper.c
+@@ -10058,7 +10058,7 @@ void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq)
+ uint64_t pmask;
+
+ assert(vq >= 1 && vq <= ARM_MAX_VQ);
+- assert(vq <= env_archcpu(env)->sve_max_vq);
++ assert(vq <= arm_max_vq(env_archcpu(env)));
+
+ /* Zap the high bits of the zregs. */
+ for (i = 0; i < 32; i++) {
+diff --git a/target/arm/internals.h b/target/arm/internals.h
+index 75677945af..d5f6d6546f 100644
+--- a/target/arm/internals.h
++++ b/target/arm/internals.h
+@@ -1807,6 +1807,15 @@ static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env)
+ ((1 << (1 - 1)) | (1 << (2 - 1)) | \
+ (1 << (4 - 1)) | (1 << (8 - 1)) | (1 << (16 - 1)))
+
++/*
++ * Return the maximum SVE/SME VQ for this CPU. This defines
++ * the maximum possible size of the Zn vector registers.
++ */
++static inline int arm_max_vq(ARMCPU *cpu)
++{
++ return MAX(cpu->sve_max_vq, cpu->sme_max_vq);
++}
++
+ /*
+ * Return true if it is possible to take a fine-grained-trap to EL2.
+ */
diff --git a/debian/patches/extra/0013-target-arm-Fix-feature-check-in-DO_SVE2_RRX-DO_SVE2_.patch b/debian/patches/extra/0013-target-arm-Fix-feature-check-in-DO_SVE2_RRX-DO_SVE2_.patch
new file mode 100644
index 0000000..880965c
--- /dev/null
+++ b/debian/patches/extra/0013-target-arm-Fix-feature-check-in-DO_SVE2_RRX-DO_SVE2_.patch
@@ -0,0 +1,47 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Maydell <peter.maydell@linaro.org>
+Date: Wed, 18 Feb 2026 18:40:13 +0000
+Subject: [PATCH] target/arm: Fix feature check in DO_SVE2_RRX, DO_SVE2_RRX_TB
+
+In the macros DO_SVE2_RRX and DO_SVE2_RRX_TB we use the
+feature check aa64_sve, thus exposing this set of instructions
+in SVE as well as SVE2. Use aa64_sve2 instead, so they UNDEF
+on an SVE1-only CPU as they should.
+
+Strictly, the condition here should be "SVE2 or SME"; but we
+will correct that in a following commit with all the other
+missing "or SME" checks.
+
+Cc: qemu-stable@nongnu.org
+Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
+Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
+Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+Message-id: 20260202133353.2231685-4-peter.maydell@linaro.org
+(cherry picked from commit ee5bf0962ed6e0eb42d6bc9bfb3687f2408e3580)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ target/arm/tcg/translate-sve.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c
+index 07b827fa8e..d69a2f5d75 100644
+--- a/target/arm/tcg/translate-sve.c
++++ b/target/arm/tcg/translate-sve.c
+@@ -3769,7 +3769,7 @@ TRANS_FEAT(UDOT_zzxw_2s, aa64_sme2_or_sve2p1, gen_gvec_ool_arg_zzxz,
+ gen_helper_gvec_udot_idx_2h, a)
+
+ #define DO_SVE2_RRX(NAME, FUNC) \
+- TRANS_FEAT(NAME, aa64_sve, gen_gvec_ool_zzz, FUNC, \
++ TRANS_FEAT(NAME, aa64_sve2, gen_gvec_ool_zzz, FUNC, \
+ a->rd, a->rn, a->rm, a->index)
+
+ DO_SVE2_RRX(MUL_zzx_h, gen_helper_gvec_mul_idx_h)
+@@ -3787,7 +3787,7 @@ DO_SVE2_RRX(SQRDMULH_zzx_d, gen_helper_sve2_sqrdmulh_idx_d)
+ #undef DO_SVE2_RRX
+
+ #define DO_SVE2_RRX_TB(NAME, FUNC, TOP) \
+- TRANS_FEAT(NAME, aa64_sve, gen_gvec_ool_zzz, FUNC, \
++ TRANS_FEAT(NAME, aa64_sve2, gen_gvec_ool_zzz, FUNC, \
+ a->rd, a->rn, a->rm, (a->index << 1) | TOP)
+
+ DO_SVE2_RRX_TB(SQDMULLB_zzx_s, gen_helper_sve2_sqdmull_idx_s, false)
diff --git a/debian/patches/extra/0014-target-arm-tcg-Allow-SVE-RAX1-in-SME2p1-streaming-mo.patch b/debian/patches/extra/0014-target-arm-tcg-Allow-SVE-RAX1-in-SME2p1-streaming-mo.patch
new file mode 100644
index 0000000..d24758e
--- /dev/null
+++ b/debian/patches/extra/0014-target-arm-tcg-Allow-SVE-RAX1-in-SME2p1-streaming-mo.patch
@@ -0,0 +1,44 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Maydell <peter.maydell@linaro.org>
+Date: Wed, 18 Feb 2026 18:40:13 +0000
+Subject: [PATCH] target/arm/tcg: Allow SVE RAX1 in SME2p1 streaming mode
+
+The SVE RAX1 instruction is permitted in SME streaming mode starting
+from SME2p1. We forgot to allow this relaxation when we implemented
+SME2p1.
+
+Cc: qemu-stable@nongnu.org
+Fixes: 7b1613a1020d2 ("target/arm: Enable FEAT_SME2p1 on -cpu max")
+Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
+Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
+Message-id: 20260202133353.2231685-5-peter.maydell@linaro.org
+(cherry picked from commit 433097a2242120918090201129e5fbb8e16b3e34)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ target/arm/tcg/translate-sve.c | 13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c
+index d69a2f5d75..76e4a6c52c 100644
+--- a/target/arm/tcg/translate-sve.c
++++ b/target/arm/tcg/translate-sve.c
+@@ -7803,8 +7803,17 @@ TRANS_FEAT_NONSTREAMING(SM4E, aa64_sve2_sm4, gen_gvec_ool_arg_zzz,
+ TRANS_FEAT_NONSTREAMING(SM4EKEY, aa64_sve2_sm4, gen_gvec_ool_arg_zzz,
+ gen_helper_crypto_sm4ekey, a, 0)
+
+-TRANS_FEAT_NONSTREAMING(RAX1, aa64_sve2_sha3, gen_gvec_fn_arg_zzz,
+- gen_gvec_rax1, a)
++static bool trans_RAX1(DisasContext *s, arg_RAX1 *a)
++{
++ if (!dc_isar_feature(aa64_sve2_sha3, s)) {
++ return false;
++ }
++ if (!dc_isar_feature(aa64_sme2p1, s)) {
++ /* SME2p1 adds this as valid in streaming SVE mode */
++ s->is_nonstreaming = true;
++ }
++ return gen_gvec_fn_arg_zzz(s, gen_gvec_rax1, a);
++}
+
+ TRANS_FEAT(FCVTNT_sh, aa64_sve2, gen_gvec_fpst_arg_zpz,
+ gen_helper_sve2_fcvtnt_sh, a, 0, FPST_A64)
diff --git a/debian/patches/extra/0015-target-arm-Don-t-let-sme-on-downgrade-SME.patch b/debian/patches/extra/0015-target-arm-Don-t-let-sme-on-downgrade-SME.patch
new file mode 100644
index 0000000..b6859b0
--- /dev/null
+++ b/debian/patches/extra/0015-target-arm-Don-t-let-sme-on-downgrade-SME.patch
@@ -0,0 +1,98 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Maydell <peter.maydell@linaro.org>
+Date: Wed, 18 Feb 2026 18:40:13 +0000
+Subject: [PATCH] target/arm: Don't let 'sme=on' downgrade SME
+
+In our handling of the boolean 'sme' CPU property, we write this 0/1
+value directly to ID_AA64PFR1_EL1.SME. This worked when the only
+valid values in that field were 0 (for no SME) and 1 (for SME1).
+However, with the addition of SME2 the SME field can now also read 2.
+This means that "-cpu max,sme=on" will result in an inconsistent set
+of ID registers, where ID_AA64PFR1_EL1.SME claims SME1 but
+ID_AA64SMFR0_EL1.SMEver claims SME2p1. This isn't a valid thing to
+report, and confuses Linux into reporting SME2 to userspace but not
+actually enabling userspace access for it.
+
+Fix this bug by having arm_cpu_sme_finalize() fix up the
+ID_AA64PFR1_EL1.SME field to match ID_AA64SMFR0.SMEver. This means
+the "sme" property's semantics are "off" for "no SME" and "on" for
+"enable at whatever the default SME version this CPU provides is".
+
+Update the documentation to clarify what 'sve=on' and 'sme=on' do.
+(We don't have the equivalent bug for 'sve=on' because
+ID_AA64PFR0_EL1.SVE only has 0 and 1 as valid values, but the
+semantics of the property are the same.)
+
+Cc: qemu-stable@nongnu.org
+Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
+Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
+Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+Message-id: 20260202133353.2231685-6-peter.maydell@linaro.org
+(cherry picked from commit aeb3c147fc4a1eb9a73f9f10923fc06def088aeb)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ docs/system/arm/cpu-features.rst | 10 ++++++++++
+ target/arm/cpu64.c | 15 +++++++++++++++
+ 2 files changed, 25 insertions(+)
+
+diff --git a/docs/system/arm/cpu-features.rst b/docs/system/arm/cpu-features.rst
+index 37d5dfd15b..024119449c 100644
+--- a/docs/system/arm/cpu-features.rst
++++ b/docs/system/arm/cpu-features.rst
+@@ -318,6 +318,11 @@ SVE CPU Property Parsing Semantics
+ provided an error will be generated. To avoid this error, one must
+ enable at least one vector length prior to enabling SVE.
+
++ 10) Enabling SVE (with ``sve=on`` or by default) enables all the SVE
++ sub-features that the CPU supports (for example, it may also
++ enable SVE2). There are not generally any lower-level controls
++ for disabling specific SVE sub-features.
++
+ SVE CPU Property Examples
+ -------------------------
+
+@@ -430,6 +435,11 @@ and all vector lengths must be powers of 2. The maximum vector
+ length supported by qemu is 2048 bits. Otherwise, there are no
+ additional constraints on the set of vector lengths supported by SME.
+
++As with SVE, ``sme=on`` enables all the SME sub-features the CPU
++supports (for example, it may also enable SME2), and there are
++no lower-level controls for fine-grained disabling of specific
++SME sub-features.
++
+ SME User-mode Default Vector Length Property
+ --------------------------------------------
+
+diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
+index ae84d8e420..2082672dea 100644
+--- a/target/arm/cpu64.c
++++ b/target/arm/cpu64.c
+@@ -363,6 +363,16 @@ void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp)
+
+ cpu->sme_vq.map = vq_map;
+ cpu->sme_max_vq = 32 - clz32(vq_map);
++
++ /*
++ * The "sme" property setter writes a bool value into ID_AA64PFR1_EL1.SME
++ * (and at this point we know it's not 0). Correct that value to report
++ * the same SME version as ID_AA64SMFR0_EL1.SMEver.
++ */
++ if (FIELD_EX64_IDREG(&cpu->isar, ID_AA64SMFR0, SMEVER) != 0) {
++ /* SME2 or better */
++ FIELD_DP64_IDREG(&cpu->isar, ID_AA64PFR1, SME, 2);
++ }
+ }
+
+ static bool cpu_arm_get_sme(Object *obj, Error **errp)
+@@ -375,6 +385,11 @@ static void cpu_arm_set_sme(Object *obj, bool value, Error **errp)
+ {
+ ARMCPU *cpu = ARM_CPU(obj);
+
++ /*
++ * For now, write 0 for "off" and 1 for "on" into the PFR1 field.
++ * We will correct this value to report the right SME
++ * level (SME vs SME2) in arm_cpu_sme_finalize() later.
++ */
+ FIELD_DP64_IDREG(&cpu->isar, ID_AA64PFR1, SME, value);
+ }
+
diff --git a/debian/patches/extra/0016-target-arm-set-the-correct-TI-bits-for-WFIT-traps.patch b/debian/patches/extra/0016-target-arm-set-the-correct-TI-bits-for-WFIT-traps.patch
new file mode 100644
index 0000000..9b10110
--- /dev/null
+++ b/debian/patches/extra/0016-target-arm-set-the-correct-TI-bits-for-WFIT-traps.patch
@@ -0,0 +1,35 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org>
+Date: Thu, 26 Feb 2026 11:27:18 +0000
+Subject: [PATCH] target/arm: set the correct TI bits for WFIT traps
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The WFIT trap should be reported as 0b10.
+
+Cc: qemu-stable@nongnu.org
+Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
+Reviewed-by: Gustavo Romero <gustavo.romero@linaro.org>
+Message-id: 20260220171945.1065102-1-alex.bennee@linaro.org
+Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
+Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
+(cherry picked from commit 662fd548a027c9362df71ebfc0c9cdd7b1f349fb)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ target/arm/tcg/op_helper.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c
+index 4fbd219555..9c0651f000 100644
+--- a/target/arm/tcg/op_helper.c
++++ b/target/arm/tcg/op_helper.c
+@@ -448,7 +448,7 @@ void HELPER(wfit)(CPUARMState *env, uint64_t timeout)
+
+ if (target_el) {
+ env->pc -= 4;
+- raise_exception(env, excp, syn_wfx(1, 0xe, 0, false), target_el);
++ raise_exception(env, excp, syn_wfx(1, 0xe, 2, false), target_el);
+ }
+
+ if (uadd64_overflow(timeout, offset, &nexttick)) {
diff --git a/debian/patches/extra/0017-aio-posix-notify-main-loop-when-SQEs-are-queued.patch b/debian/patches/extra/0017-aio-posix-notify-main-loop-when-SQEs-are-queued.patch
new file mode 100644
index 0000000..ddc6803
--- /dev/null
+++ b/debian/patches/extra/0017-aio-posix-notify-main-loop-when-SQEs-are-queued.patch
@@ -0,0 +1,119 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jens Axboe <axboe@kernel.dk>
+Date: Wed, 18 Feb 2026 15:09:58 -0500
+Subject: [PATCH] aio-posix: notify main loop when SQEs are queued
+
+When a vCPU thread handles MMIO (holding BQL), aio_co_enter() runs the
+block I/O coroutine inline on the vCPU thread because
+qemu_get_current_aio_context() returns the main AioContext when BQL is
+held. The coroutine calls luring_co_submit() which queues an SQE via
+fdmon_io_uring_add_sqe(), but the actual io_uring_submit() only happens
+in gsource_prepare() on the main loop thread.
+
+Since the coroutine ran inline (not via aio_co_schedule()), no BH is
+scheduled and aio_notify() is never called. The main loop remains asleep
+in ppoll() with up to a 499ms timeout, leaving the SQE unsubmitted until
+the next timer fires.
+
+Fix this by calling aio_notify() after queuing the SQE. This wakes the
+main loop via the eventfd so it can run gsource_prepare() and submit the
+pending SQE promptly.
+
+This is a generic fix that benefits all devices using aio=io_uring.
+Without it, AHCI/SATA devices see MUCH worse I/O latency since they use
+MMIO (not ioeventfd like virtio) and have no other mechanism to wake the
+main loop after queuing block I/O.
+
+This is usually a bit hard to detect, as it also relies on the ppoll
+loop not waking up for other activity, and micro benchmarks tend not to
+see it because they don't have any real processing time. With a
+synthetic test case that has a few usleep() to simulate processing of
+read data, it's very noticeable. The below example reads 128MB with
+O_DIRECT in 128KB chunks in batches of 16, and has a 1ms delay before
+each batch submit, and a 1ms delay after processing each completion.
+Running it on /dev/sda yields:
+
+time sudo ./iotest /dev/sda
+
+________________________________________________________
+Executed in 25.76 secs fish external
+ usr time 6.19 millis 783.00 micros 5.41 millis
+ sys time 12.43 millis 642.00 micros 11.79 millis
+
+while on a virtio-blk or NVMe device we get:
+
+time sudo ./iotest /dev/vdb
+
+________________________________________________________
+Executed in 1.25 secs fish external
+ usr time 1.40 millis 0.30 millis 1.10 millis
+ sys time 17.61 millis 1.43 millis 16.18 millis
+
+time sudo ./iotest /dev/nvme0n1
+
+________________________________________________________
+Executed in 1.26 secs fish external
+ usr time 6.11 millis 0.52 millis 5.59 millis
+ sys time 13.94 millis 1.50 millis 12.43 millis
+
+where the latter are consistent. If we run the same test but keep the
+socket for the ssh connection active by having activity there, then
+the sda test looks as follows:
+
+time sudo ./iotest /dev/sda
+
+________________________________________________________
+Executed in 1.23 secs fish external
+ usr time 2.70 millis 39.00 micros 2.66 millis
+ sys time 4.97 millis 977.00 micros 3.99 millis
+
+as now the ppoll loop is woken all the time anyway.
+
+After this fix, on an idle system:
+
+time sudo ./iotest /dev/sda
+
+________________________________________________________
+Executed in 1.30 secs fish external
+ usr time 2.14 millis 0.14 millis 2.00 millis
+ sys time 16.93 millis 1.16 millis 15.76 millis
+
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Message-Id: <07d701b9-3039-4f9b-99a2-abeae51146a5@kernel.dk>
+Reviewed-by: Kevin Wolf <kwolf@redhat.com>
+[Generalize the comment since this applies to all vCPU thread activity,
+not just coroutines, as suggested by Kevin Wolf <kwolf@redhat.com>.
+--Stefan]
+Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
+(cherry picked from commit 2ae361ef1d7d526b07ff88d854552e2d009bfb1b)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ util/aio-posix.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/util/aio-posix.c b/util/aio-posix.c
+index e24b955fd9..488d964611 100644
+--- a/util/aio-posix.c
++++ b/util/aio-posix.c
+@@ -23,6 +23,7 @@
+ #include "qemu/rcu_queue.h"
+ #include "qemu/sockets.h"
+ #include "qemu/cutils.h"
++#include "system/iothread.h"
+ #include "trace.h"
+ #include "aio-posix.h"
+
+@@ -813,5 +814,13 @@ void aio_add_sqe(void (*prep_sqe)(struct io_uring_sqe *sqe, void *opaque),
+ {
+ AioContext *ctx = qemu_get_current_aio_context();
+ ctx->fdmon_ops->add_sqe(ctx, prep_sqe, opaque, cqe_handler);
++
++ /*
++ * Wake the main loop if it is sleeping in ppoll(). When a vCPU thread
++ * queues SQEs, the actual io_uring_submit() only happens in
++ * gsource_prepare() in the main loop thread. Without this notify, the
++ * main loop thread's ppoll() can sleep up to 499ms before submitting.
++ */
++ aio_notify(ctx);
+ }
+ #endif /* CONFIG_LINUX_IO_URING */
diff --git a/debian/patches/extra/0018-fdmon-io_uring-check-CQ-ring-directly-in-gsource_che.patch b/debian/patches/extra/0018-fdmon-io_uring-check-CQ-ring-directly-in-gsource_che.patch
new file mode 100644
index 0000000..263e145
--- /dev/null
+++ b/debian/patches/extra/0018-fdmon-io_uring-check-CQ-ring-directly-in-gsource_che.patch
@@ -0,0 +1,49 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jens Axboe <axboe@kernel.dk>
+Date: Fri, 13 Feb 2026 07:26:37 -0700
+Subject: [PATCH] fdmon-io_uring: check CQ ring directly in gsource_check
+
+gsource_check() only looks at the ppoll revents for the io_uring fd,
+but CQEs can be posted during gsource_prepare()'s io_uring_submit()
+call via kernel task_work processing on syscall exit. These completions
+are already sitting in the CQ ring but the ring fd may not be signaled
+yet, causing gsource_check() to return false.
+
+Add a fallback io_uring_cq_ready() check so completions that arrive
+during submission are dispatched immediately rather than waiting for
+the next ppoll() cycle.
+
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Message-ID: <20260213143225.161043-3-axboe@kernel.dk>
+Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
+(cherry picked from commit 961fcc0f22768e7c3432fc645b93dc7cd4932fae)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ util/fdmon-io_uring.c | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/util/fdmon-io_uring.c b/util/fdmon-io_uring.c
+index d0b56127c6..b81e412402 100644
+--- a/util/fdmon-io_uring.c
++++ b/util/fdmon-io_uring.c
+@@ -344,7 +344,19 @@ static void fdmon_io_uring_gsource_prepare(AioContext *ctx)
+ static bool fdmon_io_uring_gsource_check(AioContext *ctx)
+ {
+ gpointer tag = ctx->io_uring_fd_tag;
+- return g_source_query_unix_fd(&ctx->source, tag) & G_IO_IN;
++
++ /* Check ppoll revents (normal path) */
++ if (g_source_query_unix_fd(&ctx->source, tag) & G_IO_IN) {
++ return true;
++ }
++
++ /*
++ * Also check for CQEs that may have been posted during prepare's
++ * io_uring_submit() via task_work on syscall exit. Without this,
++ * the main loop can miss completions and sleep in ppoll() until the
++ * next timer fires.
++ */
++ return io_uring_cq_ready(&ctx->fdmon_io_uring);
+ }
+
+ /* Dispatch CQE handlers that are ready */
diff --git a/debian/patches/extra/0019-target-i386-add-compat-for-migrating-error-code.patch b/debian/patches/extra/0019-target-i386-add-compat-for-migrating-error-code.patch
new file mode 100644
index 0000000..37a1d3a
--- /dev/null
+++ b/debian/patches/extra/0019-target-i386-add-compat-for-migrating-error-code.patch
@@ -0,0 +1,75 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fiona Ebner <f.ebner@proxmox.com>
+Date: Tue, 3 Mar 2026 15:29:22 +0100
+Subject: [PATCH] target/i386: add compat for migrating error code
+
+If cpu->env.has_error_code is true, backwards migration of a VM from
+a QEMU binary with commit 27535e9cca to a QEMU binary without commit
+27535e9cca will fail:
+
+> kvm: error while loading state for instance 0x0 of device 'cpu'
+
+In practice, wrongly setting the error code to 0 on the target is
+often unproblematic, so additionally checking error_code != 0 in
+cpu_errcode_needed() is not enough to mitigate the issue. Instead, add
+proper machine version compat handling.
+
+Cc: qemu-stable@nongnu.org
+Fixes: 27535e9cca ("target/i386: Add support for save/load of exception error code")
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ hw/i386/pc.c | 1 +
+ target/i386/cpu.c | 1 +
+ target/i386/cpu.h | 1 +
+ target/i386/machine.c | 2 +-
+ 4 files changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/hw/i386/pc.c b/hw/i386/pc.c
+index f8b919cb6c..48e153c93f 100644
+--- a/hw/i386/pc.c
++++ b/hw/i386/pc.c
+@@ -83,6 +83,7 @@
+
+ GlobalProperty pc_compat_10_1[] = {
+ { "mch", "extended-tseg-mbytes", "16" },
++ { TYPE_X86_CPU, "x-migrate-error-code", "false" },
+ };
+ const size_t pc_compat_10_1_len = G_N_ELEMENTS(pc_compat_10_1);
+
+diff --git a/target/i386/cpu.c b/target/i386/cpu.c
+index 6417775786..78308a82a0 100644
+--- a/target/i386/cpu.c
++++ b/target/i386/cpu.c
+@@ -10024,6 +10024,7 @@ static const Property x86_cpu_properties[] = {
+ DEFINE_PROP_BOOL("tcg-cpuid", X86CPU, expose_tcg, true),
+ DEFINE_PROP_BOOL("x-migrate-smi-count", X86CPU, migrate_smi_count,
+ true),
++ DEFINE_PROP_BOOL("x-migrate-error-code", X86CPU, migrate_error_code, true),
+ /*
+ * lecacy_cache defaults to true unless the CPU model provides its
+ * own cache information (see x86_cpu_load_def()).
+diff --git a/target/i386/cpu.h b/target/i386/cpu.h
+index cee1f692a1..ee3ed5ae6d 100644
+--- a/target/i386/cpu.h
++++ b/target/i386/cpu.h
+@@ -2211,6 +2211,7 @@ struct ArchCPU {
+ bool expose_tcg;
+ bool migratable;
+ bool migrate_smi_count;
++ bool migrate_error_code;
+ uint32_t apic_id;
+
+ /* Enables publishing of TSC increment and Local APIC bus frequencies to
+diff --git a/target/i386/machine.c b/target/i386/machine.c
+index 45b7cea80a..21531f8ba2 100644
+--- a/target/i386/machine.c
++++ b/target/i386/machine.c
+@@ -466,7 +466,7 @@ static bool cpu_errcode_needed(void *opaque)
+ {
+ X86CPU *cpu = opaque;
+
+- return cpu->env.has_error_code != 0;
++ return cpu->env.has_error_code != 0 && cpu->migrate_error_code;
+ }
+
+ static const VMStateDescription vmstate_error_code = {
--git a/debian/patches/extra/0020-virtio-snd-remove-TODO-comments.patch b/debian/patches/extra/0020-virtio-snd-remove-TODO-comments.patch
new file mode 100644
index 0000000..eddb849
--- /dev/null
+++ b/debian/patches/extra/0020-virtio-snd-remove-TODO-comments.patch
@@ -0,0 +1,93 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+Date: Fri, 20 Feb 2026 11:40:13 +0200
+Subject: [PATCH] virtio-snd: remove TODO comments
+
+Replying with a VIRTIO_SND_S_BAD_MSG error does not warrant a device
+reset. Instead, a device reset happens when the driver requests it from the
+transport.
+
+Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Message-Id: <20260220-virtio-snd-series-v1-2-207c4f7200a2@linaro.org>
+(cherry picked from commit 34238f078a04f24b91199249b83846ab082b4e05)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ hw/audio/virtio-snd.c | 21 ---------------------
+ 1 file changed, 21 deletions(-)
+
+diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
+index 9101560f38..fd03efc120 100644
+--- a/hw/audio/virtio-snd.c
++++ b/hw/audio/virtio-snd.c
+@@ -168,9 +168,6 @@ static void virtio_snd_handle_pcm_info(VirtIOSound *s,
+ sizeof(virtio_snd_query_info));
+
+ if (msg_sz != sizeof(virtio_snd_query_info)) {
+- /*
+- * TODO: do we need to set DEVICE_NEEDS_RESET?
+- */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: virtio-snd command size incorrect %zu vs \
+ %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info));
+@@ -184,9 +181,6 @@ static void virtio_snd_handle_pcm_info(VirtIOSound *s,
+
+ if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) <
+ sizeof(virtio_snd_hdr) + size * count) {
+- /*
+- * TODO: do we need to set DEVICE_NEEDS_RESET?
+- */
+ error_report("pcm info: buffer too small, got: %zu, needed: %zu",
+ iov_size(cmd->elem->in_sg, cmd->elem->in_num),
+ sizeof(virtio_snd_pcm_info));
+@@ -244,9 +238,6 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
+ virtio_snd_pcm_set_params *st_params;
+
+ if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) {
+- /*
+- * TODO: do we need to set DEVICE_NEEDS_RESET?
+- */
+ virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n");
+ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ }
+@@ -297,9 +288,6 @@ static void virtio_snd_handle_pcm_set_params(VirtIOSound *s,
+ sizeof(virtio_snd_pcm_set_params));
+
+ if (msg_sz != sizeof(virtio_snd_pcm_set_params)) {
+- /*
+- * TODO: do we need to set DEVICE_NEEDS_RESET?
+- */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: virtio-snd command size incorrect %zu vs \
+ %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_set_params));
+@@ -609,9 +597,6 @@ static void virtio_snd_handle_pcm_release(VirtIOSound *s,
+ sizeof(stream_id));
+
+ if (msg_sz != sizeof(stream_id)) {
+- /*
+- * TODO: do we need to set DEVICE_NEEDS_RESET?
+- */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: virtio-snd command size incorrect %zu vs \
+ %zu\n", __func__, msg_sz, sizeof(stream_id));
+@@ -623,9 +608,6 @@ static void virtio_snd_handle_pcm_release(VirtIOSound *s,
+ trace_virtio_snd_handle_pcm_release(stream_id);
+ stream = virtio_snd_pcm_get_stream(s, stream_id);
+ if (stream == NULL) {
+- /*
+- * TODO: do we need to set DEVICE_NEEDS_RESET?
+- */
+ error_report("already released stream %"PRIu32, stream_id);
+ virtio_error(VIRTIO_DEVICE(s),
+ "already released stream %"PRIu32,
+@@ -668,9 +650,6 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd)
+ sizeof(virtio_snd_hdr));
+
+ if (msg_sz != sizeof(virtio_snd_hdr)) {
+- /*
+- * TODO: do we need to set DEVICE_NEEDS_RESET?
+- */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: virtio-snd command size incorrect %zu vs \
+ %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr));
diff --git a/debian/patches/extra/0021-virtio-snd-handle-5.14.6.2-for-PCM_INFO-properly.patch b/debian/patches/extra/0021-virtio-snd-handle-5.14.6.2-for-PCM_INFO-properly.patch
new file mode 100644
index 0000000..ec9ad51
--- /dev/null
+++ b/debian/patches/extra/0021-virtio-snd-handle-5.14.6.2-for-PCM_INFO-properly.patch
@@ -0,0 +1,89 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+Date: Fri, 20 Feb 2026 11:40:14 +0200
+Subject: [PATCH] virtio-snd: handle 5.14.6.2 for PCM_INFO properly
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The section 5.14.6.2 of the VIRTIO spec says:
+
+ 5.14.6.2 Driver Requirements: Item Information Request
+
+ - The driver MUST NOT set start_id and count such that start_id +
+ count is greater than the total number of particular items that is
+ indicated in the device configuration space.
+
+ - The driver MUST provide a buffer of sizeof(struct virtio_snd_hdr) +
+ count * size bytes for the response.
+
+While we performed some check for the second requirement, it failed to
+check for integer overflow.
+
+Add also a check for the first requirement, which should limit exposure
+to any overflow, since realistically the number of streams will be low
+enough in value such that overflow is improbable.
+
+Cc: qemu-stable@nongnu.org
+Reported-by: 罗铭源 <myluo24@m.fudan.edu.cn>
+Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Message-Id: <20260220-virtio-snd-series-v1-3-207c4f7200a2@linaro.org>
+(cherry picked from commit 61679d7dcfa2dffc8fb115aa19b09e0e7cf5ea5c)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ hw/audio/virtio-snd.c | 31 +++++++++++++++++++++++++++----
+ 1 file changed, 27 insertions(+), 4 deletions(-)
+
+diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
+index fd03efc120..e9c24d6795 100644
+--- a/hw/audio/virtio-snd.c
++++ b/hw/audio/virtio-snd.c
+@@ -156,7 +156,7 @@ static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s,
+ static void virtio_snd_handle_pcm_info(VirtIOSound *s,
+ virtio_snd_ctrl_command *cmd)
+ {
+- uint32_t stream_id, start_id, count, size;
++ uint32_t stream_id, start_id, count, size, tmp;
+ virtio_snd_pcm_info val;
+ virtio_snd_query_info req;
+ VirtIOSoundPCMStream *stream = NULL;
+@@ -179,11 +179,34 @@ static void virtio_snd_handle_pcm_info(VirtIOSound *s,
+ count = le32_to_cpu(req.count);
+ size = le32_to_cpu(req.size);
+
+- if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) <
+- sizeof(virtio_snd_hdr) + size * count) {
++ /*
++ * 5.14.6.2 Driver Requirements: Item Information Request
++ * "The driver MUST NOT set start_id and count such that start_id + count
++ * is greater than the total number of particular items that is indicated
++ * in the device configuration space."
++ */
++ if (start_id > s->snd_conf.streams
++ || !g_uint_checked_add(&tmp, start_id, count)
++ || start_id + count > s->snd_conf.streams) {
++ error_report("pcm info: start_id + count is greater than the total "
++ "number of streams, got: start_id = %u, count = %u",
++ start_id, count);
++ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
++ return;
++ }
++
++ /*
++ * 5.14.6.2 Driver Requirements: Item Information Request
++ * "The driver MUST provide a buffer of sizeof(struct virtio_snd_hdr) +
++ * count * size bytes for the response."
++ */
++ if (!g_uint_checked_mul(&tmp, size, count)
++ || !g_uint_checked_add(&tmp, tmp, sizeof(virtio_snd_hdr))
++ || iov_size(cmd->elem->in_sg, cmd->elem->in_num) <
++ sizeof(virtio_snd_hdr) + size * count) {
+ error_report("pcm info: buffer too small, got: %zu, needed: %zu",
+ iov_size(cmd->elem->in_sg, cmd->elem->in_num),
+- sizeof(virtio_snd_pcm_info));
++ sizeof(virtio_snd_pcm_info) * count);
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
diff --git a/debian/patches/extra/0022-virtio-snd-fix-max_size-bounds-check-in-input-cb.patch b/debian/patches/extra/0022-virtio-snd-fix-max_size-bounds-check-in-input-cb.patch
new file mode 100644
index 0000000..e4e255a
--- /dev/null
+++ b/debian/patches/extra/0022-virtio-snd-fix-max_size-bounds-check-in-input-cb.patch
@@ -0,0 +1,44 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+Date: Fri, 20 Feb 2026 11:40:15 +0200
+Subject: [PATCH] virtio-snd: fix max_size bounds check in input cb
+
+In 98e77e3d we calculated the max size and checked that each buffer is smaller than it.
+
+We neglected to subtract the size of the virtio_snd_pcm_status header
+from the max size, and max_size was thus larger than the correct value,
+leading to potential OOB writes.
+
+If the buffer cannot fit the header or can fit only the header, return
+the buffer immediately.
+
+Cc: qemu-stable@nongnu.org
+Fixes: 98e77e3dd8dd6e7aa9a7dffa60f49c8c8a49d4e3 ("virtio-snd: add max size bounds check in input cb")
+Reported-by: DARKNAVY <vr@darknavy.com>
+Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Message-Id: <20260220-virtio-snd-series-v1-4-207c4f7200a2@linaro.org>
+(cherry picked from commit bcb53328aa70023f1405fade4e253e7f77567261)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ hw/audio/virtio-snd.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
+index e9c24d6795..3437211f79 100644
+--- a/hw/audio/virtio-snd.c
++++ b/hw/audio/virtio-snd.c
+@@ -1255,6 +1255,12 @@ static void virtio_snd_pcm_in_cb(void *data, int available)
+ }
+
+ max_size = iov_size(buffer->elem->in_sg, buffer->elem->in_num);
++ if (max_size <= sizeof(virtio_snd_pcm_status)) {
++ return_rx_buffer(stream, buffer);
++ continue;
++ }
++ max_size -= sizeof(virtio_snd_pcm_status);
++
+ for (;;) {
+ if (buffer->size >= max_size) {
+ return_rx_buffer(stream, buffer);
diff --git a/debian/patches/extra/0023-virtio-snd-tighten-read-amount-in-in_cb.patch b/debian/patches/extra/0023-virtio-snd-tighten-read-amount-in-in_cb.patch
new file mode 100644
index 0000000..00e496c
--- /dev/null
+++ b/debian/patches/extra/0023-virtio-snd-tighten-read-amount-in-in_cb.patch
@@ -0,0 +1,51 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+Date: Fri, 20 Feb 2026 11:40:16 +0200
+Subject: [PATCH] virtio-snd: tighten read amount in in_cb
+
+The amount of bytes to read passed to AUD_read() should never surpass
+the maximum available buffer length. Tighten the current amount by
+MIN(<amount>, max_size - <existing size>).
+
+Cc: qemu-stable@nongnu.org
+Fixes: 98e77e3dd8dd6e7aa9a7dffa60f49c8c8a49d4e3 ("virtio-snd: add max size bounds check in input cb")
+Reported-by: DARKNAVY <vr@darknavy.com>
+Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Message-Id: <20260220-virtio-snd-series-v1-5-207c4f7200a2@linaro.org>
+(cherry picked from commit 7994203bb1b83a6604f3ab00fe9598909bb66164)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ hw/audio/virtio-snd.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
+index 3437211f79..fc0781ae9a 100644
+--- a/hw/audio/virtio-snd.c
++++ b/hw/audio/virtio-snd.c
+@@ -1240,7 +1240,7 @@ static void virtio_snd_pcm_in_cb(void *data, int available)
+ {
+ VirtIOSoundPCMStream *stream = data;
+ VirtIOSoundPCMBuffer *buffer;
+- size_t size, max_size;
++ size_t size, max_size, to_read;
+
+ WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
+ while (!QSIMPLEQ_EMPTY(&stream->queue)) {
+@@ -1266,10 +1266,12 @@ static void virtio_snd_pcm_in_cb(void *data, int available)
+ return_rx_buffer(stream, buffer);
+ break;
+ }
++ to_read = stream->params.period_bytes - buffer->size;
++ to_read = MIN(to_read, available);
++ to_read = MIN(to_read, max_size - buffer->size);
+ size = AUD_read(stream->voice.in,
+- buffer->data + buffer->size,
+- MIN(available, (stream->params.period_bytes -
+- buffer->size)));
++ buffer->data + buffer->size,
++ to_read);
+ if (!size) {
+ available = 0;
+ break;
diff --git a/debian/patches/extra/0024-hw-misc-virt_ctrl-Fix-incorrect-trace-event-in-read-.patch b/debian/patches/extra/0024-hw-misc-virt_ctrl-Fix-incorrect-trace-event-in-read-.patch
new file mode 100644
index 0000000..fd41b73
--- /dev/null
+++ b/debian/patches/extra/0024-hw-misc-virt_ctrl-Fix-incorrect-trace-event-in-read-.patch
@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Kuan-Wei Chiu <visitorckw@gmail.com>
+Date: Sun, 11 Jan 2026 18:49:15 +0000
+Subject: [PATCH] hw/misc/virt_ctrl: Fix incorrect trace event in read
+ operation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The virt_ctrl_read() function currently invokes trace_virt_ctrl_write()
+instead of trace_virt_ctrl_read(). This results in read operations
+appearing as write operations in the trace output, which is misleading
+during debugging and analysis.
+
+Replace the incorrect trace call with the proper read-specific trace
+event to accurately reflect the hardware behavior.
+
+Fixes: 0791bc02b8fb ("m68k: add a system controller")
+Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
+Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
+Message-ID: <20260111184915.1363318-1-visitorckw@gmail.com>
+Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
+(cherry picked from commit 8608ed356ef90815cc5bcf04fcdbde987fd24bca)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ hw/misc/virt_ctrl.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/hw/misc/virt_ctrl.c b/hw/misc/virt_ctrl.c
+index 9f16093ca2..7dc2fe4f94 100644
+--- a/hw/misc/virt_ctrl.c
++++ b/hw/misc/virt_ctrl.c
+@@ -43,7 +43,7 @@ static uint64_t virt_ctrl_read(void *opaque, hwaddr addr, unsigned size)
+ break;
+ }
+
+- trace_virt_ctrl_write(s, addr, size, value);
++ trace_virt_ctrl_read(s, addr, size, value);
+
+ return value;
+ }
diff --git a/debian/patches/extra/0025-target-i386-emulate-x86_decode-Actually-use-stream-i.patch b/debian/patches/extra/0025-target-i386-emulate-x86_decode-Actually-use-stream-i.patch
new file mode 100644
index 0000000..f66e6a2
--- /dev/null
+++ b/debian/patches/extra/0025-target-i386-emulate-x86_decode-Actually-use-stream-i.patch
@@ -0,0 +1,52 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Bernhard Beschow <shentey@gmail.com>
+Date: Tue, 24 Feb 2026 00:39:25 +0100
+Subject: [PATCH] target/i386/emulate/x86_decode: Actually use stream in
+ decode_instruction_stream()
+
+Compared to decode_instruction(), decode_instruction_stream() has an additional
+stream parameter which avoids some guest memory accesses during instruction
+decoding. Both functions defer the actual work to decode_opcode() which would
+set the stream pointer to zero such that decode_instruction_stream() essentially
+behaved like decode_instruction(). Given that all callers of
+decode_instruction_stream() properly zero-initialize the decode parameter, the
+memset() call can be moved into decode_instruction() which is the only other
+user of decode_opcode(). This preserves the non-zero stream pointer which
+avoids extra guest memory accesses.
+
+Fixes: 1e25327b244a ("target/i386/emulate: Allow instruction decoding from stream")
+cc: qemu-stable
+Signed-off-by: Bernhard Beschow <shentey@gmail.com>
+Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr>
+Reviewed-by: Wei Liu (Microsoft) <wei.liu@kernel.org>
+Tested-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
+Link: https://lore.kernel.org/r/20260223233950.96076-4-mohamed@unpredictable.fr
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+(cherry picked from commit 1b93832f55927b1b76a6587ca75a5a35676188de)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ target/i386/emulate/x86_decode.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/target/i386/emulate/x86_decode.c b/target/i386/emulate/x86_decode.c
+index d037ed1142..a1122d2b27 100644
+--- a/target/i386/emulate/x86_decode.c
++++ b/target/i386/emulate/x86_decode.c
+@@ -2088,8 +2088,6 @@ static void decode_opcodes(CPUX86State *env, struct x86_decode *decode)
+
+ static uint32_t decode_opcode(CPUX86State *env, struct x86_decode *decode)
+ {
+- memset(decode, 0, sizeof(*decode));
+-
+ decode_prefix(env, decode);
+ set_addressing_size(env, decode);
+ set_operand_size(env, decode);
+@@ -2101,6 +2099,8 @@ static uint32_t decode_opcode(CPUX86State *env, struct x86_decode *decode)
+
+ uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode)
+ {
++ memset(decode, 0, sizeof(*decode));
++
+ return decode_opcode(env, decode);
+ }
+
diff --git a/debian/patches/extra/0026-io-separate-freeing-of-tasks-from-marking-them-as-co.patch b/debian/patches/extra/0026-io-separate-freeing-of-tasks-from-marking-them-as-co.patch
new file mode 100644
index 0000000..50ab81c
--- /dev/null
+++ b/debian/patches/extra/0026-io-separate-freeing-of-tasks-from-marking-them-as-co.patch
@@ -0,0 +1,258 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com>
+Date: Tue, 6 Jan 2026 16:08:49 +0000
+Subject: [PATCH] io: separate freeing of tasks from marking them as complete
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The original design of QIOTask was intended to simplify lifecycle
+management by automatically freeing it when the task was marked as
+complete. This overlooked the fact that when a QIOTask is used in
+combination with a GSource, there may be times when the source
+callback is never invoked. This is typically when a GSource is
+released before any I/O event arrives. In such cases it is not
+desirable to mark a QIOTask as complete, but it still needs to be
+freed. To satisfy this, the task must be released manually.
+
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
+(cherry picked from commit 163cd0ae1182e67509b271f244a73dfd938337b9)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ include/io/task.h | 29 +++++++++++++++++++++--------
+ io/channel-tls.c | 4 ++++
+ io/channel-websock.c | 3 +++
+ io/task.c | 8 ++++++--
+ tests/unit/test-io-task.c | 26 ++++++++++++++++++++++++++
+ 5 files changed, 60 insertions(+), 10 deletions(-)
+
+diff --git a/include/io/task.h b/include/io/task.h
+index 0b5342ee84..98847f5994 100644
+--- a/include/io/task.h
++++ b/include/io/task.h
+@@ -96,7 +96,7 @@ typedef void (*QIOTaskWorker)(QIOTask *task,
+ * 1000,
+ * myobject_operation_timer,
+ * task,
+- * NULL);
++ * qio_task_free);
+ * }
+ * </programlisting>
+ * </example>
+@@ -138,9 +138,8 @@ typedef void (*QIOTaskWorker)(QIOTask *task,
+ * the callback func 'myobject_operation_notify' shown
+ * earlier to deal with the results.
+ *
+- * Once this function returns false, object_unref will be called
+- * automatically on the task causing it to be released and the
+- * ref on QMyObject dropped too.
++ * Once this function returns FALSE, the task will be freed,
++ * causing it release the ref on QMyObject too.
+ *
+ * The QIOTask module can also be used to perform operations
+ * in a background thread context, while still reporting the
+@@ -208,8 +207,8 @@ typedef void (*QIOTaskWorker)(QIOTask *task,
+ * 'err' attribute in the task object to determine if
+ * the operation was successful or not.
+ *
+- * The returned task will be released when qio_task_complete()
+- * is invoked.
++ * The returned task must be released by calling
++ * qio_task_free() when no longer required.
+ *
+ * Returns: the task struct
+ */
+@@ -218,6 +217,19 @@ QIOTask *qio_task_new(Object *source,
+ gpointer opaque,
+ GDestroyNotify destroy);
+
++/**
++ * qio_task_free:
++ * task: the task object to free
++ *
++ * Free the resources associated with the task. Typically
++ * the qio_task_complete() method will be called immediately
++ * before this to trigger the task callback, however, it is
++ * permissible to free the task in the case of cancellation.
++ * The destroy callback will be used to release the opaque
++ * data provided to qio_task_new().
++ */
++void qio_task_free(QIOTask *task);
++
+ /**
+ * qio_task_run_in_thread:
+ * @task: the task struct
+@@ -268,8 +280,9 @@ void qio_task_wait_thread(QIOTask *task);
+ * qio_task_complete:
+ * @task: the task struct
+ *
+- * Invoke the completion callback for @task and
+- * then free its memory.
++ * Invoke the completion callback for @task. This should typically
++ * only be invoked once on a task, and then qio_task_free() used
++ * to free it.
+ */
+ void qio_task_complete(QIOTask *task);
+
+diff --git a/io/channel-tls.c b/io/channel-tls.c
+index b0cec27cb9..07274c12df 100644
+--- a/io/channel-tls.c
++++ b/io/channel-tls.c
+@@ -170,6 +170,7 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
+ trace_qio_channel_tls_handshake_fail(ioc);
+ qio_task_set_error(task, err);
+ qio_task_complete(task);
++ qio_task_free(task);
+ return;
+ }
+
+@@ -183,6 +184,7 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
+ trace_qio_channel_tls_credentials_allow(ioc);
+ }
+ qio_task_complete(task);
++ qio_task_free(task);
+ } else {
+ GIOCondition condition;
+ QIOChannelTLSData *data = g_new0(typeof(*data), 1);
+@@ -270,11 +272,13 @@ static void qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task,
+ trace_qio_channel_tls_bye_fail(ioc);
+ qio_task_set_error(task, err);
+ qio_task_complete(task);
++ qio_task_free(task);
+ return;
+ }
+
+ if (status == QCRYPTO_TLS_BYE_COMPLETE) {
+ qio_task_complete(task);
++ qio_task_free(task);
+ return;
+ }
+
+diff --git a/io/channel-websock.c b/io/channel-websock.c
+index cb4dafdebb..b4f96a0af4 100644
+--- a/io/channel-websock.c
++++ b/io/channel-websock.c
+@@ -545,6 +545,7 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
+ trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
+ qio_task_set_error(task, err);
+ qio_task_complete(task);
++ qio_task_free(task);
+ wioc->hs_io_tag = 0;
+ return FALSE;
+ }
+@@ -561,6 +562,7 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
+ trace_qio_channel_websock_handshake_complete(ioc);
+ qio_task_complete(task);
+ }
++ qio_task_free(task);
+ wioc->hs_io_tag = 0;
+ return FALSE;
+ }
+@@ -588,6 +590,7 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
+ trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
+ qio_task_set_error(task, err);
+ qio_task_complete(task);
++ qio_task_free(task);
+ wioc->hs_io_tag = 0;
+ return FALSE;
+ }
+diff --git a/io/task.c b/io/task.c
+index 451f26f8b4..331febd4e1 100644
+--- a/io/task.c
++++ b/io/task.c
+@@ -70,8 +70,12 @@ QIOTask *qio_task_new(Object *source,
+ return task;
+ }
+
+-static void qio_task_free(QIOTask *task)
++void qio_task_free(QIOTask *task)
+ {
++ if (!task) {
++ return;
++ }
++
+ qemu_mutex_lock(&task->thread_lock);
+ if (task->thread) {
+ if (task->thread->destroy) {
+@@ -110,6 +114,7 @@ static gboolean qio_task_thread_result(gpointer opaque)
+
+ trace_qio_task_thread_result(task);
+ qio_task_complete(task);
++ qio_task_free(task);
+
+ return FALSE;
+ }
+@@ -196,7 +201,6 @@ void qio_task_complete(QIOTask *task)
+ {
+ task->func(task, task->opaque);
+ trace_qio_task_complete(task);
+- qio_task_free(task);
+ }
+
+
+diff --git a/tests/unit/test-io-task.c b/tests/unit/test-io-task.c
+index 115dba8970..b1c8ecb7ab 100644
+--- a/tests/unit/test-io-task.c
++++ b/tests/unit/test-io-task.c
+@@ -73,6 +73,7 @@ static void test_task_complete(void)
+ src = qio_task_get_source(task);
+
+ qio_task_complete(task);
++ qio_task_free(task);
+
+ g_assert(obj == src);
+
+@@ -84,6 +85,28 @@ static void test_task_complete(void)
+ }
+
+
++static void test_task_cancel(void)
++{
++ QIOTask *task;
++ Object *obj = object_new(TYPE_DUMMY);
++ Object *src;
++ struct TestTaskData data = { NULL, NULL, false };
++
++ task = qio_task_new(obj, task_callback, &data, NULL);
++ src = qio_task_get_source(task);
++
++ qio_task_free(task);
++
++ g_assert(obj == src);
++
++ object_unref(obj);
++
++ g_assert(data.source == NULL);
++ g_assert(data.err == NULL);
++ g_assert(data.freed == false);
++}
++
++
+ static void task_data_free(gpointer opaque)
+ {
+ struct TestTaskData *data = opaque;
+@@ -101,6 +124,7 @@ static void test_task_data_free(void)
+ task = qio_task_new(obj, task_callback, &data, task_data_free);
+
+ qio_task_complete(task);
++ qio_task_free(task);
+
+ object_unref(obj);
+
+@@ -123,6 +147,7 @@ static void test_task_failure(void)
+
+ qio_task_set_error(task, err);
+ qio_task_complete(task);
++ qio_task_free(task);
+
+ object_unref(obj);
+
+@@ -260,6 +285,7 @@ int main(int argc, char **argv)
+ module_call_init(MODULE_INIT_QOM);
+ type_register_static(&dummy_info);
+ g_test_add_func("/crypto/task/complete", test_task_complete);
++ g_test_add_func("/crypto/task/cancel", test_task_cancel);
+ g_test_add_func("/crypto/task/datafree", test_task_data_free);
+ g_test_add_func("/crypto/task/failure", test_task_failure);
+ g_test_add_func("/crypto/task/thread_complete", test_task_thread_complete);
diff --git a/debian/patches/extra/0027-io-fix-cleanup-for-TLS-I-O-source-data-on-cancellati.patch b/debian/patches/extra/0027-io-fix-cleanup-for-TLS-I-O-source-data-on-cancellati.patch
new file mode 100644
index 0000000..9627d84
--- /dev/null
+++ b/debian/patches/extra/0027-io-fix-cleanup-for-TLS-I-O-source-data-on-cancellati.patch
@@ -0,0 +1,176 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com>
+Date: Tue, 6 Jan 2026 13:45:10 +0000
+Subject: [PATCH] io: fix cleanup for TLS I/O source data on cancellation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The TLS code will create a GSource for tracking completion of the
+handshake process, passing a QIOChannelTLSData struct that contains
+various data items. The data struct is freed by the callback when
+it completes, which means when a source is cancelled, nothing is
+free'ing the data struct or its contents.
+
+Switch to provide a data free callback to the GSource, which ensures
+the QIOChannelTLSData struct is always freed even when the main event
+callback never fires.
+
+Fixes: https://gitlab.com/qemu-project/qemu/-/issues/3114
+Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
+(cherry picked from commit d39d0f3acdd7c1bb275db7e97b511f98254ecd9f)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ io/channel-tls.c | 68 ++++++++++++++++++++++++++++++------------------
+ 1 file changed, 43 insertions(+), 25 deletions(-)
+
+diff --git a/io/channel-tls.c b/io/channel-tls.c
+index 07274c12df..940fc3c6d1 100644
+--- a/io/channel-tls.c
++++ b/io/channel-tls.c
+@@ -153,13 +153,32 @@ struct QIOChannelTLSData {
+ };
+ typedef struct QIOChannelTLSData QIOChannelTLSData;
+
++static void qio_channel_tls_io_data_free(gpointer user_data)
++{
++ QIOChannelTLSData *data = user_data;
++ /*
++ * Usually 'task' will be NULL since the GSource
++ * callback will either complete the task or pass
++ * it on to a new GSource. We'll see a non-NULL
++ * task here only if the GSource was released before
++ * its callback triggers
++ */
++ if (data->task) {
++ qio_task_free(data->task);
++ }
++ if (data->context) {
++ g_main_context_unref(data->context);
++ }
++ g_free(data);
++}
++
+ static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer user_data);
+
+-static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
+- QIOTask *task,
+- GMainContext *context)
++static gboolean qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
++ QIOTask *task,
++ GMainContext *context)
+ {
+ Error *err = NULL;
+ int status;
+@@ -170,8 +189,7 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
+ trace_qio_channel_tls_handshake_fail(ioc);
+ qio_task_set_error(task, err);
+ qio_task_complete(task);
+- qio_task_free(task);
+- return;
++ return TRUE;
+ }
+
+ if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
+@@ -184,7 +202,7 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
+ trace_qio_channel_tls_credentials_allow(ioc);
+ }
+ qio_task_complete(task);
+- qio_task_free(task);
++ return TRUE;
+ } else {
+ GIOCondition condition;
+ QIOChannelTLSData *data = g_new0(typeof(*data), 1);
+@@ -208,8 +226,9 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
+ condition,
+ qio_channel_tls_handshake_io,
+ data,
+- NULL,
++ qio_channel_tls_io_data_free,
+ context);
++ return FALSE;
+ }
+ }
+
+@@ -225,11 +244,9 @@ static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc,
+ qio_task_get_source(task));
+
+ tioc->hs_ioc_tag = 0;
+- g_free(data);
+- qio_channel_tls_handshake_task(tioc, task, context);
+-
+- if (context) {
+- g_main_context_unref(context);
++ if (!qio_channel_tls_handshake_task(tioc, task, context)) {
++ /* task is kept by new GSource so must not be released yet */
++ data->task = NULL;
+ }
+
+ return FALSE;
+@@ -252,14 +269,16 @@ void qio_channel_tls_handshake(QIOChannelTLS *ioc,
+ func, opaque, destroy);
+
+ trace_qio_channel_tls_handshake_start(ioc);
+- qio_channel_tls_handshake_task(ioc, task, context);
++ if (qio_channel_tls_handshake_task(ioc, task, context)) {
++ qio_task_free(task);
++ }
+ }
+
+ static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition,
+ gpointer user_data);
+
+-static void qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task,
+- GMainContext *context)
++static gboolean qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task,
++ GMainContext *context)
+ {
+ GIOCondition condition;
+ QIOChannelTLSData *data;
+@@ -272,14 +291,12 @@ static void qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task,
+ trace_qio_channel_tls_bye_fail(ioc);
+ qio_task_set_error(task, err);
+ qio_task_complete(task);
+- qio_task_free(task);
+- return;
++ return TRUE;
+ }
+
+ if (status == QCRYPTO_TLS_BYE_COMPLETE) {
+ qio_task_complete(task);
+- qio_task_free(task);
+- return;
++ return TRUE;
+ }
+
+ data = g_new0(typeof(*data), 1);
+@@ -299,7 +316,10 @@ static void qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task,
+ trace_qio_channel_tls_bye_pending(ioc, status);
+ ioc->bye_ioc_tag = qio_channel_add_watch_full(ioc->master, condition,
+ qio_channel_tls_bye_io,
+- data, NULL, context);
++ data,
++ qio_channel_tls_io_data_free,
++ context);
++ return FALSE;
+ }
+
+
+@@ -312,11 +332,9 @@ static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition,
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(qio_task_get_source(task));
+
+ tioc->bye_ioc_tag = 0;
+- g_free(data);
+- qio_channel_tls_bye_task(tioc, task, context);
+-
+- if (context) {
+- g_main_context_unref(context);
++ if (!qio_channel_tls_bye_task(tioc, task, context)) {
++ /* task is kept by new GSource so must not be released yet */
++ data->task = NULL;
+ }
+
+ return FALSE;
diff --git a/debian/patches/extra/0028-io-fix-cleanup-for-websock-I-O-source-data-on-cancel.patch b/debian/patches/extra/0028-io-fix-cleanup-for-websock-I-O-source-data-on-cancel.patch
new file mode 100644
index 0000000..2604d55
--- /dev/null
+++ b/debian/patches/extra/0028-io-fix-cleanup-for-websock-I-O-source-data-on-cancel.patch
@@ -0,0 +1,143 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com>
+Date: Tue, 6 Jan 2026 13:45:10 +0000
+Subject: [PATCH] io: fix cleanup for websock I/O source data on cancellation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The websock code will create a GSource for tracking completion of the
+handshake process, passing a QIOTask which is freed by the callback
+when it completes, which means when a source is cancelled, nothing is
+free'ing the task.
+
+Switch to provide a data free callback to the GSource, which ensures
+the QIOTask is always freed even when the main event callback never
+fires.
+
+Fixes: https://gitlab.com/qemu-project/qemu/-/issues/3114
+Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
+(cherry picked from commit 9545c059f77e3f814fcbaba83203572ea655c50e)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ io/channel-websock.c | 49 +++++++++++++++++++++++++++++++-------------
+ 1 file changed, 35 insertions(+), 14 deletions(-)
+
+diff --git a/io/channel-websock.c b/io/channel-websock.c
+index b4f96a0af4..bb10bc4f7f 100644
+--- a/io/channel-websock.c
++++ b/io/channel-websock.c
+@@ -526,11 +526,32 @@ static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,
+ return 1;
+ }
+
++typedef struct QIOChannelWebsockData {
++ QIOTask *task;
++} QIOChannelWebsockData;
++
++static void qio_channel_websock_data_free(gpointer user_data)
++{
++ QIOChannelWebsockData *data = user_data;
++ /*
++ * Usually 'task' will be NULL since the GSource
++ * callback will either complete the task or pass
++ * it on to a new GSource. We'll see a non-NULL
++ * task here only if the GSource was released before
++ * its callback triggers
++ */
++ if (data->task) {
++ qio_task_free(data->task);
++ }
++ g_free(data);
++}
++
+ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer user_data)
+ {
+- QIOTask *task = user_data;
++ QIOChannelWebsockData *data = user_data;
++ QIOTask *task = data->task;
+ QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(
+ qio_task_get_source(task));
+ Error *err = NULL;
+@@ -545,7 +566,6 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
+ trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
+ qio_task_set_error(task, err);
+ qio_task_complete(task);
+- qio_task_free(task);
+ wioc->hs_io_tag = 0;
+ return FALSE;
+ }
+@@ -562,7 +582,6 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
+ trace_qio_channel_websock_handshake_complete(ioc);
+ qio_task_complete(task);
+ }
+- qio_task_free(task);
+ wioc->hs_io_tag = 0;
+ return FALSE;
+ }
+@@ -574,7 +593,8 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer user_data)
+ {
+- QIOTask *task = user_data;
++ QIOChannelWebsockData *data = user_data, *newdata = NULL;
++ QIOTask *task = data->task;
+ QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(
+ qio_task_get_source(task));
+ Error *err = NULL;
+@@ -590,7 +610,6 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
+ trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
+ qio_task_set_error(task, err);
+ qio_task_complete(task);
+- qio_task_free(task);
+ wioc->hs_io_tag = 0;
+ return FALSE;
+ }
+@@ -603,12 +622,14 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
+ error_propagate(&wioc->io_err, err);
+
+ trace_qio_channel_websock_handshake_reply(ioc);
++ newdata = g_new0(QIOChannelWebsockData, 1);
++ newdata->task = g_steal_pointer(&data->task);
+ wioc->hs_io_tag = qio_channel_add_watch(
+ wioc->master,
+ G_IO_OUT,
+ qio_channel_websock_handshake_send,
+- task,
+- NULL);
++ newdata,
++ qio_channel_websock_data_free);
+ return FALSE;
+ }
+
+@@ -904,12 +925,12 @@ void qio_channel_websock_handshake(QIOChannelWebsock *ioc,
+ gpointer opaque,
+ GDestroyNotify destroy)
+ {
+- QIOTask *task;
++ QIOChannelWebsockData *data = g_new0(QIOChannelWebsockData, 1);
+
+- task = qio_task_new(OBJECT(ioc),
+- func,
+- opaque,
+- destroy);
++ data->task = qio_task_new(OBJECT(ioc),
++ func,
++ opaque,
++ destroy);
+
+ trace_qio_channel_websock_handshake_start(ioc);
+ trace_qio_channel_websock_handshake_pending(ioc, G_IO_IN);
+@@ -917,8 +938,8 @@ void qio_channel_websock_handshake(QIOChannelWebsock *ioc,
+ ioc->master,
+ G_IO_IN,
+ qio_channel_websock_handshake_io,
+- task,
+- NULL);
++ data,
++ qio_channel_websock_data_free);
+ }
+
+
diff --git a/debian/patches/extra/0029-hw-Make-qdev_get_printable_name-consistently-return-.patch b/debian/patches/extra/0029-hw-Make-qdev_get_printable_name-consistently-return-.patch
new file mode 100644
index 0000000..50438b0
--- /dev/null
+++ b/debian/patches/extra/0029-hw-Make-qdev_get_printable_name-consistently-return-.patch
@@ -0,0 +1,142 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Peter Maydell <peter.maydell@linaro.org>
+Date: Sat, 7 Mar 2026 15:50:46 +0000
+Subject: [PATCH] hw: Make qdev_get_printable_name() consistently return
+ freeable string
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The current implementation of qdev_get_printable_name() sometimes
+returns a string that must not be freed (vdev->id or the fixed
+fallback string "<unknown device>" and sometimes returns a string
+that must be freed (the return value of qdev_get_dev_path()). This
+forces callers to leak the string in the "must be freed" case.
+
+Make the function consistent that it always returns a string that
+the caller must free, and make the three callsites free it.
+
+This fixes leaks like this that show up when running "make check"
+with the address sanitizer enabled:
+
+Direct leak of 13 byte(s) in 1 object(s) allocated from:
+ #0 0x5561de21f293 in malloc (/home/pm215/qemu/build/san/qemu-system-i386+0x1a2d293) (BuildId: 6d6fad7130fd5c8dbbc03401df554f68b8034936)
+ #1 0x767ad7a82ac9 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62ac9) (BuildId: 116e142b9b52c8a4dfd403e759e71ab8f95d8bb3)
+ #2 0x5561deaf34f2 in pcibus_get_dev_path /home/pm215/qemu/build/san/../../hw/pci/pci.c:2792:12
+ #3 0x5561df9d8830 in qdev_get_printable_name /home/pm215/qemu/build/san/../../hw/core/qdev.c:431:24
+ #4 0x5561deebdca2 in virtio_init_region_cache /home/pm215/qemu/build/san/../../hw/virtio/virtio.c:298:17
+ #5 0x5561df05f842 in memory_region_write_accessor /home/pm215/qemu/build/san/../../system/memory.c:491:5
+ #6 0x5561df05ed1b in access_with_adjusted_size /home/pm215/qemu/build/san/../../system/memory.c:567:18
+ #7 0x5561df05e3fa in memory_region_dispatch_write /home/pm215/qemu/build/san/../../system/memory.c
+ #8 0x5561df0aa805 in address_space_stm_internal /home/pm215/qemu/build/san/../../system/memory_ldst.c.inc:85:13
+ #9 0x5561df0bcad3 in qtest_process_command /home/pm215/qemu/build/san/../../system/qtest.c:480:13
+
+Cc: qemu-stable@nongnu.org
+Fixes: e209d4d7a31b9 ("virtio: improve virtqueue mapping error messages")
+Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
+Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
+Message-ID: <20260307155046.3940197-3-peter.maydell@linaro.org>
+Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
+(cherry picked from commit 1e3e1d51e20e8b38efa089bf54b5ee2cbbcca221)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ hw/core/qdev.c | 4 ++--
+ hw/virtio/virtio.c | 12 +++++++++---
+ include/hw/qdev-core.h | 16 ++++++++++++++++
+ 3 files changed, 27 insertions(+), 5 deletions(-)
+
+diff --git a/hw/core/qdev.c b/hw/core/qdev.c
+index fab42a7270..ce0ee9fcef 100644
+--- a/hw/core/qdev.c
++++ b/hw/core/qdev.c
+@@ -420,7 +420,7 @@ const char *qdev_get_printable_name(DeviceState *vdev)
+ * names.
+ */
+ if (vdev->id) {
+- return vdev->id;
++ return g_strdup(vdev->id);
+ }
+ /*
+ * Fall back to the canonical QOM device path (eg. ID for PCI
+@@ -437,7 +437,7 @@ const char *qdev_get_printable_name(DeviceState *vdev)
+ * Final fallback: if all else fails, return a placeholder string.
+ * This ensures the error message always contains a valid string.
+ */
+- return "<unknown device>";
++ return g_strdup("<unknown device>");
+ }
+
+ void qdev_add_unplug_blocker(DeviceState *dev, Error *reason)
+diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
+index 683026adc4..deb7c6695e 100644
+--- a/hw/virtio/virtio.c
++++ b/hw/virtio/virtio.c
+@@ -258,10 +258,12 @@ void virtio_init_region_cache(VirtIODevice *vdev, int n)
+ len = address_space_cache_init(&new->desc, vdev->dma_as,
+ addr, size, packed);
+ if (len < size) {
++ g_autofree const char *devname = qdev_get_printable_name(DEVICE(vdev));
++
+ virtio_error(vdev,
+ "Failed to map descriptor ring for device %s: "
+ "invalid guest physical address or corrupted queue setup",
+- qdev_get_printable_name(DEVICE(vdev)));
++ devname);
+ goto err_desc;
+ }
+
+@@ -269,10 +271,12 @@ void virtio_init_region_cache(VirtIODevice *vdev, int n)
+ len = address_space_cache_init(&new->used, vdev->dma_as,
+ vq->vring.used, size, true);
+ if (len < size) {
++ g_autofree const char *devname = qdev_get_printable_name(DEVICE(vdev));
++
+ virtio_error(vdev,
+ "Failed to map used ring for device %s: "
+ "possible guest misconfiguration or insufficient memory",
+- qdev_get_printable_name(DEVICE(vdev)));
++ devname);
+ goto err_used;
+ }
+
+@@ -280,10 +284,12 @@ void virtio_init_region_cache(VirtIODevice *vdev, int n)
+ len = address_space_cache_init(&new->avail, vdev->dma_as,
+ vq->vring.avail, size, false);
+ if (len < size) {
++ g_autofree const char *devname = qdev_get_printable_name(DEVICE(vdev));
++
+ virtio_error(vdev,
+ "Failed to map avalaible ring for device %s: "
+ "possible queue misconfiguration or overlapping memory region",
+- qdev_get_printable_name(DEVICE(vdev)));
++ devname);
+ goto err_avail;
+ }
+
+diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
+index 2caa0cbd26..774329bba9 100644
+--- a/include/hw/qdev-core.h
++++ b/include/hw/qdev-core.h
+@@ -1065,6 +1065,22 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp);
+ extern bool qdev_hot_removed;
+
+ char *qdev_get_dev_path(DeviceState *dev);
++
++/**
++ * qdev_get_printable_name: Return human readable name for device
++ * @dev: Device to get name of
++ *
++ * Returns: A newly allocated string containing some human
++ * readable name for the device, suitable for printing in
++ * user-facing error messages. The function will never return NULL,
++ * so the name can be used without further checking or fallbacks.
++ *
++ * If the device has an explicitly set ID (e.g. by the user on the
++ * command line via "-device thisdev,id=myid") this is preferred.
++ * Otherwise we try the canonical QOM device path (which will be
++ * the PCI ID for PCI devices, for example). If all else fails
++ * we will return the placeholder "<unknown device">.
++ */
+ const char *qdev_get_printable_name(DeviceState *dev);
+
+ void qbus_set_hotplug_handler(BusState *bus, Object *handler);
diff --git a/debian/patches/extra/0030-fuse-Copy-write-buffer-content-before-polling.patch b/debian/patches/extra/0030-fuse-Copy-write-buffer-content-before-polling.patch
new file mode 100644
index 0000000..9b760f7
--- /dev/null
+++ b/debian/patches/extra/0030-fuse-Copy-write-buffer-content-before-polling.patch
@@ -0,0 +1,114 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Hanna Czenczek <hreitz@redhat.com>
+Date: Mon, 9 Mar 2026 16:08:32 +0100
+Subject: [PATCH] fuse: Copy write buffer content before polling
+
+aio_poll() in I/O functions can lead to nested read_from_fuse_export()
+calls, overwriting the request buffer's content. The only function
+affected by this is fuse_write(), which therefore must use a bounce
+buffer or corruption may occur.
+
+Note that in addition we do not know whether libfuse-internal structures
+can cope with this nesting, and even if we did, we probably cannot rely
+on it in the future. This is the main reason why we want to remove
+libfuse from the I/O path.
+
+I do not have a good reproducer for this other than:
+
+$ dd if=/dev/urandom of=image bs=1M count=4096
+$ dd if=/dev/zero of=copy bs=1M count=4096
+$ touch fuse-export
+$ qemu-storage-daemon \
+ --blockdev file,node-name=file,filename=copy \
+ --export \
+ fuse,id=exp,node-name=file,mountpoint=fuse-export,writable=true \
+ &
+
+Other shell:
+$ qemu-img convert -p -n -f raw -O raw -t none image fuse-export
+$ killall -SIGINT qemu-storage-daemon
+$ qemu-img compare image copy
+Content mismatch at offset 0!
+
+(The -t none in qemu-img convert is important.)
+
+I tried reproducing this with throttle and small aio_write requests from
+another qemu-io instance, but for some reason all requests are perfectly
+serialized then.
+
+I think in theory we should get parallel writes only if we set
+fi->parallel_direct_writes in fuse_open(). In fact, I can confirm that
+if we do that, that throttle-based reproducer works (i.e. does get
+parallel (nested) write requests). I have no idea why we still get
+parallel requests with qemu-img convert anyway.
+
+Also, a later patch in this series will set fi->parallel_direct_writes
+and note that it makes basically no difference when running fio on the
+current libfuse-based version of our code. It does make a difference
+without libfuse. So something quite fishy is going on.
+
+I will try to investigate further what the root cause is, but I think
+for now let's assume that calling blk_pwrite() can invalidate the buffer
+contents through nested polling.
+
+Cc: qemu-stable@nongnu.org
+Reviewed-by: Kevin Wolf <kwolf@redhat.com>
+Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
+Message-ID: <20260309150856.26800-2-hreitz@redhat.com>
+Reviewed-by: Kevin Wolf <kwolf@redhat.com>
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+(cherry picked from commit a3fcbca0ef643a8aecf354bdeb08b1d81e5b33e7)
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ block/export/fuse.c | 17 ++++++++++++++++-
+ 1 file changed, 16 insertions(+), 1 deletion(-)
+
+diff --git a/block/export/fuse.c b/block/export/fuse.c
+index 465cc9891d..aec4d8736d 100644
+--- a/block/export/fuse.c
++++ b/block/export/fuse.c
+@@ -301,6 +301,12 @@ static void read_from_fuse_export(void *opaque)
+ goto out;
+ }
+
++ /*
++ * Note that aio_poll() in any request-processing function can lead to a
++ * nested read_from_fuse_export() call, which will overwrite the contents of
++ * exp->fuse_buf. Anything that takes a buffer needs to take care that the
++ * content is copied before potentially polling via aio_poll().
++ */
+ fuse_session_process_buf(exp->fuse_session, &exp->fuse_buf);
+
+ out:
+@@ -624,6 +630,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inode, const char *buf,
+ size_t size, off_t offset, struct fuse_file_info *fi)
+ {
+ FuseExport *exp = fuse_req_userdata(req);
++ QEMU_AUTO_VFREE void *copied = NULL;
+ int64_t length;
+ int ret;
+
+@@ -638,6 +645,14 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inode, const char *buf,
+ return;
+ }
+
++ /*
++ * Heed the note on read_from_fuse_export(): If we call aio_poll() (which
++ * any blk_*() I/O function may do), read_from_fuse_export() may be nested,
++ * overwriting the request buffer content. Therefore, we must copy it here.
++ */
++ copied = blk_blockalign(exp->common.blk, size);
++ memcpy(copied, buf, size);
++
+ /**
+ * Clients will expect short writes at EOF, so we have to limit
+ * offset+size to the image length.
+@@ -660,7 +675,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inode, const char *buf,
+ }
+ }
+
+- ret = blk_pwrite(exp->common.blk, offset, size, buf, 0);
++ ret = blk_pwrite(exp->common.blk, offset, size, copied, 0);
+ if (ret >= 0) {
+ fuse_reply_write(req, size);
+ } else {
diff --git a/debian/patches/pve/0003-PVE-Config-set-the-CPU-model-to-kvm64-32-instead-of-.patch b/debian/patches/pve/0003-PVE-Config-set-the-CPU-model-to-kvm64-32-instead-of-.patch
index b4e1b2f..09e94e8 100644
--- a/debian/patches/pve/0003-PVE-Config-set-the-CPU-model-to-kvm64-32-instead-of-.patch
+++ b/debian/patches/pve/0003-PVE-Config-set-the-CPU-model-to-kvm64-32-instead-of-.patch
@@ -10,10 +10,10 @@ Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
-index cee1f692a1..6f49be3796 100644
+index ee3ed5ae6d..9d69d76f69 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
-@@ -2641,9 +2641,9 @@ uint64_t cpu_get_tsc(CPUX86State *env);
+@@ -2642,9 +2642,9 @@ uint64_t cpu_get_tsc(CPUX86State *env);
#define CPU_RESOLVING_TYPE TYPE_X86_CPU
#ifdef TARGET_X86_64
diff --git a/debian/patches/pve/0019-PVE-block-add-the-zeroinit-block-driver-filter.patch b/debian/patches/pve/0019-PVE-block-add-the-zeroinit-block-driver-filter.patch
index b353177..e3f8ab8 100644
--- a/debian/patches/pve/0019-PVE-block-add-the-zeroinit-block-driver-filter.patch
+++ b/debian/patches/pve/0019-PVE-block-add-the-zeroinit-block-driver-filter.patch
@@ -247,7 +247,7 @@ index 0000000000..036edb17f5
+
+block_init(bdrv_zeroinit_init);
diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 64f2befdf5..19b60e3a96 100644
+index d4a5765dc4..476b9c011e 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3376,7 +3376,7 @@
diff --git a/debian/patches/pve/0022-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch b/debian/patches/pve/0022-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch
index 2f70373..2cae103 100644
--- a/debian/patches/pve/0022-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch
+++ b/debian/patches/pve/0022-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch
@@ -119,7 +119,7 @@ index 41ac3f222f..612942a222 100644
};
return raw_co_create(&options, errp);
diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 19b60e3a96..029b8f2b51 100644
+index 476b9c011e..bddc97d494 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -5153,6 +5153,10 @@
diff --git a/debian/patches/pve/0030-PVE-Backup-Proxmox-backup-patches-for-QEMU.patch b/debian/patches/pve/0030-PVE-Backup-Proxmox-backup-patches-for-QEMU.patch
index a56fc83..0f97bdd 100644
--- a/debian/patches/pve/0030-PVE-Backup-Proxmox-backup-patches-for-QEMU.patch
+++ b/debian/patches/pve/0030-PVE-Backup-Proxmox-backup-patches-for-QEMU.patch
@@ -1688,7 +1688,7 @@ index 0000000000..177fb851b4
+ return ret;
+}
diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 029b8f2b51..75406f4215 100644
+index bddc97d494..693a123e94 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -947,6 +947,248 @@
diff --git a/debian/patches/pve/0032-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch b/debian/patches/pve/0032-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch
index 00fef7e..8995604 100644
--- a/debian/patches/pve/0032-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch
+++ b/debian/patches/pve/0032-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch
@@ -361,7 +361,7 @@ index 73f6e2e93b..b717cad2f9 100644
summary_info += {'libdaxctl support': libdaxctl}
summary_info += {'libcbor support': libcbor}
diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 75406f4215..f998aafc49 100644
+index 693a123e94..e0f44067e0 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3614,6 +3614,7 @@
diff --git a/debian/patches/pve/0034-PVE-Migrate-dirty-bitmap-state-via-savevm.patch b/debian/patches/pve/0034-PVE-Migrate-dirty-bitmap-state-via-savevm.patch
index 2c3c25f..09565fe 100644
--- a/debian/patches/pve/0034-PVE-Migrate-dirty-bitmap-state-via-savevm.patch
+++ b/debian/patches/pve/0034-PVE-Migrate-dirty-bitmap-state-via-savevm.patch
@@ -192,7 +192,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 f998aafc49..5b3bb3c19e 100644
+index e0f44067e0..f1679264bb 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1107,6 +1107,11 @@
diff --git a/debian/patches/pve/0038-block-add-alloc-track-driver.patch b/debian/patches/pve/0038-block-add-alloc-track-driver.patch
index de886e2..cb3c9ae 100644
--- a/debian/patches/pve/0038-block-add-alloc-track-driver.patch
+++ b/debian/patches/pve/0038-block-add-alloc-track-driver.patch
@@ -449,7 +449,7 @@ index d023753091..a777c8079c 100644
out:
diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 5b3bb3c19e..1d98f0a406 100644
+index f1679264bb..6d8c2df22b 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3606,7 +3606,8 @@
diff --git a/debian/patches/pve/0039-PVE-backup-add-fleecing-option.patch b/debian/patches/pve/0039-PVE-backup-add-fleecing-option.patch
index 50021c5..c9d4d39 100644
--- a/debian/patches/pve/0039-PVE-backup-add-fleecing-option.patch
+++ b/debian/patches/pve/0039-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 1d98f0a406..db0a5a1266 100644
+index 6d8c2df22b..ea73974495 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1049,6 +1049,10 @@
diff --git a/debian/patches/pve/0045-PVE-backup-implement-backup-access-setup-and-teardow.patch b/debian/patches/pve/0045-PVE-backup-implement-backup-access-setup-and-teardow.patch
index 8bdbf11..f4ec604 100644
--- a/debian/patches/pve/0045-PVE-backup-implement-backup-access-setup-and-teardow.patch
+++ b/debian/patches/pve/0045-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 db0a5a1266..94296c0bc9 100644
+index ea73974495..52b750dbb3 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1123,6 +1123,9 @@
diff --git a/debian/patches/pve/0047-savevm-async-reuse-migration-blocker-check-for-snaps.patch b/debian/patches/pve/0047-savevm-async-reuse-migration-blocker-check-for-snaps.patch
index 18db6fa..d0c7868 100644
--- a/debian/patches/pve/0047-savevm-async-reuse-migration-blocker-check-for-snaps.patch
+++ b/debian/patches/pve/0047-savevm-async-reuse-migration-blocker-check-for-snaps.patch
@@ -61,7 +61,7 @@ Message-ID: <20250618102531.57444-1-f.ebner@proxmox.com>
5 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/block/vmdk.c b/block/vmdk.c
-index 89e89cd10e..06df10a799 100644
+index cd8b4ec7c8..1f64ab1766 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -1404,9 +1404,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
diff --git a/debian/patches/series b/debian/patches/series
index 7c8e26c..8ed0c52 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -2,6 +2,32 @@ extra/0001-monitor-qmp-fix-race-with-clients-disconnecting-earl.patch
extra/0002-ide-avoid-potential-deadlock-when-draining-during-tr.patch
extra/0003-block-mirror-check-range-when-setting-zero-bitmap-fo.patch
extra/0004-block-io-fallback-to-bounce-buffer-if-BLKZEROOUT-is-.patch
+extra/0005-block-vmdk-fix-OOB-read-in-vmdk_read_extent.patch
+extra/0006-block-throttle-groups-fix-deadlock-with-iolimits-and.patch
+extra/0007-block-Never-drop-BLOCK_IO_ERROR-with-action-stop-for.patch
+extra/0008-mirror-Fix-missed-dirty-bitmap-writes-during-startup.patch
+extra/0009-virtio-gpu-virgl-Add-virtio-gpu-virgl-hostmem-region.patch
+extra/0010-virtio-gpu-Ensure-BHs-are-invoked-only-from-main-loo.patch
+extra/0011-hw-i2c-aspeed_i2c-Fix-out-of-bounds-read-in-I2C-MMIO.patch
+extra/0012-target-arm-Account-for-SME-in-aarch64_sve_narrow_vq-.patch
+extra/0013-target-arm-Fix-feature-check-in-DO_SVE2_RRX-DO_SVE2_.patch
+extra/0014-target-arm-tcg-Allow-SVE-RAX1-in-SME2p1-streaming-mo.patch
+extra/0015-target-arm-Don-t-let-sme-on-downgrade-SME.patch
+extra/0016-target-arm-set-the-correct-TI-bits-for-WFIT-traps.patch
+extra/0017-aio-posix-notify-main-loop-when-SQEs-are-queued.patch
+extra/0018-fdmon-io_uring-check-CQ-ring-directly-in-gsource_che.patch
+extra/0019-target-i386-add-compat-for-migrating-error-code.patch
+extra/0020-virtio-snd-remove-TODO-comments.patch
+extra/0021-virtio-snd-handle-5.14.6.2-for-PCM_INFO-properly.patch
+extra/0022-virtio-snd-fix-max_size-bounds-check-in-input-cb.patch
+extra/0023-virtio-snd-tighten-read-amount-in-in_cb.patch
+extra/0024-hw-misc-virt_ctrl-Fix-incorrect-trace-event-in-read-.patch
+extra/0025-target-i386-emulate-x86_decode-Actually-use-stream-i.patch
+extra/0026-io-separate-freeing-of-tasks-from-marking-them-as-co.patch
+extra/0027-io-fix-cleanup-for-TLS-I-O-source-data-on-cancellati.patch
+extra/0028-io-fix-cleanup-for-websock-I-O-source-data-on-cancel.patch
+extra/0029-hw-Make-qdev_get_printable_name-consistently-return-.patch
+extra/0030-fuse-Copy-write-buffer-content-before-polling.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
prev parent reply other threads:[~2026-03-12 12:13 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-12 11:44 [PATCH-SERIES qemu 0/2] " Fiona Ebner
2026-03-12 11:44 ` [PATCH qemu 1/2] update submodule and patches to " Fiona Ebner
2026-03-12 11:44 ` Fiona Ebner [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260312114417.82984-3-f.ebner@proxmox.com \
--to=f.ebner@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox