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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.