Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:qemu
User: [email protected]
Usertags: pu

[ Reason ]
There's a new upstream stable/bugfix release, with a number
of security and correctness fixes.  Security fixes include:
CVE-2026-17588, CVE-2026-93834 (#1149064), CVE-2026-12080,
CVE-2026-66899, CVE-2026-66900, CVE-2026-66020, CVE-2026-84788,
CVE-2026-81627 (#1148473), CVE-2026-77913, CVE-2026-16271.

[ Tests ]
As usual, this release passes upstream testsuite (the bits
which are relevant still, since more and more environments
used in the testing becomes unavailable), and a bunch of
my regular testing VMs, including windows, linux and freebsd.
No regressions are observed.

[ Risks ]
There's relatively small amount of changes this time, and most
of them are easily understandable and verifiable.  I don't
expect much risks from this update.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]
The debian/changelog is below, in the debdiff output.
There are two obvious changes in debian/ - the changelog
and a version bump in debian/rules.  The rest are upstream
changes, which might better be viewed as git commits at
https://salsa.debian.org/qemu-team/qemu/-/commits/upstream-10.0
between v10.0.13 (already in debian) and v10.0.14 tags.

Thanks,

/mjt

diff -Nru qemu-10.0.13+ds/debian/changelog qemu-10.0.14+ds/debian/changelog
--- qemu-10.0.13+ds/debian/changelog    2026-08-29 20:55:40.000000000 +0300
+++ qemu-10.0.14+ds/debian/changelog    2026-10-03 20:01:57.000000000 +0300
@@ -1,3 +1,94 @@
+qemu (1:10.0.14+ds-0+deb13u1) trixie; urgency=medium
+
+  * new upstream stable/bugfix release, including multiple security fixes:
+   - Update version for 10.0.14 release
+   - target/sh4: Replace TB_FLAG_GUSA_EXCLUSIVE with CF_STEP_ATOMIC
+   - accel/tcg: Set CF_NOIRQ during cpu_exec_step_atomic
+   - target/i386: Mark MOVNTI as not valid with prefixes 0x66, 0xF2, 0xF3
+   - target/i386: Update FPU tag word for FXCH
+   - target/i386: Update FPU tag word for FSTP
+   - target/i386: Update FPU tag word for FXTRACT's old ST(0)
+   - target/i386: Fix FXCH to unconditionally clear C1
+   - target/ppc: Stop vCPU thread before calling parent_unrealize
+   - tests/qtest/usb-hcd-xhci: test isoch endpoint type mismatch
+   - tests/qtest/usb-hcd-xhci: test isoch pacing with MFINDEX above 2^32
+   - hw/usb/hcd-xhci: don't assert on NAK when retrying an isoch transfer
+   - hw/usb/hcd-xhci: fix interval alignment after MFINDEX passes 2^32
+   - hw/usb/hcd-xhci: Set reentrancy guard in timer functions
+     Closes: CVE-2026-17588
+   - tcg/riscv64: Set vtype before whole-register vector loads
+   - accel/tcg: Fix TLB_MMIO check in tlb_plugin_lookup()
+   - accel/tcg: Use TLB_FORCE_SLOW not TLB_MMIO for system plugins
+   - hw/9pfs: mutate FID path from main thread only
+     Closes: #1149064, CVE-2026-93834
+   - s390x/pci: fix DMA slot leak on I/O TLB entry replacement
+   - linux-user/loongarch64: Detect vector stores in host_signal_write()
+   - linux-user: implement mlock2(2) syscall
+   - linux-user/riscv: honor zicntr=false for base counterCSRs
+   - hw/uefi: add missing uefi_str_is_valid check to uefi_vars_mm_lock_variable
+   - hw/riscv/virt.c: fix aclint soc/mtimer nodename
+   - hw/intc/bcm2835_ic: reject out-of-range FIQ source values
+   - qga: Change effective user/group ID in guest-ssh-* commands
+     Closes: CVE-2026-12080
+   - vhost-user-gpu: validate command buffer size in submit_3d
+   - ui/cursor: make the cursor refcount atomic
+   - hw/display/qxl: hold ssd.lock while replacing ssd.cursor
+   - hw/cxl: fix the CDAT DOE overlapping the Flex Bus DVSEC when sn= is set
+   - virtio-scsi: set dataplane_started to false upon failure
+   - virtio-balloon: fix free-page BH teardown on unrealize
+     Closes: CVE-2026-66899
+   - hw/virtio: reject inverted virtio-iommu IOVA ranges
+   - hw/net/virtio-net: strip trailing padding when caching RSC segment
+     Closes: CVE-2026-66900
+   - hw/net/virtio-net: check packet size before VLAN tag access
+     in receive_filter()
+   - qapi/misc: Fix missed query-iothreads items
+   - hw/cxl: Fix guest-triggerable QEMU exit on reserved interleave ways
+   - virtio-gpu: clear res->blob on mapping cleanup
+   - virtio-gpu: disable blob scanouts on mapping cleanup
+     Closes: CVE-2026-66020
+   - tests/unit: cover blocked IO during the websock handshake
+   - io/channel-websock: do not lose QIO_CHANNEL_ERR_BLOCK while reading
+   - tests/unit: add websock handshake test
+   - io/channel-websock: handle a blocked write during the handshake
+   - io/channel-websock: send an HTTP 400 when the greeting has no space
+   - io/channel-socket: do not treat a zero length write as an error
+     (The above 5 changes) Closes: CVE-2026-84788
+   - hw/sd: sdhci: Accept version 4 enable without UHS-I
+   - target/arm: Make Thumb T1 hint space UNDEF before v6T2
+   - target/arm: Make IT insn undef when not present
+   - target/arm: Make CBZ/CBNZ UNDEF before v6T2
+   - target/arm: Correct reset value of SCTLR for arm926, arm1026
+   - target/arm: fix TTA instruction S bit for IDAU-exempt addresses
+   - i386/vapic: unref MemoryRegion if vapic_map_rom_writable fails
+   - vapic: confine the VAPIC region to 0xc0000..0xe0000
+     Closes: #1148473, CVE-2026-81627
+   - scsi: hide MODE SELECT block size change behind a quirk
+   - scsi-disk: fix out-of-bound read in WRITE SAME
+   - hw/ide: report the default CHS translation in IDENTIFY DEVICE
+   - tests/qtest/ide-test: cover a CHS translation with zero sectors
+   - hw/ide: reject an unsupported CHS translation
+   - s390x/sclp: pv: only copy the original SCCB buffer
+   - tests/qtest: Add seed CSR zero extension test
+   - target/riscv: Fix seed CSR sign extension
+   - target/riscv: tt-ascalon: Enable Zkr extension
+   - dirty-bitmap: fix integer overflow in serialization coverage
+   - block/monitor: allow dropping a bitmap never stored on disk
+   - migration/block-dirty-bitmap: reject bitmap load onto ro node
+   - block/monitor: reject persistent bitmap add on a read-only node
+   - tests/unit/test-blockjob: cover keeping a job paused
+     while a pause is pending
+   - job: keep job paused across overlapping pause requests
+   - hw/input/ps2: answer unknown mouse commands with a resend
+   - hw/display/vga: fix text-mode OOB write after a graphics surface switch
+     Closes: CVE-2026-77913
+   - crypto: fix build against nettle >= 4
+   - virtio-gpu: use g_try_malloc to avoid guest-triggered abort
+   - hw/display/qxl: validate primary surface stride against width
+     Closes: CVE-2026-16271
+
+ -- Michael Tokarev <[email protected]>  Sat, 03 Oct 2026 20:01:57 +0300
+
 qemu (1:10.0.13+ds-0+deb13u1) trixie; urgency=medium
 
   * new upstream stable/bugfix release, including multiple security fixes:
diff -Nru qemu-10.0.13+ds/debian/control.mk qemu-10.0.14+ds/debian/control.mk
--- qemu-10.0.13+ds/debian/control.mk   2026-08-29 20:55:40.000000000 +0300
+++ qemu-10.0.14+ds/debian/control.mk   2026-10-02 22:51:51.000000000 +0300
@@ -9,7 +9,7 @@
 
 # since some files and/or lists differ from version to version,
 # ensure we have the expected qemu version, or else scream loudly
-checked-version := 10.0.13+ds
+checked-version := 10.0.14+ds
 # version of last vdso change for d/control Depends field:
 vdso-version := 1:9.2.0~rc3+ds-1~
 
diff -Nru qemu-10.0.13+ds/VERSION qemu-10.0.14+ds/VERSION
--- qemu-10.0.13+ds/VERSION     2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/VERSION     2026-09-29 03:48:20.000000000 +0300
@@ -1 +1 @@
-10.0.13
+10.0.14
diff -Nru qemu-10.0.13+ds/accel/tcg/cpu-exec.c 
qemu-10.0.14+ds/accel/tcg/cpu-exec.c
--- qemu-10.0.13+ds/accel/tcg/cpu-exec.c        2026-08-26 22:31:25.000000000 
+0300
+++ qemu-10.0.14+ds/accel/tcg/cpu-exec.c        2026-09-29 03:48:20.000000000 
+0300
@@ -580,7 +580,8 @@
         /* Execute in a serial context. */
         cflags &= ~CF_PARALLEL;
         /* After 1 insn, return and release the exclusive lock. */
-        cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | 1;
+        cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR |
+                    CF_NOIRQ | CF_STEP_ATOMIC | 1;
         /*
          * No need to check_for_breakpoints here.
          * We only arrive in cpu_exec_step_atomic after beginning execution
diff -Nru qemu-10.0.13+ds/accel/tcg/cputlb.c qemu-10.0.14+ds/accel/tcg/cputlb.c
--- qemu-10.0.13+ds/accel/tcg/cputlb.c  2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/accel/tcg/cputlb.c  2026-09-29 03:48:20.000000000 +0300
@@ -1363,7 +1363,6 @@
     uint64_t tlb_addr = tlb_read_idx(entry, access_type);
     vaddr page_addr = addr & TARGET_PAGE_MASK;
     int flags = TLB_FLAGS_MASK & ~TLB_FORCE_SLOW;
-    bool force_mmio = check_mem_cbs && cpu_plugin_mem_cbs_enabled(cpu);
     CPUTLBEntryFull *full;
 
     if (!tlb_hit_page(tlb_addr, page_addr)) {
@@ -1393,16 +1392,13 @@
 
     *pfull = full = &cpu->neg.tlb.d[mmu_idx].fulltlb[index];
     flags |= full->slow_flags[access_type];
-
-    /* Fold all "mmio-like" bits into TLB_MMIO.  This is not RAM.  */
-    if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY | TLB_CHECK_ALIGNED))
-        || (access_type != MMU_INST_FETCH && force_mmio)) {
-        *phost = NULL;
-        return TLB_MMIO;
+    if (check_mem_cbs && cpu_plugin_mem_cbs_enabled(cpu)) {
+        flags |= TLB_FORCE_SLOW;
     }
 
-    /* Everything else is RAM. */
-    *phost = (void *)((uintptr_t)addr + entry->addend);
+    *phost = (flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY | TLB_CHECK_ALIGNED)
+              ? NULL
+              : (void *)((uintptr_t)addr + entry->addend));
     return flags;
 }
 
@@ -1587,7 +1583,7 @@
     data->phys_addr = full->phys_addr | (addr & ~TARGET_PAGE_MASK);
 
     /* We must have an iotlb entry for MMIO */
-    if (tlb_addr & TLB_MMIO) {
+    if (full->slow_flags[access_type] & TLB_MMIO) {
         MemoryRegionSection *section =
             iotlb_to_section(cpu, full->xlat_section & ~TARGET_PAGE_MASK,
                              full->attrs);
diff -Nru qemu-10.0.13+ds/block/dirty-bitmap.c 
qemu-10.0.14+ds/block/dirty-bitmap.c
--- qemu-10.0.13+ds/block/dirty-bitmap.c        2026-08-26 22:31:25.000000000 
+0300
+++ qemu-10.0.14+ds/block/dirty-bitmap.c        2026-09-29 03:48:20.000000000 
+0300
@@ -612,7 +612,7 @@
                                                   const BdrvDirtyBitmap 
*bitmap)
 {
     uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
-    uint64_t limit = granularity * (serialized_chunk_size << 3);
+    uint64_t limit = granularity * ((uint64_t)serialized_chunk_size << 3);
 
     assert(QEMU_IS_ALIGNED(limit,
                            bdrv_dirty_bitmap_serialization_align(bitmap)));
diff -Nru qemu-10.0.13+ds/block/monitor/bitmap-qmp-cmds.c 
qemu-10.0.14+ds/block/monitor/bitmap-qmp-cmds.c
--- qemu-10.0.13+ds/block/monitor/bitmap-qmp-cmds.c     2026-08-26 
22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/block/monitor/bitmap-qmp-cmds.c     2026-09-29 
03:48:20.000000000 +0300
@@ -125,10 +125,17 @@
         disabled = false;
     }
 
-    if (persistent &&
-        !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp))
-    {
-        return;
+    if (persistent) {
+        if (!bdrv_is_writable(bs)) {
+            error_setg(errp, "Cannot add a persistent bitmap to "
+                       "read-only or inactive node '%s'",
+                       bdrv_get_node_name(bs));
+            return;
+        }
+
+        if (!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) {
+            return;
+        }
     }
 
     bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp);
@@ -158,11 +165,11 @@
         return NULL;
     }
 
-    if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO,
-                                errp)) {
+    if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) {
         return NULL;
     }
 
+    /* Dropping a bitmap needs no write access unless it is actually stored. */
     if (bdrv_dirty_bitmap_get_persistence(bitmap) &&
         bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0)
     {
diff -Nru qemu-10.0.13+ds/block/qcow2-bitmap.c 
qemu-10.0.14+ds/block/qcow2-bitmap.c
--- qemu-10.0.13+ds/block/qcow2-bitmap.c        2026-08-26 22:31:25.000000000 
+0300
+++ qemu-10.0.14+ds/block/qcow2-bitmap.c        2026-09-29 03:48:20.000000000 
+0300
@@ -1487,6 +1487,15 @@
         goto out;
     }
 
+    if (!can_write(bs)) {
+        error_setg(errp, "Cannot remove persistent bitmap '%s': "
+                   "no write access to node '%s'", name,
+                   bdrv_get_node_name(bs));
+        ret = -EACCES;
+        bm = NULL;
+        goto out;
+    }
+
     QSIMPLEQ_REMOVE(bm_list, bm, Qcow2Bitmap, entry);
 
     ret = update_ext_header_and_dir(bs, bm_list);
diff -Nru qemu-10.0.13+ds/contrib/vhost-user-gpu/vhost-user-gpu.c 
qemu-10.0.14+ds/contrib/vhost-user-gpu/vhost-user-gpu.c
--- qemu-10.0.13+ds/contrib/vhost-user-gpu/vhost-user-gpu.c     2026-08-26 
22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/contrib/vhost-user-gpu/vhost-user-gpu.c     2026-09-29 
03:48:20.000000000 +0300
@@ -487,7 +487,7 @@
                       struct virtio_gpu_ctrl_command *cmd,
                       struct iovec **iov)
 {
-    struct virtio_gpu_mem_entry *ents;
+    g_autofree struct virtio_gpu_mem_entry *ents = NULL;
     size_t esize, s;
     int i;
 
@@ -498,17 +498,22 @@
     }
 
     esize = sizeof(*ents) * ab->nr_entries;
-    ents = g_malloc(esize);
+    ents = g_try_malloc(esize);
+    if (!ents && esize) {
+        return -1;
+    }
     s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
                    sizeof(*ab), ents, esize);
     if (s != esize) {
         g_critical("%s: command data size incorrect %zu vs %zu",
                    __func__, s, esize);
-        g_free(ents);
         return -1;
     }
 
-    *iov = g_new0(struct iovec, ab->nr_entries);
+    *iov = g_try_new0(struct iovec, ab->nr_entries);
+    if (!*iov && ab->nr_entries) {
+        return -1;
+    }
     for (i = 0; i < ab->nr_entries; i++) {
         uint64_t len = ents[i].length;
         (*iov)[i].iov_len = ents[i].length;
@@ -517,12 +522,10 @@
             g_critical("%s: resource %d element %d",
                        __func__, ab->resource_id, i);
             g_free(*iov);
-            g_free(ents);
             *iov = NULL;
             return -1;
         }
     }
-    g_free(ents);
     return 0;
 }
 
@@ -828,8 +831,14 @@
                 PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) / 8;
             size_t size = width * height * bpp;
 
-            void *p = g_malloc(VHOST_USER_GPU_HDR_SIZE +
-                               sizeof(VhostUserGpuUpdate) + size);
+            void *p = g_try_malloc(VHOST_USER_GPU_HDR_SIZE +
+                                   sizeof(VhostUserGpuUpdate) + size);
+            if (!p) {
+                pixman_region_fini(&region);
+                pixman_region_fini(&finalregion);
+                cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
+                break;
+            }
             VhostUserGpuMsg *msg = p;
             msg->request = VHOST_USER_GPU_UPDATE;
             msg->size = sizeof(VhostUserGpuUpdate) + size;
diff -Nru qemu-10.0.13+ds/contrib/vhost-user-gpu/virgl.c 
qemu-10.0.14+ds/contrib/vhost-user-gpu/virgl.c
--- qemu-10.0.13+ds/contrib/vhost-user-gpu/virgl.c      2026-08-26 
22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/contrib/vhost-user-gpu/virgl.c      2026-09-29 
03:48:20.000000000 +0300
@@ -197,19 +197,27 @@
                     struct virtio_gpu_ctrl_command *cmd)
 {
     struct virtio_gpu_cmd_submit cs;
+    size_t iov_len;
     void *buf;
     size_t s;
 
     VUGPU_FILL_CMD(cs);
 
-    if (cs.size > VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE) {
-        g_critical("%s: command buffer too large (%u)",
-                   __func__, cs.size);
+    iov_len = iov_size(cmd->elem.out_sg, cmd->elem.out_num);
+    if (cs.size == 0 || iov_len < sizeof(cs) ||
+        cs.size > iov_len - sizeof(cs) ||
+        cs.size > VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE) {
+        g_critical("%s: size out of range (%u/%zu)",
+                   __func__, cs.size, iov_len);
         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
         return;
     }
 
-    buf = g_malloc(cs.size);
+    buf = g_try_malloc(cs.size);
+    if (!buf) {
+        cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
+        return;
+    }
     s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
                    sizeof(cs), buf, cs.size);
     if (s != cs.size) {
diff -Nru qemu-10.0.13+ds/contrib/vhost-user-gpu/vugbm.c 
qemu-10.0.14+ds/contrib/vhost-user-gpu/vugbm.c
--- qemu-10.0.13+ds/contrib/vhost-user-gpu/vugbm.c      2026-08-26 
22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/contrib/vhost-user-gpu/vugbm.c      2026-09-29 
03:48:20.000000000 +0300
@@ -13,7 +13,10 @@
 static bool
 mem_alloc_bo(struct vugbm_buffer *buf)
 {
-    buf->mmap = g_malloc((uint64_t)buf->width * buf->height * 4);
+    buf->mmap = g_try_malloc((uint64_t)buf->width * buf->height * 4);
+    if (!buf->mmap && buf->width && buf->height) {
+        return false;
+    }
     buf->stride = buf->width * 4;
     return true;
 }
diff -Nru qemu-10.0.13+ds/crypto/hash-nettle.c 
qemu-10.0.14+ds/crypto/hash-nettle.c
--- qemu-10.0.13+ds/crypto/hash-nettle.c        2026-08-26 22:31:25.000000000 
+0300
+++ qemu-10.0.14+ds/crypto/hash-nettle.c        2026-09-29 03:48:20.000000000 
+0300
@@ -24,7 +24,8 @@
 #include "crypto/hash.h"
 #include "hashpriv.h"
 #include <nettle/md5.h>
-#include <nettle/sha.h>
+#include <nettle/sha1.h>
+#include <nettle/sha2.h>
 #include <nettle/ripemd160.h>
 #ifdef CONFIG_CRYPTO_SM3
 #include <nettle/sm3.h>
diff -Nru qemu-10.0.13+ds/hw/9pfs/cofile.c qemu-10.0.14+ds/hw/9pfs/cofile.c
--- qemu-10.0.13+ds/hw/9pfs/cofile.c    2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/9pfs/cofile.c    2026-09-29 03:48:20.000000000 +0300
@@ -143,10 +143,11 @@
     cred.fc_mode = mode & 07777;
     cred.fc_uid = fidp->uid;
     cred.fc_gid = gid;
+    v9fs_path_init(&path);
     /*
      * Hold the directory fid lock so that directory path name
-     * don't change. Take the write lock to be sure this fid
-     * cannot be used by another operation.
+     * don't change. Take the write lock since the fid path is
+     * mutated below on success.
      */
     v9fs_path_write_lock(s);
     v9fs_co_run_in_worker(
@@ -156,23 +157,30 @@
             if (err < 0) {
                 err = -errno;
             } else {
-                v9fs_path_init(&path);
                 err = v9fs_name_to_path(s, &fidp->path, name->data, &path);
                 if (!err) {
                     err = s->ops->lstat(&s->ctx, &path, stbuf);
                     if (err < 0) {
                         err = -errno;
                         s->ops->close(&s->ctx, &fidp->fs);
-                    } else {
-                        v9fs_path_copy(&fidp->path, &path);
                     }
                 } else {
                     s->ops->close(&s->ctx, &fidp->fs);
                 }
-                v9fs_path_free(&path);
             }
         });
+    /*
+     * The fid path must not be mutated from the worker thread: other
+     * requests may access the same fid on the main thread, and the main
+     * thread never takes the path lock for reads. Mutate the new path
+     * here, on the main thread and still under the held write lock, like
+     * every other mutation of a fid path.
+     */
+    if (!err) {
+        v9fs_path_copy(&fidp->path, &path);
+    }
     v9fs_path_unlock(s);
+    v9fs_path_free(&path);
     if (!err) {
         total_open_fd++;
         if (total_open_fd > open_fd_hw) {
diff -Nru qemu-10.0.13+ds/hw/display/qxl-render.c 
qemu-10.0.14+ds/hw/display/qxl-render.c
--- qemu-10.0.13+ds/hw/display/qxl-render.c     2026-08-26 22:31:25.000000000 
+0300
+++ qemu-10.0.14+ds/hw/display/qxl-render.c     2026-09-29 03:48:20.000000000 
+0300
@@ -27,6 +27,7 @@
 static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect)
 {
     DisplaySurface *surface = qemu_console_surface(qxl->vga.con);
+    int dst_stride = surface_stride(surface);
     uint8_t *dst = surface_data(surface);
     uint8_t *src;
     int len, i;
@@ -45,14 +46,14 @@
     } else {
         src += rect->top * qxl->guest_primary.abs_stride;
     }
-    dst += rect->top  * qxl->guest_primary.abs_stride;
+    dst += rect->top  * dst_stride;
     src += rect->left * qxl->guest_primary.bytes_pp;
     dst += rect->left * qxl->guest_primary.bytes_pp;
     len  = (rect->right - rect->left) * qxl->guest_primary.bytes_pp;
 
     for (i = rect->top; i < rect->bottom; i++) {
         memcpy(dst, src, len);
-        dst += qxl->guest_primary.abs_stride;
+        dst += dst_stride;
         src += qxl->guest_primary.qxl_stride;
     }
 }
@@ -61,30 +62,13 @@
 {
     QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
 
-    qxl->guest_primary.qxl_stride = sc->stride;
-    qxl->guest_primary.abs_stride = abs(sc->stride);
+    qxl->guest_primary.qxl_stride = le32_to_cpu(sc->stride);
+    qxl->guest_primary.abs_stride = abs(qxl->guest_primary.qxl_stride);
     qxl->guest_primary.resized++;
-    switch (sc->format) {
-    case SPICE_SURFACE_FMT_16_555:
-        qxl->guest_primary.bytes_pp = 2;
-        qxl->guest_primary.bits_pp = 15;
-        break;
-    case SPICE_SURFACE_FMT_16_565:
-        qxl->guest_primary.bytes_pp = 2;
-        qxl->guest_primary.bits_pp = 16;
-        break;
-    case SPICE_SURFACE_FMT_32_xRGB:
-    case SPICE_SURFACE_FMT_32_ARGB:
-        qxl->guest_primary.bytes_pp = 4;
-        qxl->guest_primary.bits_pp = 32;
-        break;
-    default:
-        fprintf(stderr, "%s: unhandled format: %x\n", __func__,
-                qxl->guest_primary.surface.format);
-        qxl->guest_primary.bytes_pp = 4;
-        qxl->guest_primary.bits_pp = 32;
-        break;
-    }
+    /* fallback to default bpp if format is unknown */
+    qxl_format_bpp(qxl, le32_to_cpu(sc->format),
+                   &qxl->guest_primary.bytes_pp,
+                   &qxl->guest_primary.bits_pp);
 }
 
 static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area)
@@ -101,15 +85,45 @@
     DisplaySurface *surface;
     int width = qxl->guest_head0_width ?: qxl->guest_primary.surface.width;
     int height = qxl->guest_head0_height ?: qxl->guest_primary.surface.height;
+    uint64_t map_height;
     int i;
 
+    if (width <= 0 || height <= 0) {
+        goto end;
+    }
+
+    if (qxl->guest_primary.bytes_pp > 0) {
+        int max_width = qxl->guest_primary.abs_stride
+                        / qxl->guest_primary.bytes_pp;
+        width = MIN(width, max_width);
+    }
+
+    if (qxl->guest_primary.qxl_stride < 0) {
+        /* qxl_blit() uses the primary height to find the first scanline. */
+        height = MIN(height, (int)qxl->guest_primary.surface.height);
+    }
+
+    if (qxl->guest_primary.abs_stride > 0) {
+        int max_height = qxl->vgamem_size / qxl->guest_primary.abs_stride;
+        height = MIN(height, max_height);
+    }
+
+    /*
+     * height limits the visible update, while map_height is the guest memory
+     * span validated by qxl_phys2virt().  With a negative stride qxl_blit()
+     * addresses scanlines from the declared primary height, so a shorter
+     * monitor still requires validating the full primary surface.
+     */
+    map_height = qxl->guest_primary.qxl_stride < 0 ?
+                 qxl->guest_primary.surface.height : height;
+
     if (qxl->guest_primary.resized) {
         qxl->guest_primary.resized = 0;
         qxl->guest_primary.data = qxl_phys2virt(qxl,
                                                 qxl->guest_primary.surface.mem,
                                                 MEMSLOT_GROUP_GUEST,
                                                 qxl->guest_primary.abs_stride
-                                                * height);
+                                                * map_height);
         if (!qxl->guest_primary.data) {
             goto end;
         }
diff -Nru qemu-10.0.13+ds/hw/display/qxl.c qemu-10.0.14+ds/hw/display/qxl.c
--- qemu-10.0.13+ds/hw/display/qxl.c    2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/display/qxl.c    2026-09-29 03:48:20.000000000 +0300
@@ -299,10 +299,12 @@
     qemu_mutex_lock(&qxl->track_lock);
     qxl->guest_cursor = 0;
     qemu_mutex_unlock(&qxl->track_lock);
+    qemu_mutex_lock(&qxl->ssd.lock);
     if (qxl->ssd.cursor) {
         cursor_unref(qxl->ssd.cursor);
     }
     qxl->ssd.cursor = cursor_builtin_hidden();
+    qemu_mutex_unlock(&qxl->ssd.lock);
 }
 
 static uint32_t qxl_crc32(const uint8_t *p, unsigned len)
@@ -1507,6 +1509,47 @@
     qxl_render_resize(qxl);
 }
 
+/*
+ * Convert a SpiceSurfaceFormat to bytes per pixel and bits per pixel.
+ *
+ * Only valid for surface suitable for rendering.
+ */
+bool qxl_format_bpp(PCIQXLDevice *qxl, SpiceSurfaceFmt format,
+                    uint32_t *bytes_pp, uint32_t *bits_pp)
+{
+    uint32_t bypp = 4;
+    uint32_t bipp = 32;
+    bool ret = true;
+
+    switch (format) {
+    case SPICE_SURFACE_FMT_16_555:
+        bypp = 2;
+        bipp = 15;
+        break;
+    case SPICE_SURFACE_FMT_16_565:
+        bypp = 2;
+        bipp = 16;
+        break;
+    case SPICE_SURFACE_FMT_32_xRGB:
+    case SPICE_SURFACE_FMT_32_ARGB:
+        bypp = 4;
+        bipp = 32;
+        break;
+    default:
+        ret = false;
+        qxl_set_guest_bug(qxl, "%s: unhandled format: %x", __func__, format);
+    }
+
+    if (bytes_pp != NULL) {
+        *bytes_pp = bypp;
+    }
+    if (bits_pp != NULL) {
+        *bits_pp = bipp;
+    }
+
+    return ret;
+}
+
 static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
                                      qxl_async_io async)
 {
@@ -1514,6 +1557,7 @@
     QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
     uint32_t requested_height = le32_to_cpu(sc->height);
     int requested_stride = le32_to_cpu(sc->stride);
+    uint32_t bytes_pp;
 
     if (requested_stride == INT32_MIN ||
         abs(requested_stride) * (uint64_t)requested_height
@@ -1550,6 +1594,23 @@
         return;
     }
 
+    if (!qxl_format_bpp(qxl, surface.format, &bytes_pp, NULL)) {
+        return;
+    }
+
+    if (surface.width == 0 || surface.height == 0) {
+        qxl_set_guest_bug(qxl, "%s: zero dimension %ux%u",
+                          __func__, surface.width, surface.height);
+        return;
+    }
+
+    if ((uint64_t)surface.width * bytes_pp > abs(surface.stride)) {
+        qxl_set_guest_bug(qxl, "%s: stride too small for width:"
+                          " stride %d width %u bpp %u",
+                          __func__, surface.stride, surface.width, bytes_pp);
+        return;
+    }
+
     surface.mouse_mode = true;
     surface.group_id   = MEMSLOT_GROUP_GUEST;
     if (loadvm) {
diff -Nru qemu-10.0.13+ds/hw/display/qxl.h qemu-10.0.14+ds/hw/display/qxl.h
--- qemu-10.0.13+ds/hw/display/qxl.h    2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/display/qxl.h    2026-09-29 03:48:20.000000000 +0300
@@ -181,6 +181,8 @@
 void qxl_spice_reset_memslots(PCIQXLDevice *qxl);
 void qxl_spice_reset_image_cache(PCIQXLDevice *qxl);
 void qxl_spice_reset_cursor(PCIQXLDevice *qxl);
+bool qxl_format_bpp(PCIQXLDevice *qxl, SpiceSurfaceFmt format,
+                    uint32_t *bytes_pp, uint32_t *bits_pp);
 
 /* qxl-logger.c */
 int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id);
diff -Nru qemu-10.0.13+ds/hw/display/vga.c qemu-10.0.14+ds/hw/display/vga.c
--- qemu-10.0.13+ds/hw/display/vga.c    2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/display/vga.c    2026-09-29 03:48:20.000000000 +0300
@@ -1239,7 +1239,10 @@
         return;
     }
 
-    if (width != s->last_width || height != s->last_height ||
+    if (surface == NULL ||
+        surface_width(surface) != width * cw ||
+        surface_height(surface) != height * cheight ||
+        width != s->last_text_width || height != s->last_text_height ||
         cw != s->last_cw || cheight != s->last_ch || s->last_depth) {
         s->last_scr_width = width * cw;
         s->last_scr_height = height * cheight;
@@ -1247,8 +1250,8 @@
         surface = qemu_console_surface(s->con);
         dpy_text_resize(s->con, width, height);
         s->last_depth = 0;
-        s->last_width = width;
-        s->last_height = height;
+        s->last_text_width = width;
+        s->last_text_height = height;
         s->last_ch = cheight;
         s->last_cw = cw;
         full_update = 1;
@@ -1827,6 +1830,8 @@
 
     s->last_width = -1;
     s->last_height = -1;
+    s->last_text_width = -1;
+    s->last_text_height = -1;
 }
 
 void vga_common_reset(VGACommonState *s)
@@ -1869,6 +1874,8 @@
     s->last_ch = 0;
     s->last_width = 0;
     s->last_height = 0;
+    s->last_text_width = 0;
+    s->last_text_height = 0;
     s->last_scr_width = 0;
     s->last_scr_height = 0;
     s->cursor_start = 0;
@@ -1920,8 +1927,8 @@
         s->graphic_mode = graphic_mode;
         full_update = 1;
     }
-    if (s->last_width == -1) {
-        s->last_width = 0;
+    if (s->last_text_width == -1) {
+        s->last_text_width = 0;
         full_update = 1;
     }
 
@@ -1960,15 +1967,15 @@
             break;
         }
 
-        if (width != s->last_width || height != s->last_height ||
+        if (width != s->last_text_width || height != s->last_text_height ||
             cw != s->last_cw || cheight != s->last_ch) {
             s->last_scr_width = width * cw;
             s->last_scr_height = height * cheight;
             qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height);
             dpy_text_resize(s->con, width, height);
             s->last_depth = 0;
-            s->last_width = width;
-            s->last_height = height;
+            s->last_text_width = width;
+            s->last_text_height = height;
             s->last_ch = cheight;
             s->last_cw = cw;
             full_update = 1;
@@ -2053,22 +2060,22 @@
     }
 
     /* Display a message */
-    s->last_width = 60;
-    s->last_height = height = 3;
+    s->last_text_width = 60;
+    s->last_text_height = height = 3;
     dpy_text_cursor(s->con, -1, -1);
-    dpy_text_resize(s->con, s->last_width, height);
+    dpy_text_resize(s->con, s->last_text_width, height);
 
-    for (dst = chardata, i = 0; i < s->last_width * height; i ++)
+    for (dst = chardata, i = 0; i < s->last_text_width * height; i ++)
         console_write_ch(dst ++, ' ');
 
     size = strlen(msg_buffer);
-    width = (s->last_width - size) / 2;
-    dst = chardata + s->last_width + width;
+    width = (s->last_text_width - size) / 2;
+    dst = chardata + s->last_text_width + width;
     for (i = 0; i < size; i ++)
         console_write_ch(dst ++, ATTR2CHTYPE(msg_buffer[i], QEMU_COLOR_BLUE,
                                              QEMU_COLOR_BLACK, 1));
 
-    dpy_text_update(s->con, 0, 0, s->last_width, height);
+    dpy_text_update(s->con, 0, 0, s->last_text_width, height);
 }
 
 static uint64_t vga_mem_read(void *opaque, hwaddr addr,
diff -Nru qemu-10.0.13+ds/hw/display/vga_int.h 
qemu-10.0.14+ds/hw/display/vga_int.h
--- qemu-10.0.13+ds/hw/display/vga_int.h        2026-08-26 22:31:25.000000000 
+0300
+++ qemu-10.0.14+ds/hw/display/vga_int.h        2026-09-29 03:48:20.000000000 
+0300
@@ -122,7 +122,8 @@
     uint32_t plane_updated;
     uint32_t last_line_offset;
     uint8_t last_cw, last_ch;
-    uint32_t last_width, last_height; /* in chars or pixels */
+    uint32_t last_width, last_height; /* in pixels (graphics renderer) */
+    uint32_t last_text_width, last_text_height; /* in chars (text renderer) */
     uint32_t last_scr_width, last_scr_height; /* in pixels */
     uint32_t last_depth; /* in bits */
     bool last_byteswap;
diff -Nru qemu-10.0.13+ds/hw/display/virtio-gpu-rutabaga.c 
qemu-10.0.14+ds/hw/display/virtio-gpu-rutabaga.c
--- qemu-10.0.13+ds/hw/display/virtio-gpu-rutabaga.c    2026-08-26 
22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/display/virtio-gpu-rutabaga.c    2026-09-29 
03:48:20.000000000 +0300
@@ -366,10 +366,20 @@
         return;
     }
 
-    buf = g_new0(uint8_t, cs.size);
+    buf = g_try_new0(uint8_t, cs.size);
+    if (!buf && cs.size) {
+        cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
+        return;
+    }
     s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
                    sizeof(cs), buf, cs.size);
-    CHECK(s == cs.size, cmd);
+    if (s != cs.size) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: size mismatch (%zu/%u)\n",
+                      __func__, s, cs.size);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+        return;
+    }
 
     rutabaga_cmd.ctx_id = cs.hdr.ctx_id;
     rutabaga_cmd.cmd = buf;
diff -Nru qemu-10.0.13+ds/hw/display/virtio-gpu-udmabuf.c 
qemu-10.0.14+ds/hw/display/virtio-gpu-udmabuf.c
--- qemu-10.0.13+ds/hw/display/virtio-gpu-udmabuf.c     2026-08-26 
22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/display/virtio-gpu-udmabuf.c     2026-09-29 
03:48:20.000000000 +0300
@@ -38,8 +38,11 @@
         return;
     }
 
-    list = g_malloc0(sizeof(struct udmabuf_create_list) +
-                     sizeof(struct udmabuf_create_item) * res->iov_cnt);
+    list = g_try_malloc0(sizeof(struct udmabuf_create_list) +
+                         sizeof(struct udmabuf_create_item) * res->iov_cnt);
+    if (!list) {
+        return;
+    }
 
     for (i = 0; i < res->iov_cnt; i++) {
         rcu_read_lock();
@@ -88,6 +91,7 @@
         close(res->dmabuf_fd);
         res->dmabuf_fd = -1;
     }
+    res->blob = NULL;
 }
 
 static int find_memory_backend_type(Object *obj, void *opaque)
diff -Nru qemu-10.0.13+ds/hw/display/virtio-gpu-virgl.c 
qemu-10.0.14+ds/hw/display/virtio-gpu-virgl.c
--- qemu-10.0.13+ds/hw/display/virtio-gpu-virgl.c       2026-08-26 
22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/display/virtio-gpu-virgl.c       2026-09-29 
03:48:20.000000000 +0300
@@ -499,7 +499,11 @@
         return;
     }
 
-    buf = g_malloc(cs.size);
+    buf = g_try_malloc(cs.size);
+    if (!buf && cs.size) {
+        cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
+        return;
+    }
     s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
                    sizeof(cs), buf, cs.size);
     if (s != cs.size) {
diff -Nru qemu-10.0.13+ds/hw/display/virtio-gpu.c 
qemu-10.0.14+ds/hw/display/virtio-gpu.c
--- qemu-10.0.13+ds/hw/display/virtio-gpu.c     2026-08-26 22:31:25.000000000 
+0300
+++ qemu-10.0.14+ds/hw/display/virtio-gpu.c     2026-09-29 03:48:20.000000000 
+0300
@@ -63,8 +63,16 @@
         }
         data = pixman_image_get_data(res->image);
     } else {
+        if (!res->blob) {
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: resource %d has no blob\n",
+                          __func__, resource_id);
+            return;
+        }
         if (res->blob_size < (s->current_cursor->width *
                               s->current_cursor->height * 4)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: blob size too small for resource %d\n",
+                          __func__, resource_id);
             return;
         }
         data = res->blob;
@@ -888,7 +896,10 @@
     }
 
     esize = sizeof(*ents) * nr_entries;
-    ents = g_malloc(esize);
+    ents = g_try_malloc(esize);
+    if (!ents && esize) {
+        return -1;
+    }
     s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
                    offset, ents, esize);
     if (s != esize) {
@@ -909,6 +920,7 @@
         hwaddr len;
         void *map;
 
+        /* TODO: a common DMA map SG helper */
         do {
             len = l;
             map = dma_memory_map(VIRTIO_DEVICE(g)->dma_as, a, &len,
@@ -917,20 +929,27 @@
             if (!map) {
                 qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map MMIO memory 
for"
                               " element %d\n", __func__, e);
-                virtio_gpu_cleanup_mapping_iov(g, *iov, v);
-                g_free(ents);
-                *iov = NULL;
-                if (addr) {
-                    g_free(*addr);
-                    *addr = NULL;
-                }
-                return -1;
+                goto err;
             }
 
             if (!(v % 16)) {
-                *iov = g_renew(struct iovec, *iov, v + 16);
+                struct iovec *new_iov;
+                new_iov = g_try_renew(struct iovec, *iov, v + 16);
+                if (!new_iov) {
+                    dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, map, len,
+                                     DMA_DIRECTION_TO_DEVICE, len);
+                    goto err;
+                }
+                *iov = new_iov;
                 if (addr) {
-                    *addr = g_renew(uint64_t, *addr, v + 16);
+                    uint64_t *new_addr;
+                    new_addr = g_try_renew(uint64_t, *addr, v + 16);
+                    if (!new_addr) {
+                        dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, map, len,
+                                         DMA_DIRECTION_TO_DEVICE, len);
+                        goto err;
+                    }
+                    *addr = new_addr;
                 }
             }
             (*iov)[v].iov_base = map;
@@ -948,6 +967,15 @@
 
     g_free(ents);
     return 0;
+
+err:
+    virtio_gpu_cleanup_mapping_iov(g, *iov, v);
+    *iov = NULL;
+    if (addr) {
+        g_clear_pointer(addr, g_free);
+    }
+    g_free(ents);
+    return -1;
 }
 
 void virtio_gpu_cleanup_mapping_iov(VirtIOGPU *g,
@@ -967,6 +995,16 @@
 void virtio_gpu_cleanup_mapping(VirtIOGPU *g,
                                 struct virtio_gpu_simple_resource *res)
 {
+    if (res->blob) {
+        int i, max_outputs = g->parent_obj.conf.max_outputs;
+
+        for (i = 0; i < max_outputs; i++) {
+            if (g->parent_obj.scanout[i].resource_id == res->resource_id) {
+                virtio_gpu_disable_scanout(g, i);
+            }
+        }
+    }
+
     virtio_gpu_cleanup_mapping_iov(g, res->iov, res->iov_cnt);
     res->iov = NULL;
     res->iov_cnt = 0;
diff -Nru qemu-10.0.13+ds/hw/i386/vapic.c qemu-10.0.14+ds/hw/i386/vapic.c
--- qemu-10.0.13+ds/hw/i386/vapic.c     2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/i386/vapic.c     2026-09-29 03:48:20.000000000 +0300
@@ -30,6 +30,10 @@
 #define ROM_BLOCK_SIZE          512
 #define ROM_BLOCK_MASK          (~(ROM_BLOCK_SIZE - 1))
 
+/* Option ROM window on PC/Q35 machines; the vapic ROM must live in here. */
+#define OPTION_ROM_START        0xc0000
+#define OPTION_ROM_END          0xe0000
+
 typedef enum VAPICMode {
     VAPIC_INACTIVE = 0,
     VAPIC_ACTIVE   = 1,
@@ -586,6 +590,14 @@
     size_t rom_size;
     uint8_t *ram;
 
+    /*
+     * The VAPIC region should be mapped in place, refuse mapping it
+     * outside of the option ROM window.
+     */
+    if (rom_paddr < OPTION_ROM_START || rom_paddr >= OPTION_ROM_END) {
+        return -1;
+    }
+
     if (s->rom_mapped_writable) {
         memory_region_del_subregion(mr, &s->rom);
         object_unparent(OBJECT(&s->rom));
@@ -596,13 +608,16 @@
 
     /* read ROM size from RAM region */
     if (rom_paddr + 2 >= memory_region_size(section.mr)) {
+        memory_region_unref(section.mr);
         return -1;
     }
     ram = memory_region_get_ram_ptr(section.mr);
     rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE;
-    if (rom_size == 0) {
+    if (rom_size == 0 || rom_size > OPTION_ROM_END - rom_paddr) {
+        memory_region_unref(section.mr);
         return -1;
     }
+
     s->rom_size = rom_size;
 
     /* We need to round to avoid creating subpages
@@ -610,6 +625,7 @@
     rom_size += rom_paddr & ~TARGET_PAGE_MASK;
     rom_paddr &= TARGET_PAGE_MASK;
     rom_size = TARGET_PAGE_ALIGN(rom_size);
+    assert(rom_paddr >= OPTION_ROM_START && rom_paddr + rom_size <= 
OPTION_ROM_END);
 
     memory_region_init_alias(&s->rom, OBJECT(s), "kvmvapic-rom", section.mr,
                              rom_paddr, rom_size);
diff -Nru qemu-10.0.13+ds/hw/ide/core.c qemu-10.0.14+ds/hw/ide/core.c
--- qemu-10.0.13+ds/hw/ide/core.c       2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/ide/core.c       2026-09-29 03:48:20.000000000 +0300
@@ -138,11 +138,12 @@
     memset(p, 0, sizeof(s->identify_data));
 
     put_le16(p + 0, 0x0040);
+    /* Words 1, 3 and 6 describe the default translation (ATA-5 8.16.8) */
     put_le16(p + 1, s->cylinders);
-    put_le16(p + 3, s->heads);
-    put_le16(p + 4, 512 * s->sectors); /* XXX: retired, remove ? */
+    put_le16(p + 3, s->drive_heads);
+    put_le16(p + 4, 512 * s->drive_sectors); /* XXX: retired, remove ? */
     put_le16(p + 5, 512); /* XXX: retired, remove ? */
-    put_le16(p + 6, s->sectors);
+    put_le16(p + 6, s->drive_sectors);
     padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */
     put_le16(p + 20, 3); /* XXX: retired, remove ? */
     put_le16(p + 21, 512); /* cache size in sectors */
@@ -332,8 +333,8 @@
 
     put_le16(p + 0, 0x848a);                    /* CF Storage Card signature */
     put_le16(p + 1, s->cylinders);              /* Default cylinders */
-    put_le16(p + 3, s->heads);                  /* Default heads */
-    put_le16(p + 6, s->sectors);                /* Default sectors per track */
+    put_le16(p + 3, s->drive_heads);            /* Default heads */
+    put_le16(p + 6, s->drive_sectors);          /* Default sectors per track */
     /* *(p + 7) := nb_sectors >> 16 -- see ide_cfata_identify_size */
     /* *(p + 8) := nb_sectors       -- see ide_cfata_identify_size */
     padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */
@@ -1655,14 +1656,21 @@
 /* INITIALIZE DEVICE PARAMETERS */
 static bool cmd_specify(IDEState *s, uint8_t cmd)
 {
-    if (s->blk && s->drive_kind != IDE_CD) {
-        s->heads = (s->select & (ATA_DEV_HS)) + 1;
-        s->sectors = s->nsector;
-        ide_bus_set_irq(s->bus);
-    } else {
+    if (!s->blk || s->drive_kind == IDE_CD) {
+        ide_abort_command(s);
+        return true;
+    }
+
+    /* ATA-2 D.2.8 limits IDENTIFY DEVICE word 56, and the count, to 1..255 */
+    if (s->nsector == 0 || s->nsector > 255) {
         ide_abort_command(s);
+        return true;
     }
 
+    s->heads = (s->select & (ATA_DEV_HS)) + 1;
+    s->sectors = s->nsector;
+    ide_bus_set_irq(s->bus);
+
     return true;
 }
 
diff -Nru qemu-10.0.13+ds/hw/input/ps2.c qemu-10.0.14+ds/hw/input/ps2.c
--- qemu-10.0.13+ds/hw/input/ps2.c      2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/input/ps2.c      2026-09-29 03:48:20.000000000 +0300
@@ -72,6 +72,7 @@
 #define AUX_SET_DEFAULT     0xF6
 #define AUX_RESET           0xFF    /* Reset aux device */
 #define AUX_ACK             0xFA    /* Command byte ACK. */
+#define AUX_RESEND          0xFE    /* Command NACK, send the cmd again */
 
 #define MOUSE_STATUS_REMOTE     0x40
 #define MOUSE_STATUS_ENABLED    0x20
@@ -961,6 +962,11 @@
                 s->mouse_type);
             break;
         default:
+            /*
+             * A PS/2 device answers every command it is given; an unknown
+             * one draws a resend.
+             */
+            ps2_queue(ps2, AUX_RESEND);
             break;
         }
         break;
diff -Nru qemu-10.0.13+ds/hw/intc/bcm2835_ic.c 
qemu-10.0.14+ds/hw/intc/bcm2835_ic.c
--- qemu-10.0.13+ds/hw/intc/bcm2835_ic.c        2026-08-26 22:31:25.000000000 
+0300
+++ qemu-10.0.14+ds/hw/intc/bcm2835_ic.c        2026-09-29 03:48:20.000000000 
+0300
@@ -139,10 +139,19 @@
     BCM2835ICState *s = opaque;
 
     switch (offset) {
-    case FIQ_CONTROL:
-        s->fiq_select = extract32(val, 0, 7);
+    case FIQ_CONTROL: {
+        unsigned fiq_select = extract32(val, 0, 7);
+
+        if (fiq_select >= GPU_IRQS + ARM_IRQS) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: FIQ select %u out of range\n",
+                          __func__, fiq_select);
+            return;
+        }
+        s->fiq_select = fiq_select;
         s->fiq_enable = extract32(val, 7, 1);
         break;
+    }
     case IRQ_ENABLE_1:
         s->gpu_irq_enable |= val;
         break;
diff -Nru qemu-10.0.13+ds/hw/m68k/q800.c qemu-10.0.14+ds/hw/m68k/q800.c
--- qemu-10.0.13+ds/hw/m68k/q800.c      2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/m68k/q800.c      2026-09-29 03:48:20.000000000 +0300
@@ -715,12 +715,14 @@
 
 static GlobalProperty hw_compat_q800[] = {
     { "scsi-hd", "quirk_mode_page_vendor_specific_apple", "on" },
+    { "scsi-hd", "quirk_mode_page_set_block_size", "on" },
     { "scsi-hd", "vendor", " SEAGATE" },
     { "scsi-hd", "product", "          ST225N" },
     { "scsi-hd", "ver", "1.0 " },
     { "scsi-cd", "quirk_mode_page_apple_vendor", "on" },
     { "scsi-cd", "quirk_mode_sense_rom_use_dbd", "on" },
     { "scsi-cd", "quirk_mode_page_vendor_specific_apple", "on" },
+    { "scsi-cd", "quirk_mode_page_set_block_size", "on" },
     { "scsi-cd", "quirk_mode_page_truncated", "on" },
     { "scsi-cd", "vendor", "MATSHITA" },
     { "scsi-cd", "product", "CD-ROM CR-8005" },
diff -Nru qemu-10.0.13+ds/hw/mem/cxl_type3.c qemu-10.0.14+ds/hw/mem/cxl_type3.c
--- qemu-10.0.13+ds/hw/mem/cxl_type3.c  2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/mem/cxl_type3.c  2026-09-29 03:48:20.000000000 +0300
@@ -903,8 +903,8 @@
     }
 
     /* DOE Initialization */
-    pcie_doe_init(pci_dev, &ct3d->doe_cdat, 0x190, doe_cdat_prot, true,
-                  CXL_T3_MSIX_PCIE_DOE_TABLE_ACCESS);
+    pcie_doe_init(pci_dev, &ct3d->doe_cdat, cxl_cstate->dvsec_offset,
+                  doe_cdat_prot, true, CXL_T3_MSIX_PCIE_DOE_TABLE_ACCESS);
 
     cxl_cstate->cdat.build_cdat_table = ct3_build_cdat_table;
     cxl_cstate->cdat.free_cdat_table = ct3_free_cdat_table;
@@ -1093,7 +1093,7 @@
         }
         if (((uint64_t)host_addr < decoder_base) ||
             (hpa_offset >= decoder_size)) {
-            int decoded_iw = cxl_interleave_ways_dec(iw, &error_fatal);
+            int decoded_iw = cxl_interleave_ways_dec(iw, NULL);
 
             if (decoded_iw == 0) {
                 return false;
diff -Nru qemu-10.0.13+ds/hw/net/virtio-net.c 
qemu-10.0.14+ds/hw/net/virtio-net.c
--- qemu-10.0.13+ds/hw/net/virtio-net.c 2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/net/virtio-net.c 2026-09-29 03:48:20.000000000 +0300
@@ -1756,12 +1756,16 @@
     }
 
     ptr += n->host_hdr_len;
+    size -= n->host_hdr_len;
+
+    if (size < sizeof(struct eth_header)) {
+        return 0;
+    }
 
     if (!memcmp(&ptr[12], vlan, sizeof(vlan))) {
         int vid;
 
-        /* Truncated vlan packet */
-        if (size < n->host_hdr_len + 16) {
+        if (size < 16) {
             return 0;
         }
         vid = lduw_be_p(ptr + 14) & 0xfff;
@@ -2198,13 +2202,31 @@
 {
     uint16_t hdr_len;
     VirtioNetRscSeg *seg;
+    size_t ip_size;
 
     hdr_len = chain->n->guest_hdr_len;
+
+    /*
+     * Strip any trailing padding beyond the IP payload so that seg->size
+     * stays in sync with the IP length field used by the bounds check in
+     * virtio_net_rsc_coalesce_data(). virtio_net_rsc_sanity_check4/6()
+     * guarantees that ip_size <= size.
+     */
+    ip_size = hdr_len + sizeof(struct eth_header);
+    if (chain->proto == ETH_P_IP) {
+        struct ip_header *ip = (struct ip_header *)(buf + ip_size);
+        ip_size += htons(ip->ip_len);
+    } else {
+        struct ip6_header *ip6 = (struct ip6_header *)(buf + ip_size);
+        ip_size += sizeof(struct ip6_header)
+                   + htons(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
+    }
+
     seg = g_new(VirtioNetRscSeg, 1);
     seg->buf = g_malloc(hdr_len + sizeof(struct eth_header)
         + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD);
-    memcpy(seg->buf, buf, size);
-    seg->size = size;
+    memcpy(seg->buf, buf, ip_size);
+    seg->size = ip_size;
     seg->packets = 1;
     seg->dup_ack = 0;
     seg->is_coalesced = 0;
diff -Nru qemu-10.0.13+ds/hw/riscv/virt.c qemu-10.0.14+ds/hw/riscv/virt.c
--- qemu-10.0.13+ds/hw/riscv/virt.c     2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/riscv/virt.c     2026-09-29 03:48:20.000000000 +0300
@@ -405,7 +405,8 @@
             (memmap[VIRT_CLINT].size * socket);
         size = memmap[VIRT_CLINT].size - RISCV_ACLINT_SWI_SIZE;
     }
-    name = g_strdup_printf("/soc/mtimer@%lx", addr);
+    name = g_strdup_printf("/soc/mtimer@%lx",
+                           addr + RISCV_ACLINT_DEFAULT_MTIME);
     qemu_fdt_add_subnode(ms->fdt, name);
     qemu_fdt_setprop_string(ms->fdt, name, "compatible",
         "riscv,aclint-mtimer");
diff -Nru qemu-10.0.13+ds/hw/s390x/s390-pci-inst.c 
qemu-10.0.14+ds/hw/s390x/s390-pci-inst.c
--- qemu-10.0.13+ds/hw/s390x/s390-pci-inst.c    2026-08-26 22:31:25.000000000 
+0300
+++ qemu-10.0.14+ds/hw/s390x/s390-pci-inst.c    2026-09-29 03:48:20.000000000 
+0300
@@ -653,6 +653,7 @@
         goto out;
     } else {
         if (cache) {
+            /* valid->valid transitions reuse the DMA slot */
             if (cache->perm == entry->perm &&
                 cache->translated_addr == entry->translated_addr) {
                 goto out;
@@ -663,6 +664,9 @@
             memory_region_notify_iommu(&iommu->iommu_mr, 0, event);
             event.type = IOMMU_NOTIFIER_MAP;
             event.entry.perm = entry->perm;
+        } else {
+            /* invalid->valid transitions consume a new DMA slot */
+            dec_dma_avail(iommu);
         }
 
         cache = g_new(S390IOTLBEntry, 1);
@@ -671,7 +675,6 @@
         cache->len = TARGET_PAGE_SIZE;
         cache->perm = entry->perm;
         g_hash_table_replace(iommu->iotlb, &cache->iova, cache);
-        dec_dma_avail(iommu);
     }
 
     /*
diff -Nru qemu-10.0.13+ds/hw/s390x/sclp.c qemu-10.0.14+ds/hw/s390x/sclp.c
--- qemu-10.0.13+ds/hw/s390x/sclp.c     2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/s390x/sclp.c     2026-09-29 03:48:20.000000000 +0300
@@ -291,7 +291,7 @@
     sclp_c->execute(sclp, work_sccb, code);
 out_write:
     s390_cpu_pv_mem_write(env_archcpu(env), 0, work_sccb,
-                          be16_to_cpu(work_sccb->h.length));
+                          be16_to_cpu(header.length));
     sclp_c->service_interrupt(sclp, SCLP_PV_DUMMY_ADDR);
     return 0;
 }
diff -Nru qemu-10.0.13+ds/hw/scsi/scsi-disk.c 
qemu-10.0.14+ds/hw/scsi/scsi-disk.c
--- qemu-10.0.13+ds/hw/scsi/scsi-disk.c 2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/scsi/scsi-disk.c 2026-09-29 03:48:20.000000000 +0300
@@ -1695,8 +1695,12 @@
         goto invalid_param;
     }
 
-    /* Allow changing the block size */
-    if (bd_len) {
+    /*
+     * Allow changing the block size only if the quirk is enabled for it.
+     * Writing s->qdev.blocksize is not thread safe!
+     */
+    if (bd_len && (s->quirks &
+                   (1 << SCSI_DISK_QUIRK_MODE_PAGE_SET_BLOCK_SIZE))) {
         bs = p[5] << 16 | p[6] << 8 | p[7];
 
         /*
@@ -1933,6 +1937,7 @@
     SCSIRequest *req = &r->req;
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
     uint32_t nb_sectors = scsi_data_cdb_xfer(r->req.cmd.buf);
+    uint32_t buflen = MIN(s->qdev.blocksize, r->buflen);
     WriteSameCBData *data;
     uint8_t *buf;
     int i, l;
@@ -1952,7 +1957,7 @@
         return;
     }
 
-    if ((req->cmd.buf[1] & 0x1) || buffer_is_zero(inbuf, s->qdev.blocksize)) {
+    if ((req->cmd.buf[1] & 0x1) || buffer_is_zero(inbuf, buflen)) {
         int flags = (req->cmd.buf[1] & 0x8) ? BDRV_REQ_MAY_UNMAP : 0;
 
         /* The request is used as the AIO opaque value, so add a ref.  */
@@ -1978,7 +1983,7 @@
     qemu_iovec_init_external(&data->qiov, &data->iov, 1);
 
     for (i = 0; i < data->iov.iov_len; i += l) {
-        l = MIN(s->qdev.blocksize, data->iov.iov_len - i);
+        l = MIN(buflen, data->iov.iov_len - i);
         memcpy(&buf[i], inbuf, l);
     }
 
@@ -3234,6 +3239,8 @@
     DEFINE_PROP_BIT("quirk_mode_page_vendor_specific_apple", SCSIDiskState,
                     quirks, SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE,
                     0),
+    DEFINE_PROP_BIT("quirk_mode_page_set_block_size", SCSIDiskState,
+                    quirks, SCSI_DISK_QUIRK_MODE_PAGE_SET_BLOCK_SIZE, 0),
     DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf),
 };
 
@@ -3292,6 +3299,8 @@
                     0),
     DEFINE_PROP_BIT("quirk_mode_page_truncated", SCSIDiskState, quirks,
                     SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED, 0),
+    DEFINE_PROP_BIT("quirk_mode_page_set_block_size", SCSIDiskState,
+                    quirks, SCSI_DISK_QUIRK_MODE_PAGE_SET_BLOCK_SIZE, 0),
 };
 
 static void scsi_cd_class_initfn(ObjectClass *klass, void *data)
diff -Nru qemu-10.0.13+ds/hw/scsi/virtio-scsi-dataplane.c 
qemu-10.0.14+ds/hw/scsi/virtio-scsi-dataplane.c
--- qemu-10.0.13+ds/hw/scsi/virtio-scsi-dataplane.c     2026-08-26 
22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/scsi/virtio-scsi-dataplane.c     2026-09-29 
03:48:20.000000000 +0300
@@ -230,7 +230,7 @@
 fail_guest_notifiers:
     s->dataplane_fenced = true;
     s->dataplane_starting = false;
-    s->dataplane_started = true;
+    s->dataplane_started = false;
     return -ENOSYS;
 }
 
diff -Nru qemu-10.0.13+ds/hw/sd/sdhci.c qemu-10.0.14+ds/hw/sd/sdhci.c
--- qemu-10.0.13+ds/hw/sd/sdhci.c       2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/sd/sdhci.c       2026-09-29 03:48:20.000000000 +0300
@@ -1344,11 +1344,24 @@
         }
         sdhci_update_irq(s);
         break;
-    case SDHC_ACMD12ERRSTS:
+    case SDHC_ACMD12ERRSTS: {
+        uint16_t hostctl2_mask = mask >> 16;
+        uint16_t hostctl2_value = value >> 16;
+
         MASKED_WRITE(s->acmd12errsts, mask, value & UINT16_MAX);
-        if (s->uhs_mode >= UHS_I) {
-            MASKED_WRITE(s->hostctl2, mask >> 16, value >> 16);
+        if (s->uhs_mode < UHS_I) {
+            /*
+             * VERSION4 is writable even without UHS-I. Preserve all other
+             * Host Control 2 bits when UHS-I is not supported.
+             */
+            uint16_t independent = R_SDHC_HOSTCTL2_VERSION4_MASK;
 
+            hostctl2_mask |= ~independent;
+            hostctl2_value &= independent;
+        }
+        MASKED_WRITE(s->hostctl2, hostctl2_mask, hostctl2_value);
+
+        if (s->uhs_mode >= UHS_I) {
             if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, V18_ENA)) {
                 sdbus_set_voltage(&s->sdbus, SD_VOLTAGE_1_8V);
             } else {
@@ -1356,6 +1369,7 @@
             }
         }
         break;
+    }
 
     case SDHC_CAPAB:
     case SDHC_CAPAB + 4:
diff -Nru qemu-10.0.13+ds/hw/uefi/var-service-vars.c 
qemu-10.0.14+ds/hw/uefi/var-service-vars.c
--- qemu-10.0.13+ds/hw/uefi/var-service-vars.c  2026-08-26 22:31:25.000000000 
+0300
+++ qemu-10.0.14+ds/hw/uefi/var-service-vars.c  2026-09-29 03:48:20.000000000 
+0300
@@ -652,6 +652,10 @@
         return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE);
     }
 
+    if (!uefi_str_is_valid(name, lv->name_size, true)) {
+        return uefi_vars_mm_error(mhdr, mvar, EFI_INVALID_PARAMETER);
+    }
+
     uefi_trace_variable(__func__, lv->guid, name, lv->name_size);
 
     pe = g_malloc0(sizeof(*pe) + lv->name_size);
diff -Nru qemu-10.0.13+ds/hw/usb/hcd-xhci.c qemu-10.0.14+ds/hw/usb/hcd-xhci.c
--- qemu-10.0.13+ds/hw/usb/hcd-xhci.c   2026-08-26 22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/hw/usb/hcd-xhci.c   2026-09-29 03:48:20.000000000 +0300
@@ -458,9 +458,15 @@
 {
     XHCIState *xhci = opaque;
     XHCIEvent wrap = { ER_MFINDEX_WRAP, CC_SUCCESS };
+    MemReentrancyGuard *guard = &xhci->parent.mem_reentrancy_guard;
+
+    assert(!guard->engaged_in_io);
+    guard->engaged_in_io = true;
 
     xhci_event(xhci, &wrap, 0);
     xhci_mfwrap_update(xhci);
+
+    guard->engaged_in_io = false;
 }
 
 static void xhci_die(XHCIState *xhci)
@@ -1087,7 +1093,14 @@
 static void xhci_ep_kick_timer(void *opaque)
 {
     XHCIEPContext *epctx = opaque;
+    MemReentrancyGuard *guard = &epctx->xhci->parent.mem_reentrancy_guard;
+
+    assert(!guard->engaged_in_io);
+    guard->engaged_in_io = true;
+
     xhci_kick_epctx(epctx, 0);
+
+    guard->engaged_in_io = false;
 }
 
 static XHCIEPContext *xhci_alloc_epctx(XHCIState *xhci,
@@ -1744,8 +1757,7 @@
 static void xhci_calc_intr_kick(XHCIState *xhci, XHCITransfer *xfer,
                                 XHCIEPContext *epctx, uint64_t mfindex)
 {
-    uint64_t asap = ((mfindex + epctx->interval - 1) &
-                     ~(epctx->interval-1));
+    uint64_t asap = ROUND_UP(mfindex, epctx->interval);
     uint64_t kick = epctx->mfindex_last + epctx->interval;
 
     assert(epctx->interval != 0);
@@ -1756,8 +1768,7 @@
                                XHCIEPContext *epctx, uint64_t mfindex)
 {
     if (xfer->trbs[0].control & TRB_TR_SIA) {
-        uint64_t asap = ((mfindex + epctx->interval - 1) &
-                         ~(epctx->interval-1));
+        uint64_t asap = ROUND_UP(mfindex, epctx->interval);
         if (asap >= epctx->mfindex_last &&
             asap <= epctx->mfindex_last + epctx->interval * 4) {
             xfer->mfindex_kick = epctx->mfindex_last + epctx->interval;
@@ -1916,26 +1927,15 @@
             xfer->timed_xfer = 0;
             xfer->running_retry = 1;
         }
-        if (xfer->iso_xfer) {
-            /* retry iso transfer */
-            if (xhci_setup_packet(xfer) < 0) {
-                return;
-            }
-            usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
-            assert(xfer->packet.status != USB_RET_NAK);
-            xhci_try_complete_packet(xfer);
-        } else {
-            /* retry nak'ed transfer */
-            if (xhci_setup_packet(xfer) < 0) {
-                return;
-            }
-            usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
-            if (xfer->packet.status == USB_RET_NAK) {
-                xhci_xfer_unmap(xfer);
-                return;
-            }
-            xhci_try_complete_packet(xfer);
+        if (xhci_setup_packet(xfer) < 0) {
+            return;
+        }
+        usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+        if (xfer->packet.status == USB_RET_NAK) {
+            xhci_xfer_unmap(xfer);
+            return;
         }
+        xhci_try_complete_packet(xfer);
         assert(!xfer->running_retry);
         if (xfer->complete) {
             /* update ring dequeue ptr */
diff -Nru qemu-10.0.13+ds/hw/virtio/virtio-balloon.c 
qemu-10.0.14+ds/hw/virtio/virtio-balloon.c
--- qemu-10.0.13+ds/hw/virtio/virtio-balloon.c  2026-08-26 22:31:25.000000000 
+0300
+++ qemu-10.0.14+ds/hw/virtio/virtio-balloon.c  2026-09-29 03:48:20.000000000 
+0300
@@ -34,6 +34,7 @@
 #include "system/reset.h"
 #include "hw/virtio/virtio-bus.h"
 #include "hw/virtio/virtio-access.h"
+#include "block/aio-wait.h"
 
 #define BALLOON_PAGE_SIZE  (1 << VIRTIO_BALLOON_PFN_SHIFT)
 
@@ -518,6 +519,9 @@
     int i;
 
     while (dev->block_iothread) {
+        if (dev->free_page_hint_status == FREE_PAGE_HINT_S_UNREALIZE) {
+            return false;
+        }
         qemu_cond_wait(&dev->free_page_cond, &dev->free_page_lock);
     }
 
@@ -914,6 +918,11 @@
     qemu_register_resettable(OBJECT(dev));
 }
 
+static void dummy_bh(void *opaque)
+{
+    /* Do nothing */
+}
+
 static void virtio_balloon_device_unrealize(DeviceState *dev)
 {
     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
@@ -921,9 +930,17 @@
 
     qemu_unregister_resettable(OBJECT(dev));
     if (s->free_page_bh) {
+        AioContext *ctx = iothread_get_aio_context(s->iothread);
+
         qemu_bh_delete(s->free_page_bh);
+
+        qemu_mutex_lock(&s->free_page_lock);
+        s->free_page_hint_status = FREE_PAGE_HINT_S_UNREALIZE;
+        qemu_cond_signal(&s->free_page_cond);
+        qemu_mutex_unlock(&s->free_page_lock);
+        aio_wait_bh_oneshot(ctx, dummy_bh, NULL);
+
         object_unref(OBJECT(s->iothread));
-        virtio_balloon_free_page_stop(s);
         precopy_remove_notifier(&s->free_page_hint_notify);
     }
     balloon_stats_destroy_timer(s);
diff -Nru qemu-10.0.13+ds/hw/virtio/virtio-iommu.c 
qemu-10.0.14+ds/hw/virtio/virtio-iommu.c
--- qemu-10.0.13+ds/hw/virtio/virtio-iommu.c    2026-08-26 22:31:25.000000000 
+0300
+++ qemu-10.0.14+ds/hw/virtio/virtio-iommu.c    2026-09-29 03:48:20.000000000 
+0300
@@ -213,6 +213,10 @@
 {
     uint64_t delta = virt_end - virt_start;
 
+    if (virt_end < virt_start) {
+        return;
+    }
+
     event->entry.iova = virt_start;
     event->entry.addr_mask = delta;
 
@@ -808,6 +812,10 @@
         return VIRTIO_IOMMU_S_INVAL;
     }
 
+    if (virt_end < virt_start) {
+        return VIRTIO_IOMMU_S_INVAL;
+    }
+
     domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id));
     if (!domain) {
         return VIRTIO_IOMMU_S_NOENT;
@@ -858,6 +866,10 @@
 
     trace_virtio_iommu_unmap(domain_id, virt_start, virt_end);
 
+    if (virt_end < virt_start) {
+        return VIRTIO_IOMMU_S_INVAL;
+    }
+
     domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id));
     if (!domain) {
         return VIRTIO_IOMMU_S_NOENT;
@@ -880,7 +892,10 @@
                 virtio_iommu_notify_unmap(ep->iommu_mr, current_low,
                                           current_high);
             }
-            g_tree_remove(domain->mappings, iter_key);
+            if (!g_tree_remove(domain->mappings, iter_key)) {
+                ret = VIRTIO_IOMMU_S_DEVERR;
+                break;
+            }
             trace_virtio_iommu_unmap_done(domain_id, current_low, 
current_high);
         } else {
             ret = VIRTIO_IOMMU_S_RANGE;
diff -Nru qemu-10.0.13+ds/include/exec/translation-block.h 
qemu-10.0.14+ds/include/exec/translation-block.h
--- qemu-10.0.13+ds/include/exec/translation-block.h    2026-08-26 
22:31:25.000000000 +0300
+++ qemu-10.0.14+ds/include/exec/translation-block.h    2026-09-29 
03:48:20.000000000 +0300
@@ -80,6 +80,7 @@
 #define CF_NOIRQ         0x00010000 /* Generate an uninterruptible TB */
 #define CF_PCREL         0x00020000 /* Opcodes in TB are PC-relative */
 #define CF_BP_PAGE       0x00040000 /* Breakpoint present in code page */
+#define CF_STEP_ATOMIC   0x00080000 /* Running in cpu_exec_step_atomic */
 #define CF_CLUSTER_MASK  0xff000000 /* Top 8 bits are cluster ID */
 #define CF_CLUSTER_SHIFT 24
 
diff -Nru qemu-10.0.13+ds/include/hw/scsi/scsi.h 
qemu-10.0.14+ds/include/hw/scsi/scsi.h
--- qemu-10.0.13+ds/include/hw/scsi/scsi.h      2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/include/hw/scsi/scsi.h      2026-09-29 03:48:20.000000000 
+0300
@@ -250,5 +250,6 @@
 #define SCSI_DISK_QUIRK_MODE_SENSE_ROM_USE_DBD             1
 #define SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE    2
 #define SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED                3
+#define SCSI_DISK_QUIRK_MODE_PAGE_SET_BLOCK_SIZE           4
 
 #endif
diff -Nru qemu-10.0.13+ds/include/hw/virtio/virtio-balloon.h 
qemu-10.0.14+ds/include/hw/virtio/virtio-balloon.h
--- qemu-10.0.13+ds/include/hw/virtio/virtio-balloon.h  2026-08-26 
22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/include/hw/virtio/virtio-balloon.h  2026-09-29 
03:48:20.000000000 +0300
@@ -39,6 +39,7 @@
     FREE_PAGE_HINT_S_REQUESTED = 1,
     FREE_PAGE_HINT_S_START = 2,
     FREE_PAGE_HINT_S_DONE = 3,
+    FREE_PAGE_HINT_S_UNREALIZE = 4,
 };
 
 struct VirtIOBalloon {
diff -Nru qemu-10.0.13+ds/include/ui/console.h 
qemu-10.0.14+ds/include/ui/console.h
--- qemu-10.0.13+ds/include/ui/console.h        2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/include/ui/console.h        2026-09-29 03:48:20.000000000 
+0300
@@ -164,6 +164,15 @@
 } QEMUCursor;
 
 QEMUCursor *cursor_alloc(uint16_t width, uint16_t height);
+
+/*
+ * A cursor may be shared between the main loop, a vCPU thread and a
+ * display backend's own thread, so the refcount is atomic and these two
+ * may be called from any of them.  The object itself is not otherwise
+ * thread-safe: take a reference before publishing the pointer anywhere
+ * another thread can reach it, and never dereference a cursor you do
+ * not hold a reference to.
+ */
 QEMUCursor *cursor_ref(QEMUCursor *c);
 void cursor_unref(QEMUCursor *c);
 QEMUCursor *cursor_builtin_hidden(void);
diff -Nru qemu-10.0.13+ds/io/channel-socket.c 
qemu-10.0.14+ds/io/channel-socket.c
--- qemu-10.0.13+ds/io/channel-socket.c 2026-08-26 22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/io/channel-socket.c 2026-09-29 03:48:20.000000000 +0300
@@ -604,7 +604,7 @@
 
  retry:
     ret = sendmsg(sioc->fd, &msg, sflags);
-    if (ret <= 0) {
+    if (ret < 0) {
         switch (errno) {
         case EAGAIN:
             return QIO_CHANNEL_ERR_BLOCK;
diff -Nru qemu-10.0.13+ds/io/channel-websock.c 
qemu-10.0.14+ds/io/channel-websock.c
--- qemu-10.0.13+ds/io/channel-websock.c        2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/io/channel-websock.c        2026-09-29 03:48:20.000000000 
+0300
@@ -230,7 +230,7 @@
     tmp = strchr(buffer, ' ');
     if (!tmp) {
         error_setg(errp, "Missing HTTP path delimiter");
-        return 0;
+        goto bad_request;
     }
     *tmp = '\0';
 
@@ -492,6 +492,9 @@
     buffer_reserve(&ioc->encinput, want);
     ret = qio_channel_read(ioc->master,
                            (char *)buffer_end(&ioc->encinput), want, errp);
+    if (ret == QIO_CHANNEL_ERR_BLOCK) {
+        return 0;
+    }
     if (ret < 0) {
         return -1;
     }
@@ -562,6 +565,11 @@
                             wioc->encoutput.offset,
                             &err);
 
+    if (ret == QIO_CHANNEL_ERR_BLOCK) {
+        /* Socket buffer is full, the G_IO_OUT watch stays armed */
+        return TRUE;
+    }
+
     if (ret < 0) {
         trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
         qio_task_set_error(task, err);
diff -Nru qemu-10.0.13+ds/job.c qemu-10.0.14+ds/job.c
--- qemu-10.0.13+ds/job.c       2026-08-26 22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/job.c       2026-09-29 03:48:20.000000000 +0300
@@ -629,7 +629,14 @@
                                     ? JOB_STATUS_STANDBY
                                     : JOB_STATUS_PAUSED);
         job->paused = true;
-        job_do_yield_locked(job, -1);
+        /*
+         * Stay paused across back-to-back pause requests: a transient
+         * paused == false while pause_count > 0 would be observed as
+         * "not paused" by job_set_aio_context() and other drain consumers.
+         */
+        do {
+            job_do_yield_locked(job, -1);
+        } while (job_should_pause_locked(job) && 
!job_is_cancelled_locked(job));
         job->paused = false;
         job_state_transition_locked(job, status);
     }
diff -Nru qemu-10.0.13+ds/linux-user/include/host/loongarch64/host-signal.h 
qemu-10.0.14+ds/linux-user/include/host/loongarch64/host-signal.h
--- qemu-10.0.13+ds/linux-user/include/host/loongarch64/host-signal.h   
2026-08-26 22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/linux-user/include/host/loongarch64/host-signal.h   
2026-09-29 03:48:20.000000000 +0300
@@ -61,6 +61,10 @@
             return true;
         }
         break;
+    case 0b001011: /* v{ld,st}, xv{ld,st} */
+        return (insn >> 22) & 1;
+    case 0b001100: /* v{ldrepl,stelm}, xv{ldrepl,stelm} */
+        return (insn >> 24) & 1;
     case 0b001110: /* indexed, atomic, bounds-checking memory operations */
         switch ((insn >> 15) & 0b11111111111) {
         case 0b00000100000: /* stx.b */
@@ -69,6 +73,8 @@
         case 0b00000111000: /* stx.d */
         case 0b00001110000: /* fstx.s */
         case 0b00001111000: /* fstx.d */
+        case 0b00010001000: /* vstx */
+        case 0b00010011000: /* xvstx */
         case 0b00011101100: /* fstgt.s */
         case 0b00011101101: /* fstgt.d */
         case 0b00011101110: /* fstle.s */
diff -Nru qemu-10.0.13+ds/linux-user/syscall.c 
qemu-10.0.14+ds/linux-user/syscall.c
--- qemu-10.0.13+ds/linux-user/syscall.c        2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/linux-user/syscall.c        2026-09-29 03:48:20.000000000 
+0300
@@ -10941,6 +10941,15 @@
     case TARGET_NR_mlock:
         return get_errno(mlock(g2h(cpu, arg1), arg2));
 #endif
+#ifdef TARGET_NR_mlock2
+    case TARGET_NR_mlock2:
+        if (arg3 & ~TARGET_MLOCK_ONFAULT) {
+            return -TARGET_EINVAL;
+        }
+        return get_errno(mlock2(g2h(cpu, arg1), arg2,
+                                 (arg3 & TARGET_MLOCK_ONFAULT) ?
+                                 MLOCK_ONFAULT : 0));
+#endif
 #ifdef TARGET_NR_munlock
     case TARGET_NR_munlock:
         return get_errno(munlock(g2h(cpu, arg1), arg2));
diff -Nru qemu-10.0.13+ds/linux-user/syscall_defs.h 
qemu-10.0.14+ds/linux-user/syscall_defs.h
--- qemu-10.0.13+ds/linux-user/syscall_defs.h   2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/linux-user/syscall_defs.h   2026-09-29 03:48:20.000000000 
+0300
@@ -2792,6 +2792,10 @@
 #ifndef RESOLVE_IN_ROOT
 #define RESOLVE_IN_ROOT         0x10
 #endif
+
+/* flags for mlock2() */
+#define TARGET_MLOCK_ONFAULT    0x01
+
 #if (defined(TARGET_I386) && defined(TARGET_ABI32)) || \
     (defined(TARGET_ARM) && defined(TARGET_ABI32)) || \
     defined(TARGET_M68K) || defined(TARGET_MICROBLAZE) || \
diff -Nru qemu-10.0.13+ds/migration/block-dirty-bitmap.c 
qemu-10.0.14+ds/migration/block-dirty-bitmap.c
--- qemu-10.0.13+ds/migration/block-dirty-bitmap.c      2026-08-26 
22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/migration/block-dirty-bitmap.c      2026-09-29 
03:48:20.000000000 +0300
@@ -808,13 +808,6 @@
         error_report("Bitmap with the same name ('%s') already exists on "
                      "destination", bdrv_dirty_bitmap_name(s->bitmap));
         return -EINVAL;
-    } else {
-        s->bitmap = bdrv_create_dirty_bitmap(s->bs, granularity,
-                                             s->bitmap_name, &local_err);
-        if (!s->bitmap) {
-            error_report_err(local_err);
-            return -EINVAL;
-        }
     }
 
     if (flags & DIRTY_BITMAP_MIG_START_FLAG_RESERVED_MASK) {
@@ -831,6 +824,21 @@
         persistent = flags & DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT;
     }
 
+    /* Not bdrv_is_writable(): nodes stay inactive until migration ends. */
+    if (persistent && bdrv_is_read_only(s->bs)) {
+        error_report("Cannot make migrated bitmap '%s' persistent "
+                     "on read-only node '%s'", s->bitmap_name,
+                     bdrv_get_node_name(s->bs));
+        return -EINVAL;
+    }
+
+    s->bitmap = bdrv_create_dirty_bitmap(s->bs, granularity,
+                                         s->bitmap_name, &local_err);
+    if (!s->bitmap) {
+        error_report_err(local_err);
+        return -EINVAL;
+    }
+
     if (persistent) {
         bdrv_dirty_bitmap_set_persistence(s->bitmap, true);
     }
diff -Nru qemu-10.0.13+ds/qapi/block-core.json 
qemu-10.0.14+ds/qapi/block-core.json
--- qemu-10.0.13+ds/qapi/block-core.json        2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/qapi/block-core.json        2026-09-29 03:48:20.000000000 
+0300
@@ -2263,7 +2263,9 @@
 # @persistent: the bitmap is persistent, i.e. it will be saved to the
 #     corresponding block device image file on its close.  For now
 #     only Qcow2 disks support persistent bitmaps.  Default is false
-#     for block-dirty-bitmap-add.  (Since: 2.10)
+#     for block-dirty-bitmap-add.  This fails if the node is
+#     read-only or inactive, since such a bitmap could never be
+#     stored.  (Since: 2.10)
 #
 # @disabled: the bitmap is created in the disabled state, which means
 #     that it will not track drive changes.  The bitmap may be enabled
diff -Nru qemu-10.0.13+ds/qapi/misc.json qemu-10.0.14+ds/qapi/misc.json
--- qemu-10.0.13+ds/qapi/misc.json      2026-08-26 22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/qapi/misc.json      2026-09-29 03:48:20.000000000 +0300
@@ -117,11 +117,19 @@
 #     <- { "return": [
 #              {
 #                 "id":"iothread0",
-#                 "thread-id":3134
+#                 "thread-id":3134,
+#                 "poll-max-ns":32768,
+#                 "poll-grow":0,
+#                 "poll-shrink":0,
+#                 "aio-max-batch":0
 #              },
 #              {
 #                 "id":"iothread1",
-#                 "thread-id":3135
+#                 "thread-id":3135,
+#                 "poll-max-ns":32768,
+#                 "poll-grow":0,
+#                 "poll-shrink":0,
+#                 "aio-max-batch":0
 #              }
 #           ]
 #        }
diff -Nru qemu-10.0.13+ds/qga/commands-posix-ssh.c 
qemu-10.0.14+ds/qga/commands-posix-ssh.c
--- qemu-10.0.13+ds/qga/commands-posix-ssh.c    2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/qga/commands-posix-ssh.c    2026-09-29 03:48:20.000000000 
+0300
@@ -8,11 +8,34 @@
 #include <glib/gstdio.h>
 #include <locale.h>
 #include <pwd.h>
+#include <grp.h>
 
 #include "commands-common-ssh.h"
 #include "qapi/error.h"
 #include "qga-qapi-commands.h"
 
+typedef struct EffectiveUserInfo {
+    uid_t uid;
+    gid_t gid;
+} EffectiveUserInfo;
+
+typedef EffectiveUserInfo *PEffectiveUserInfo;
+
+static void rollback_effective_info(PEffectiveUserInfo info)
+{
+    if (info) {
+        /* There is nothing to do in case  when rollback to original 
user/group IDs
+         * fails. In that case, the process will be terminated by the kernel
+         * and systemd should restart the daemon again.
+         */
+        assert(seteuid(info->uid) == 0);
+        assert(setegid(info->gid) == 0);
+        g_free(info);
+    }
+}
+
+G_DEFINE_AUTO_CLEANUP_FREE_FUNC(PEffectiveUserInfo, rollback_effective_info, 
NULL);
+
 #ifdef QGA_BUILD_UNIT_TEST
 static struct passwd *
 test_get_passwd_entry(const gchar *user_name, GError **error)
@@ -109,6 +132,36 @@
     return true;
 }
 
+static PEffectiveUserInfo set_privileges_to_user(const struct passwd *p, Error 
**errp)
+{
+    g_auto(PEffectiveUserInfo) info = g_new0(EffectiveUserInfo, 1);
+
+    info->uid = geteuid();
+    info->gid = getegid();
+
+#ifndef QGA_BUILD_UNIT_TEST
+    /* The initgroups requires CAP_SETGID. During build time unit tests, we 
can't do this. */
+    if (initgroups(p->pw_name, p->pw_gid) == -1) {
+        error_setg_errno(errp, errno, "failed to set group for user '%s'",
+                         p->pw_name);
+        return NULL;
+    }
+#endif
+
+    if (setegid(p->pw_gid) == -1) {
+        error_setg_errno(errp, errno, "failed to set effective group ID for 
user '%s'",
+                         p->pw_name);
+        return NULL;
+    }
+    if (seteuid(p->pw_uid) == -1) {
+        error_setg_errno(errp, errno, "failed to set effective user ID for 
user '%s'",
+                         p->pw_name);
+        return NULL;
+    }
+
+    return g_steal_pointer(&info);
+}
+
 void
 qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys,
                                   bool has_reset, bool reset,
@@ -120,6 +173,7 @@
     g_auto(GStrv) authkeys = NULL;
     strList *k;
     size_t nkeys, nauthkeys;
+    g_auto(PEffectiveUserInfo) effective_user_info = NULL;
 
     reset = has_reset && reset;
 
@@ -132,6 +186,11 @@
         return;
     }
 
+    effective_user_info = set_privileges_to_user(p, errp);
+    if (effective_user_info == NULL) {
+        return;
+    }
+
     ssh_path = g_build_filename(p->pw_dir, ".ssh", NULL);
     authkeys_path = g_build_filename(ssh_path, "authorized_keys", NULL);
 
@@ -169,6 +228,7 @@
     g_auto(GStrv) authkeys = NULL;
     GStrv a;
     size_t nkeys = 0;
+    g_auto(PEffectiveUserInfo) effective_user_info = NULL;
 
     if (!check_openssh_pub_keys(keys, NULL, errp)) {
         return;
@@ -179,6 +239,11 @@
         return;
     }
 
+    effective_user_info = set_privileges_to_user(p, errp);
+    if (effective_user_info == NULL) {
+        return;
+    }
+
     authkeys_path = g_build_filename(p->pw_dir, ".ssh",
                                      "authorized_keys", NULL);
     if (!g_file_test(authkeys_path, G_FILE_TEST_EXISTS)) {
@@ -216,12 +281,18 @@
     g_auto(GStrv) authkeys = NULL;
     g_autoptr(GuestAuthorizedKeys) ret = NULL;
     int i;
+    g_auto(PEffectiveUserInfo) effective_user_info = NULL;
 
     p = get_passwd_entry(username, errp);
     if (p == NULL) {
         return NULL;
     }
 
+    effective_user_info = set_privileges_to_user(p, errp);
+    if (effective_user_info == NULL) {
+        return NULL;
+    }
+
     authkeys_path = g_build_filename(p->pw_dir, ".ssh",
                                      "authorized_keys", NULL);
     authkeys = read_authkeys(authkeys_path, errp);
diff -Nru qemu-10.0.13+ds/target/arm/tcg/cpu32.c 
qemu-10.0.14+ds/target/arm/tcg/cpu32.c
--- qemu-10.0.13+ds/target/arm/tcg/cpu32.c      2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/target/arm/tcg/cpu32.c      2026-09-29 03:48:20.000000000 
+0300
@@ -134,7 +134,7 @@
     cpu->midr = 0x41069265;
     cpu->reset_fpsid = 0x41011090;
     cpu->ctr = 0x1dd20d2;
-    cpu->reset_sctlr = 0x00090078;
+    cpu->reset_sctlr = 0x00050078;
 
     /*
      * ARMv5 does not have the ID_ISAR registers, but we can still
@@ -175,7 +175,7 @@
     cpu->midr = 0x4106a262;
     cpu->reset_fpsid = 0x410110a0;
     cpu->ctr = 0x1dd20d2;
-    cpu->reset_sctlr = 0x00090078;
+    cpu->reset_sctlr = 0x00050078;
     cpu->reset_auxcr = 1;
 
     /*
diff -Nru qemu-10.0.13+ds/target/arm/tcg/m_helper.c 
qemu-10.0.14+ds/target/arm/tcg/m_helper.c
--- qemu-10.0.13+ds/target/arm/tcg/m_helper.c   2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/target/arm/tcg/m_helper.c   2026-09-29 03:48:20.000000000 
+0300
@@ -2839,8 +2839,9 @@
     }
 
     if (env->v7m.secure) {
+        /* Note that security check is done as Secure even if alt is true */
         v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx,
-                            targetsec, &sattrs);
+                            env->v7m.secure, &sattrs);
         nsr = sattrs.ns && r;
         nsrw = sattrs.ns && rw;
     } else {
diff -Nru qemu-10.0.13+ds/target/arm/tcg/t16.decode 
qemu-10.0.14+ds/target/arm/tcg/t16.decode
--- qemu-10.0.13+ds/target/arm/tcg/t16.decode   2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/target/arm/tcg/t16.decode   2026-09-29 03:48:20.000000000 
+0300
@@ -224,6 +224,9 @@
 
 {
   {
+    # Before v6T2 this was not NOP space and must UNDEF
+    MAYBE_UNDEF_T1_HINT 1011 1111 ---- 0000
+
     YIELD       1011 1111 0001 0000
     WFE         1011 1111 0010 0000
     WFI         1011 1111 0011 0000
diff -Nru qemu-10.0.13+ds/target/arm/tcg/translate.c 
qemu-10.0.14+ds/target/arm/tcg/translate.c
--- qemu-10.0.13+ds/target/arm/tcg/translate.c  2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/target/arm/tcg/translate.c  2026-09-29 03:48:20.000000000 
+0300
@@ -1,3 +1,4 @@
+
 /*
  *  ARM translation
  *
@@ -4546,6 +4547,24 @@
     return true;
 }
 
+static bool trans_MAYBE_UNDEF_T1_HINT(DisasContext *s,
+                                      arg_MAYBE_UNDEF_T1_HINT *a)
+{
+    /*
+     * The Thumb T1 encoding hint space was only defined starting
+     * in v6T2 for A-profile. For M-profile it always exists, even
+     * in v6M.
+     */
+    if (arm_dc_feature(s, ARM_FEATURE_M) ||
+        arm_dc_feature(s, ARM_FEATURE_THUMB2)) {
+        /* Allow decode to fall through to the hint insns and NOP space */
+        return false;
+    }
+    /* On the earlier cores, we must UNDEF */
+    unallocated_encoding(s);
+    return true;
+}
+
 static bool trans_MSR_imm(DisasContext *s, arg_MSR_imm *a)
 {
     uint32_t val = ror32(a->imm, a->rot * 2);
@@ -6991,7 +7010,14 @@
 
 static bool trans_CBZ(DisasContext *s, arg_CBZ *a)
 {
-    TCGv_i32 tmp = load_reg(s, a->rn);
+    TCGv_i32 tmp;
+
+    /* CBZ was introduced in v6T2 and v7M */
+    if (!arm_dc_feature(s, ARM_FEATURE_THUMB2)) {
+        return false;
+    }
+
+    tmp = load_reg(s, a->rn);
 
     arm_gen_condlabel(s);
     tcg_gen_brcondi_i32(a->nz ? TCG_COND_EQ : TCG_COND_NE,
@@ -7240,6 +7266,16 @@
     int cond_mask = a->cond_mask;
 
     /*
+     * IT insn introduced in v6T2 for A-profile; it is only present
+     * on M-profile if the Main Extension is implemented.
+     */
+    if (!(arm_dc_feature(s, ARM_FEATURE_M)
+          ? arm_dc_feature(s, ARM_FEATURE_M_MAIN)
+          : arm_dc_feature(s, ARM_FEATURE_THUMB2))) {
+        return false;
+    }
+
+    /*
      * No actual code generated for this insn, just setup state.
      *
      * Combinations of firstcond and mask which set up an 0b1111
diff -Nru qemu-10.0.13+ds/target/i386/tcg/decode-new.c.inc 
qemu-10.0.14+ds/target/i386/tcg/decode-new.c.inc
--- qemu-10.0.13+ds/target/i386/tcg/decode-new.c.inc    2026-08-26 
22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/target/i386/tcg/decode-new.c.inc    2026-09-29 
03:48:20.000000000 +0300
@@ -1265,7 +1265,7 @@
     [0xc0] = X86_OP_ENTRY2(XADD,       E,b, G,b,            lock),
     [0xc1] = X86_OP_ENTRY2(XADD,       E,v, G,v,            lock),
     [0xc2] = X86_OP_ENTRY4(VCMP,       V,x, H,x, W,x,       vex2_rep3 
p_00_66_f3_f2),
-    [0xc3] = X86_OP_ENTRY3(MOV,        EM,y,G,y, None,None, cpuid(SSE2)), /* 
MOVNTI */
+    [0xc3] = X86_OP_ENTRY3(MOV,        EM,y,G,y, None,None, cpuid(SSE2) p_00), 
/* MOVNTI */
     [0xc4] = X86_OP_ENTRY4(PINSRW,     V,dq,H,dq,E,w,       vex5 mmx p_00_66),
     [0xc5] = X86_OP_ENTRY3(PEXTRW,     G,d, U,dq,I,b,       vex5 mmx p_00_66),
     [0xc6] = X86_OP_ENTRY4(VSHUF,      V,x, H,x, W,x,       vex4 p_00_66),
diff -Nru qemu-10.0.13+ds/target/i386/tcg/fpu_helper.c 
qemu-10.0.14+ds/target/i386/tcg/fpu_helper.c
--- qemu-10.0.13+ds/target/i386/tcg/fpu_helper.c        2026-08-26 
22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/target/i386/tcg/fpu_helper.c        2026-09-29 
03:48:20.000000000 +0300
@@ -510,6 +510,7 @@
 void helper_fmov_STN_ST0(CPUX86State *env, int st_index)
 {
     ST(st_index) = ST0;
+    env->fptags[(env->fpstt + st_index) & 7] = 0;
 }
 
 void helper_fxchg_ST0_STN(CPUX86State *env, int st_index)
@@ -519,6 +520,12 @@
     tmp = ST(st_index);
     ST(st_index) = ST0;
     ST0 = tmp;
+
+    env->fptags[env->fpstt] = 0;
+    env->fptags[(env->fpstt + st_index) & 7] = 0;
+
+    /* C1 is unconditionally cleared to 0 */
+    env->fpus &= ~0x0200;
 }
 
 /* FPU operations */
@@ -1804,6 +1811,13 @@
     merge_exception_flags(env, old_flags);
 }
 
+/* fpush() only validates the new top. FXTRACT also needs ST(1) validated. */
+static inline void fpush_fxtract(CPUX86State *env)
+{
+    fpush(env);
+    env->fptags[(env->fpstt + 1) & 7] = 0;
+}
+
 void helper_fxtract(CPUX86State *env)
 {
     int old_flags = save_exception_flags(env);
@@ -1815,22 +1829,22 @@
         /* Easy way to generate -inf and raising division by 0 exception */
         ST0 = floatx80_div(floatx80_chs(floatx80_one), floatx80_zero,
                            &env->fp_status);
-        fpush(env);
+        fpush_fxtract(env);
         ST0 = temp.d;
     } else if (floatx80_invalid_encoding(ST0, &env->fp_status)) {
         float_raise(float_flag_invalid, &env->fp_status);
         ST0 = floatx80_default_nan(&env->fp_status);
-        fpush(env);
+        fpush_fxtract(env);
         ST0 = ST1;
     } else if (floatx80_is_any_nan(ST0)) {
         if (floatx80_is_signaling_nan(ST0, &env->fp_status)) {
             float_raise(float_flag_invalid, &env->fp_status);
             ST0 = floatx80_silence_nan(ST0, &env->fp_status);
         }
-        fpush(env);
+        fpush_fxtract(env);
         ST0 = ST1;
     } else if (floatx80_is_infinity(ST0, &env->fp_status)) {
-        fpush(env);
+        fpush_fxtract(env);
         ST0 = ST1;
         ST1 = floatx80_default_inf(0, &env->fp_status);
     } else {
@@ -1846,7 +1860,7 @@
         }
         /* DP exponent bias */
         ST0 = int32_to_floatx80(expdif, &env->fp_status);
-        fpush(env);
+        fpush_fxtract(env);
         BIASEXPONENT(temp);
         ST0 = temp.d;
     }
diff -Nru qemu-10.0.13+ds/target/ppc/cpu_init.c 
qemu-10.0.14+ds/target/ppc/cpu_init.c
--- qemu-10.0.13+ds/target/ppc/cpu_init.c       2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/target/ppc/cpu_init.c       2026-09-29 03:48:20.000000000 
+0300
@@ -6969,10 +6969,10 @@
     PowerPCCPU *cpu = POWERPC_CPU(dev);
     PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
 
-    pcc->parent_unrealize(dev);
-
     cpu_remove_sync(CPU(cpu));
 
+    pcc->parent_unrealize(dev);
+
     destroy_ppc_opcodes(cpu);
 }
 
diff -Nru qemu-10.0.13+ds/target/riscv/cpu.c qemu-10.0.14+ds/target/riscv/cpu.c
--- qemu-10.0.13+ds/target/riscv/cpu.c  2026-08-26 22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/target/riscv/cpu.c  2026-09-29 03:48:20.000000000 +0300
@@ -646,6 +646,7 @@
     cpu->cfg.ext_zba = true;
     cpu->cfg.ext_zbb = true;
     cpu->cfg.ext_zbs = true;
+    cpu->cfg.ext_zkr = true;
     cpu->cfg.ext_zkt = true;
     cpu->cfg.ext_zvbb = true;
     cpu->cfg.ext_zvbc = true;
diff -Nru qemu-10.0.13+ds/target/riscv/cpu_bits.h 
qemu-10.0.14+ds/target/riscv/cpu_bits.h
--- qemu-10.0.13+ds/target/riscv/cpu_bits.h     2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/target/riscv/cpu_bits.h     2026-09-29 03:48:20.000000000 
+0300
@@ -1023,11 +1023,11 @@
     (HVICTL_VTI | HVICTL_IID | HVICTL_IPRIOM | HVICTL_IPRIO)
 
 /* seed CSR bits */
-#define SEED_OPST                        (0b11 << 30)
-#define SEED_OPST_BIST                   (0b00 << 30)
-#define SEED_OPST_WAIT                   (0b01 << 30)
-#define SEED_OPST_ES16                   (0b10 << 30)
-#define SEED_OPST_DEAD                   (0b11 << 30)
+#define SEED_OPST                        (0b11U << 30)
+#define SEED_OPST_BIST                   (0b00U << 30)
+#define SEED_OPST_WAIT                   (0b01U << 30)
+#define SEED_OPST_ES16                   (0b10U << 30)
+#define SEED_OPST_DEAD                   (0b11U << 30)
 /* PMU related bits */
 #define MIE_LCOFIE                         (1 << IRQ_PMU_OVF)
 
diff -Nru qemu-10.0.13+ds/target/riscv/csr.c qemu-10.0.14+ds/target/riscv/csr.c
--- qemu-10.0.13+ds/target/riscv/csr.c  2026-08-26 22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/target/riscv/csr.c  2026-09-29 03:48:20.000000000 +0300
@@ -107,6 +107,13 @@
 
 static RISCVException ctr(CPURISCVState *env, int csrno)
 {
+    if ((csrno >= CSR_CYCLE && csrno <= CSR_INSTRET) ||
+        (csrno >= CSR_CYCLEH && csrno <= CSR_INSTRETH)) {
+        if (!riscv_cpu_cfg(env)->ext_zicntr) {
+            return RISCV_EXCP_ILLEGAL_INST;
+        }
+    }
+
 #if !defined(CONFIG_USER_ONLY)
     RISCVCPU *cpu = env_archcpu(env);
     int ctr_index;
@@ -123,10 +130,6 @@
 
     if ((csrno >= CSR_CYCLE && csrno <= CSR_INSTRET) ||
         (csrno >= CSR_CYCLEH && csrno <= CSR_INSTRETH)) {
-        if (!riscv_cpu_cfg(env)->ext_zicntr) {
-            return RISCV_EXCP_ILLEGAL_INST;
-        }
-
         goto skip_ext_pmu_check;
     }
 
diff -Nru qemu-10.0.13+ds/target/sh4/cpu.h qemu-10.0.14+ds/target/sh4/cpu.h
--- qemu-10.0.13+ds/target/sh4/cpu.h    2026-08-26 22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/target/sh4/cpu.h    2026-09-29 03:48:20.000000000 +0300
@@ -83,8 +83,7 @@
 #define TB_FLAG_DELAY_SLOT_RTE   (1 << 2)
 #define TB_FLAG_PENDING_MOVCA    (1 << 3)
 #define TB_FLAG_GUSA_SHIFT       4                      /* [11:4] */
-#define TB_FLAG_GUSA_EXCLUSIVE   (1 << 12)
-#define TB_FLAG_UNALIGN          (1 << 13)
+#define TB_FLAG_UNALIGN          (1 << 12)
 #define TB_FLAG_SR_FD            (1 << SR_FD)           /* 15 */
 #define TB_FLAG_FPSCR_PR         FPSCR_PR               /* 19 */
 #define TB_FLAG_FPSCR_SZ         FPSCR_SZ               /* 20 */
@@ -95,8 +94,7 @@
 #define TB_FLAG_DELAY_SLOT_MASK  (TB_FLAG_DELAY_SLOT |       \
                                   TB_FLAG_DELAY_SLOT_COND |  \
                                   TB_FLAG_DELAY_SLOT_RTE)
-#define TB_FLAG_GUSA_MASK        ((0xff << TB_FLAG_GUSA_SHIFT) | \
-                                  TB_FLAG_GUSA_EXCLUSIVE)
+#define TB_FLAG_GUSA_MASK        (0xff << TB_FLAG_GUSA_SHIFT)
 #define TB_FLAG_FPSCR_MASK       (TB_FLAG_FPSCR_PR | \
                                   TB_FLAG_FPSCR_SZ | \
                                   TB_FLAG_FPSCR_FR)
diff -Nru qemu-10.0.13+ds/target/sh4/translate.c 
qemu-10.0.14+ds/target/sh4/translate.c
--- qemu-10.0.13+ds/target/sh4/translate.c      2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/target/sh4/translate.c      2026-09-29 03:48:20.000000000 
+0300
@@ -47,6 +47,9 @@
     uint16_t opcode;
 
     bool has_movcal;
+#ifdef CONFIG_USER_ONLY
+    bool in_gusa_exclusive;
+#endif
 } DisasContext;
 
 #if defined(CONFIG_USER_ONLY)
@@ -220,7 +223,11 @@
 
 static inline bool use_exit_tb(DisasContext *ctx)
 {
-    return (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) != 0;
+#ifdef CONFIG_USER_ONLY
+    return ctx->in_gusa_exclusive;
+#else
+    return false;
+#endif
 }
 
 static bool use_goto_tb(DisasContext *ctx, target_ulong dest)
@@ -273,7 +280,8 @@
     TCGLabel *l1 = gen_new_label();
     TCGCond cond_not_taken = jump_if_true ? TCG_COND_EQ : TCG_COND_NE;
 
-    if (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) {
+#ifdef CONFIG_USER_ONLY
+    if (ctx->in_gusa_exclusive) {
         /* When in an exclusive region, we must continue to the end.
            Therefore, exit the region on a taken branch, but otherwise
            fall through to the next instruction.  */
@@ -286,6 +294,7 @@
         ctx->base.is_jmp = DISAS_NEXT;
         return;
     }
+#endif
 
     gen_save_cpu_state(ctx, false);
     tcg_gen_brcondi_i32(cond_not_taken, cpu_sr_t, 0, l1);
@@ -304,7 +313,8 @@
     tcg_gen_mov_i32(ds, cpu_delayed_cond);
     tcg_gen_discard_i32(cpu_delayed_cond);
 
-    if (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) {
+#ifdef CONFIG_USER_ONLY
+    if (ctx->in_gusa_exclusive) {
         /* When in an exclusive region, we must continue to the end.
            Therefore, exit the region on a taken branch, but otherwise
            fall through to the next instruction.  */
@@ -318,6 +328,7 @@
         ctx->base.is_jmp = DISAS_NEXT;
         return;
     }
+#endif
 
     tcg_gen_brcondi_i32(TCG_COND_NE, ds, 0, l1);
     gen_goto_tb(ctx, 1, ctx->base.pc_next + 2);
@@ -1800,16 +1811,18 @@
         /* go out of the delay slot */
         ctx->envflags &= ~TB_FLAG_DELAY_SLOT_MASK;
 
+#ifdef CONFIG_USER_ONLY
         /* When in an exclusive region, we must continue to the end
            for conditional branches.  */
-        if (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE
-            && old_flags & TB_FLAG_DELAY_SLOT_COND) {
+        if (ctx->in_gusa_exclusive && old_flags & TB_FLAG_DELAY_SLOT_COND) {
             gen_delayed_conditional_jump(ctx);
             return;
         }
+
         /* Otherwise this is probably an invalid gUSA region.
            Drop the GUSA bits so the next TB doesn't see them.  */
         ctx->envflags &= ~TB_FLAG_GUSA_MASK;
+#endif
 
         tcg_gen_movi_i32(cpu_flags, ctx->envflags);
         if (old_flags & TB_FLAG_DELAY_SLOT_COND) {
@@ -1827,7 +1840,6 @@
  */
 static void gen_restart_exclusive(DisasContext *ctx)
 {
-    ctx->envflags |= TB_FLAG_GUSA_EXCLUSIVE;
     gen_save_cpu_state(ctx, false);
     gen_helper_exclusive(tcg_env);
     ctx->base.is_jmp = DISAS_NORETURN;
@@ -2215,11 +2227,13 @@
         int backup = sextract32(ctx->tbflags, TB_FLAG_GUSA_SHIFT, 8);
         int max_insns = (pc_end - pc) / 2;
 
+        ctx->in_gusa_exclusive = ctx->base.tb->cflags & CF_STEP_ATOMIC;
+
         if (pc != pc_end + backup || max_insns < 2) {
             /* This is a malformed gUSA region.  Don't do anything special,
                since the interpreter is likely to get confused.  */
             ctx->envflags &= ~TB_FLAG_GUSA_MASK;
-        } else if (tbflags & TB_FLAG_GUSA_EXCLUSIVE) {
+        } else if (ctx->in_gusa_exclusive) {
             /* Regardless of single-stepping or the end of the page,
                we must complete execution of the gUSA region while
                holding the exclusive lock.  */
@@ -2253,7 +2267,7 @@
 
 #ifdef CONFIG_USER_ONLY
     if (unlikely(ctx->envflags & TB_FLAG_GUSA_MASK)
-        && !(ctx->envflags & TB_FLAG_GUSA_EXCLUSIVE)) {
+        && !ctx->in_gusa_exclusive) {
         /*
          * We're in an gUSA region, and we have not already fallen
          * back on using an exclusive region.  Attempt to parse the
@@ -2283,10 +2297,12 @@
 {
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
 
-    if (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) {
+#ifdef CONFIG_USER_ONLY
+    if (ctx->in_gusa_exclusive) {
         /* Ending the region of exclusivity.  Clear the bits.  */
         ctx->envflags &= ~TB_FLAG_GUSA_MASK;
     }
+#endif
 
     switch (ctx->base.is_jmp) {
     case DISAS_STOP:
diff -Nru qemu-10.0.13+ds/tcg/riscv/tcg-target.c.inc 
qemu-10.0.14+ds/tcg/riscv/tcg-target.c.inc
--- qemu-10.0.13+ds/tcg/riscv/tcg-target.c.inc  2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/tcg/riscv/tcg-target.c.inc  2026-09-29 03:48:20.000000000 
+0300
@@ -784,6 +784,7 @@
     case TCG_TYPE_V64:
     case TCG_TYPE_V128:
     case TCG_TYPE_V256:
+        tcg_debug_assert(s->riscv_cur_type != TCG_TYPE_COUNT);
         {
             int lmul = type - riscv_lg2_vlenb;
             int nf = 1 << MAX(lmul, 0);
@@ -1023,6 +1024,10 @@
             unsigned idx = type - riscv_lg2_vlenb;
 
             tcg_debug_assert(idx < ARRAY_SIZE(whole_reg_ld));
+            /* We must initialize vtype to something to avoid VILL. */
+            if (s->riscv_cur_type == TCG_TYPE_COUNT) {
+                set_vtype(s, type, MO_8);
+            }
             insn = whole_reg_ld[idx];
         } else {
             static const RISCVInsn unit_stride_ld[] = {
@@ -1055,6 +1060,7 @@
     case TCG_TYPE_V64:
     case TCG_TYPE_V128:
     case TCG_TYPE_V256:
+        tcg_debug_assert(s->riscv_cur_type != TCG_TYPE_COUNT);
         if (type >= riscv_lg2_vlenb) {
             static const RISCVInsn whole_reg_st[] = {
                 OPC_VS1R_V, OPC_VS2R_V, OPC_VS4R_V, OPC_VS8R_V
diff -Nru qemu-10.0.13+ds/tests/qemu-iotests/tests/migrate-bitmaps-test 
qemu-10.0.14+ds/tests/qemu-iotests/tests/migrate-bitmaps-test
--- qemu-10.0.13+ds/tests/qemu-iotests/tests/migrate-bitmaps-test       
2026-08-26 22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/tests/qemu-iotests/tests/migrate-bitmaps-test       
2026-09-29 03:48:20.000000000 +0300
@@ -206,6 +206,39 @@
             self.vm_b.launch()
             self.check_bitmap(self.vm_b, sha256 if persistent else False)
 
+    def test_migration_to_readonly_destination(self):
+        granularity = 512
+        mig_caps = [{'capability': 'events', 'state': True},
+                   {'capability': 'dirty-bitmaps', 'state': True}]
+
+        self.vm_b.add_incoming("defer")
+        self.vm_b.add_drive(disk_b, 'read-only=on')
+
+        self.add_bitmap(self.vm_a, granularity, True)
+        self.vm_a.hmp_qemu_io('drive0', 'write 0 4096')
+
+        self.vm_a.cmd('migrate-set-capabilities', capabilities=mig_caps)
+        self.vm_a.cmd('migrate', uri=mig_cmd)
+        while True:
+            event = self.vm_a.event_wait('MIGRATION')
+            if event['data']['status'] == 'completed':
+                break
+        self.vm_a.shutdown()
+
+        self.vm_b.launch()
+        self.vm_b.cmd('migrate-set-capabilities', capabilities=mig_caps)
+        self.vm_b.cmd('migrate-incoming', uri=incoming_cmd)
+        while True:
+            event = self.vm_b.event_wait('MIGRATION')
+            if event['data']['status'] in ('completed', 'failed'):
+                break
+
+        self.assert_qmp(event, 'data/status', 'failed')
+
+        # A failed incoming load makes the destination process exit on
+        # its own; reap it so tearDown()'s shutdown() is a clean no-op.
+        self.vm_b.wait()
+
 
 def inject_test_case(klass, suffix, method, *args, **kwargs):
     mc = operator.methodcaller(method, *args, **kwargs)
diff -Nru qemu-10.0.13+ds/tests/qemu-iotests/tests/migrate-bitmaps-test.out 
qemu-10.0.14+ds/tests/qemu-iotests/tests/migrate-bitmaps-test.out
--- qemu-10.0.13+ds/tests/qemu-iotests/tests/migrate-bitmaps-test.out   
2026-08-26 22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/tests/qemu-iotests/tests/migrate-bitmaps-test.out   
2026-09-29 03:48:20.000000000 +0300
@@ -1,5 +1,5 @@
-.....................................
+......................................
 ----------------------------------------------------------------------
-Ran 37 tests
+Ran 38 tests
 
 OK
diff -Nru qemu-10.0.13+ds/tests/qemu-iotests/tests/remove-bitmap-from-backing 
qemu-10.0.14+ds/tests/qemu-iotests/tests/remove-bitmap-from-backing
--- qemu-10.0.13+ds/tests/qemu-iotests/tests/remove-bitmap-from-backing 
2026-08-26 22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/tests/qemu-iotests/tests/remove-bitmap-from-backing 
2026-09-29 03:48:20.000000000 +0300
@@ -35,7 +35,7 @@
 # Just assert that our method of checking bitmaps in the image works.
 assert 'bitmaps' in qemu_img_info(base)['format-specific']['data']
 
-vm = iotests.VM().add_drive(top, 'backing.node-name=base')
+vm = iotests.VM().add_drive(top, 'node-name=top,backing.node-name=base')
 vm.launch()
 
 log('Trying to remove persistent bitmap from r-o base node, should fail:')
@@ -66,6 +66,33 @@
 if result != {'return': {}}:
     log('Failed to reopen: ' + str(result))
 
+log('Adding a persistent bitmap to the r-o base node, should fail:')
+vm.qmp_log('block-dirty-bitmap-add', node='base', name='bitmap1',
+          persistent=True)
+
+log('Same add inside a transaction, preceded by an otherwise valid')
+log('action: the whole transaction must fail and roll back the')
+log('already-succeeded first action too:')
+vm.qmp_log('transaction', actions=[
+    {'type': 'block-dirty-bitmap-add',
+     'data': {'node': 'top', 'name': 'bitmap2', 'persistent': True}},
+    {'type': 'block-dirty-bitmap-add',
+     'data': {'node': 'base', 'name': 'bitmap1', 'persistent': True}},
+])
+
+log('bitmap2 on the rw top node must not have survived the rollback:')
+vm.qmp_log('block-dirty-bitmap-remove', node='top', name='bitmap2')
+
+log('Marking the rw top node inactive:')
+vm.qmp_log('blockdev-set-active', **{'node-name': 'top', 'active': False})
+
+log('Adding a persistent bitmap to a rw but inactive node, should fail:')
+vm.qmp_log('block-dirty-bitmap-add', node='top', name='bitmap3',
+          persistent=True)
+
+log('Reactivating the top node:')
+vm.qmp_log('blockdev-set-active', **{'node-name': 'top', 'active': True})
+
 vm.shutdown()
 
 if 'bitmaps' in qemu_img_info(base)['format-specific']['data']:
diff -Nru 
qemu-10.0.13+ds/tests/qemu-iotests/tests/remove-bitmap-from-backing.out 
qemu-10.0.14+ds/tests/qemu-iotests/tests/remove-bitmap-from-backing.out
--- qemu-10.0.13+ds/tests/qemu-iotests/tests/remove-bitmap-from-backing.out     
2026-08-26 22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/tests/qemu-iotests/tests/remove-bitmap-from-backing.out     
2026-09-29 03:48:20.000000000 +0300
@@ -1,6 +1,26 @@
 Trying to remove persistent bitmap from r-o base node, should fail:
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", 
"node": "base"}}
-{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap0' is readonly and 
cannot be modified"}}
+{"error": {"class": "GenericError", "desc": "Cannot remove persistent bitmap 
'bitmap0': no write access to node 'base'"}}
 Remove persistent bitmap from base node reopened to RW:
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", 
"node": "base"}}
 {"return": {}}
+Adding a persistent bitmap to the r-o base node, should fail:
+{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap1", "node": 
"base", "persistent": true}}
+{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitmap to 
read-only or inactive node 'base'"}}
+Same add inside a transaction, preceded by an otherwise valid
+action: the whole transaction must fail and roll back the
+already-succeeded first action too:
+{"execute": "transaction", "arguments": {"actions": [{"data": {"name": 
"bitmap2", "node": "top", "persistent": true}, "type": 
"block-dirty-bitmap-add"}, {"data": {"name": "bitmap1", "node": "base", 
"persistent": true}, "type": "block-dirty-bitmap-add"}]}}
+{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitmap to 
read-only or inactive node 'base'"}}
+bitmap2 on the rw top node must not have survived the rollback:
+{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap2", 
"node": "top"}}
+{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap2' not 
found"}}
+Marking the rw top node inactive:
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": 
"top"}}
+{"return": {}}
+Adding a persistent bitmap to a rw but inactive node, should fail:
+{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap3", "node": 
"top", "persistent": true}}
+{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitmap to 
read-only or inactive node 'top'"}}
+Reactivating the top node:
+{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": 
"top"}}
+{"return": {}}
diff -Nru qemu-10.0.13+ds/tests/qtest/bcm2835-ic-test.c 
qemu-10.0.14+ds/tests/qtest/bcm2835-ic-test.c
--- qemu-10.0.13+ds/tests/qtest/bcm2835-ic-test.c       1970-01-01 
03:00:00.000000000 +0300
+++ qemu-10.0.14+ds/tests/qtest/bcm2835-ic-test.c       2026-09-29 
03:48:20.000000000 +0300
@@ -0,0 +1,66 @@
+/*
+ * QTest testcase for the BCM2835 Interrupt Controller
+ *
+ * Copyright (c) 2026 Bin Guo <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest-single.h"
+
+#define IC_BASE     0x3f00b200
+#define FIQ_CONTROL (IC_BASE + 0x0c)
+
+static void test_fiq_select_out_of_range(void)
+{
+    uint32_t val;
+
+    /*
+     * Only FIQ sources 0..71 exist.  Source 96 used to trigger an assertion
+     * in bcm2835_ic_update() because extract32(arm_irq_level, 32, 1) was
+     * called with start >= 32.  Make sure the write is rejected and QEMU
+     * keeps running.
+     */
+    writel(FIQ_CONTROL, 0xe0); /* fiq_select = 96, fiq_enable = 1 */
+    val = readl(FIQ_CONTROL);
+    g_assert_cmpint(val, ==, 0);
+
+    /* The first source past the ARM IRQ range should also be rejected. */
+    writel(FIQ_CONTROL, 0xc8); /* fiq_select = 72, fiq_enable = 1 */
+    val = readl(FIQ_CONTROL);
+    g_assert_cmpint(val, ==, 0);
+}
+
+static void test_fiq_select_valid(void)
+{
+    uint32_t val;
+
+    /* Select the highest valid ARM IRQ source (64 + 7 = 71). */
+    writel(FIQ_CONTROL, 0xc7); /* fiq_select = 71, fiq_enable = 1 */
+    val = readl(FIQ_CONTROL);
+    g_assert_cmpint(val, ==, 0xc7);
+
+    /* Select the highest valid GPU IRQ source. */
+    writel(FIQ_CONTROL, 0x3f); /* fiq_select = 63, fiq_enable = 0 */
+    val = readl(FIQ_CONTROL);
+    g_assert_cmpint(val, ==, 0x3f);
+}
+
+int main(int argc, char **argv)
+{
+    int ret;
+
+    g_test_init(&argc, &argv, NULL);
+
+    qtest_add_func("/bcm2835/bcm2835-ic/fiq-select-out-of-range",
+                   test_fiq_select_out_of_range);
+    qtest_add_func("/bcm2835/bcm2835-ic/fiq-select-valid",
+                   test_fiq_select_valid);
+
+    qtest_start("-machine raspi3b");
+    ret = g_test_run();
+    qtest_end();
+
+    return ret;
+}
diff -Nru qemu-10.0.13+ds/tests/qtest/ide-test.c 
qemu-10.0.14+ds/tests/qtest/ide-test.c
--- qemu-10.0.13+ds/tests/qtest/ide-test.c      2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/tests/qtest/ide-test.c      2026-09-29 03:48:20.000000000 
+0300
@@ -95,6 +95,7 @@
 
 enum {
     CMD_DSM         = 0x06,
+    CMD_READ        = 0x20,  /* READ SECTOR(S) */
     CMD_DIAGNOSE    = 0x90,
     CMD_INIT_DP     = 0x91,  /* INITIALIZE DEVICE PARAMETERS */
     CMD_READ_DMA    = 0xc8,
@@ -1194,6 +1195,66 @@
     free_pci_device(dev);
 }
 
+/* Zero sectors per track has to abort (ATA-5 8.16.6), not divide by zero */
+static void test_specify_zero_sectors(void)
+{
+    QTestState *qts;
+    QPCIDevice *dev;
+    QPCIBar bmdma_bar, ide_bar;
+    uint16_t buf[256];
+    uint8_t data;
+    int i;
+
+    qts = ide_test_start(
+        "-blockdev driver=file,node-name=hda,filename=%s "
+        "-device ide-hd,drive=hda,bus=ide.0,unit=0 ",
+        tmp_path[0]);
+
+    dev = get_pci_device(qts, &bmdma_bar, &ide_bar);
+
+    qpci_io_writeb(dev, ide_bar, reg_nsectors, 0);
+    qpci_io_writeb(dev, ide_bar, reg_device, 0);
+    qpci_io_writeb(dev, ide_bar, reg_command, CMD_INIT_DP);
+
+    assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), ERR);
+    assert_bit_set(qpci_io_readb(dev, ide_bar, reg_error), ABRT);
+
+    /* The refused request has to leave the default translation in effect */
+    qpci_io_writeb(dev, ide_bar, reg_device, 0);
+    qpci_io_writeb(dev, ide_bar, reg_command, CMD_IDENTIFY);
+    for (i = 0; i < 256; i++) {
+        buf[i] = qpci_io_readw(dev, ide_bar, reg_data);
+    }
+    g_assert_cmpint(buf[55], ==, 16);
+    g_assert_cmpint(buf[56], ==, 63);
+
+    /* READ SECTOR(S) of CHS 0/0/1, which used to crash QEMU */
+    qpci_io_writeb(dev, ide_bar, reg_nsectors, 1);
+    qpci_io_writeb(dev, ide_bar, reg_lba_low, 1);
+    qpci_io_writeb(dev, ide_bar, reg_lba_middle, 0);
+    qpci_io_writeb(dev, ide_bar, reg_lba_high, 0);
+    qpci_io_writeb(dev, ide_bar, reg_device, 0);
+    qpci_io_writeb(dev, ide_bar, reg_command, CMD_READ);
+
+    data = ide_wait_clear(qts, BSY);
+    assert_bit_set(data, DRQ);
+    assert_bit_clear(data, ERR | DF);
+    for (i = 0; i < 256; i++) {
+        buf[i] = qpci_io_readw(dev, ide_bar, reg_data);
+    }
+    assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), ERR | DF | DRQ);
+
+    /* A supported translation is still accepted */
+    qpci_io_writeb(dev, ide_bar, reg_nsectors, 32);
+    qpci_io_writeb(dev, ide_bar, reg_device, 7);
+    qpci_io_writeb(dev, ide_bar, reg_command, CMD_INIT_DP);
+
+    assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), ERR);
+
+    ide_test_quit(qts);
+    free_pci_device(dev);
+}
+
 static void test_cdrom_pio(void)
 {
     cdrom_read_impl(1, CDROM_PIO);
@@ -1265,6 +1326,7 @@
     g_test_init(&argc, &argv, NULL);
 
     qtest_add_func("/ide/read_native", test_specify);
+    qtest_add_func("/ide/specify/zero_sectors", test_specify_zero_sectors);
 
     qtest_add_func("/ide/identify", test_identify);
 
diff -Nru qemu-10.0.13+ds/tests/qtest/meson.build 
qemu-10.0.14+ds/tests/qtest/meson.build
--- qemu-10.0.13+ds/tests/qtest/meson.build     2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/tests/qtest/meson.build     2026-09-29 03:48:20.000000000 
+0300
@@ -254,7 +254,7 @@
     ['tpm-tis-device-test', 'tpm-tis-device-swtpm-test'] : []) +               
                          \
   (config_all_devices.has_key('CONFIG_XLNX_ZYNQMP_ARM') ? ['xlnx-can-test', 
'fuzz-xlnx-dp-test'] : []) + \
   (config_all_devices.has_key('CONFIG_XLNX_VERSAL') ? ['xlnx-canfd-test', 
'xlnx-versal-trng-test'] : []) + \
-  (config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test', 
'bcm2835-i2c-test'] : []) +  \
+  (config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test', 
'bcm2835-i2c-test', 'bcm2835-ic-test'] : []) +  \
   (config_all_accel.has_key('CONFIG_TCG') and                                  
          \
    config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : 
[]) + \
   (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \
diff -Nru qemu-10.0.13+ds/tests/qtest/riscv-csr-test.c 
qemu-10.0.14+ds/tests/qtest/riscv-csr-test.c
--- qemu-10.0.13+ds/tests/qtest/riscv-csr-test.c        2026-08-26 
22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/tests/qtest/riscv-csr-test.c        2026-09-29 
03:48:20.000000000 +0300
@@ -20,6 +20,12 @@
 #define CSR_MVENDORID       0xf11
 #define CSR_MISELECT        0x350
 
+#define CSR_SEED           0x015
+
+#define SEED_OPST_MASK     (UINT64_C(0x3) << 30)
+#define SEED_OPST_ES16     (UINT64_C(0x2) << 30)
+#define SEED_OPST_DEAD     (UINT64_C(0x3) << 30)
+
 static void run_test_csr(void)
 {
     uint64_t res;
@@ -46,11 +52,31 @@
     qtest_quit(qts);
 }
 
+static void run_test_seed_csr(void)
+{
+    uint64_t val = 0;
+    uint64_t opst;
+    QTestState *qts;
+
+    qts = qtest_init("-machine virt -cpu tt-ascalon");
+
+    qtest_csr_call(qts, "get_csr", 0, CSR_SEED, &val);
+
+    opst = val & SEED_OPST_MASK;
+    g_assert_true(opst == SEED_OPST_ES16 ||
+                  opst == SEED_OPST_DEAD);
+
+    g_assert_cmphex(val >> 32, ==, 0);
+
+    qtest_quit(qts);
+}
+
 int main(int argc, char **argv)
 {
     g_test_init(&argc, &argv, NULL);
 
     qtest_add_func("/cpu/csr", run_test_csr);
+    qtest_add_func("/cpu/csr/seed", run_test_seed_csr);
 
     return g_test_run();
 }
diff -Nru qemu-10.0.13+ds/tests/qtest/usb-hcd-xhci-test.c 
qemu-10.0.14+ds/tests/qtest/usb-hcd-xhci-test.c
--- qemu-10.0.13+ds/tests/qtest/usb-hcd-xhci-test.c     2026-08-26 
22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/tests/qtest/usb-hcd-xhci-test.c     2026-09-29 
03:48:20.000000000 +0300
@@ -10,8 +10,70 @@
 #include "qemu/osdep.h"
 #include "libqtest-single.h"
 #include "libqos/usb.h"
+#include "libqos/malloc-pc.h"
 #include "qobject/qdict.h"
 
+/* capability registers */
+#define XHCI_CAPLENGTH          0x00
+#define XHCI_HCSPARAMS1         0x04
+#define XHCI_DBOFF              0x14
+#define XHCI_RTSOFF             0x18
+/* operational registers */
+#define XHCI_USBCMD             0x00
+#define XHCI_USBSTS             0x04
+#define XHCI_CRCR               0x18
+#define XHCI_DCBAAP             0x30
+#define XHCI_CONFIG             0x38
+#define XHCI_PORTSC(n)          (0x400 + 0x10 * (n))
+/* interrupter 0, relative to the runtime registers */
+#define XHCI_ERSTSZ             0x28
+#define XHCI_ERSTBA             0x30
+#define XHCI_ERDP               0x38
+
+#define USBCMD_RS               (1 << 0)
+#define USBCMD_HCRST            (1 << 1)
+#define USBSTS_HCH              (1 << 0)
+#define USBSTS_HCE              (1 << 12)
+#define PORTSC_CCS              (1 << 0)
+#define PORTSC_PR               (1 << 4)
+#define PORTSC_PP               (1 << 9)
+#define CRCR_RCS                (1 << 0)
+#define ERDP_EHB                (1 << 3)
+
+#define TRB_C                   (1 << 0)
+#define TRB_TR_IOC              (1 << 5)
+#define TRB_TR_SIA              (1U << 31)
+#define TRB_TYPE(t)             ((t) << 10)
+#define TRB_GET_TYPE(control)   (((control) >> 10) & 0x3f)
+#define TRB_GET_CCODE(status)   ((status) >> 24)
+#define TRB_GET_SLOT(control)   ((control) >> 24)
+
+#define TR_ISOCH                5
+#define CR_ENABLE_SLOT          9
+#define CR_ADDRESS_DEVICE       11
+#define CR_CONFIGURE_ENDPOINT   12
+#define ER_TRANSFER             32
+#define ER_COMMAND_COMPLETE     33
+#define CC_SUCCESS              1
+
+#define EP_TYPE_ISOCH_OUT       1
+#define EP_TYPE_CONTROL         4
+#define EP_TYPE_ISOCH_IN        5
+
+#define XHCI_RING_TRBS          64
+#define XHCI_MICROFRAME_NS      125000
+
+typedef struct XHCITest {
+    QTestState *qts;
+    QGuestAllocator alloc;
+    QPCIBus *bus;
+    struct qhc hc;
+    uint32_t oper, runtime, doorbell;
+    uint64_t cmd_ring, event_ring, input_ctx;
+    unsigned int cmd_idx, event_idx;
+    unsigned int port, slot;
+} XHCITest;
+
 static void wait_device_deleted_event(QTestState *qtest, const char *id)
 {
     QDict *resp, *data;
@@ -109,6 +171,258 @@
     qtest_qmp_device_del(qts, "ccid");
 }
 
+static uint32_t xhci_readl(XHCITest *x, uint32_t off)
+{
+    return qpci_io_readl(x->hc.dev, x->hc.bar, off);
+}
+
+static void xhci_writel(XHCITest *x, uint32_t off, uint32_t val)
+{
+    qpci_io_writel(x->hc.dev, x->hc.bar, off, val);
+}
+
+static void xhci_writeq(XHCITest *x, uint32_t off, uint64_t val)
+{
+    xhci_writel(x, off, val);
+    xhci_writel(x, off + 4, val >> 32);
+}
+
+static uint64_t xhci_alloc_page(XHCITest *x)
+{
+    uint64_t addr = guest_alloc(&x->alloc, 0x1000);
+
+    qtest_memset(x->qts, addr, 0, 0x1000);
+    return addr;
+}
+
+static void xhci_write_trb(XHCITest *x, uint64_t addr, uint64_t parameter,
+                           uint32_t status, uint32_t control)
+{
+    qtest_writeq(x->qts, addr, parameter);
+    qtest_writel(x->qts, addr + 8, status);
+    qtest_writel(x->qts, addr + 12, control);
+}
+
+/* Fetch the next event if there is one. Does not advance the clock. */
+static bool xhci_next_event(XHCITest *x, uint32_t *status, uint32_t *control)
+{
+    uint64_t addr = x->event_ring + 16 * x->event_idx;
+    uint32_t c = qtest_readl(x->qts, addr + 12);
+
+    if (!(c & TRB_C)) {
+        return false;
+    }
+    if (status) {
+        *status = qtest_readl(x->qts, addr + 8);
+    }
+    if (control) {
+        *control = c;
+    }
+    x->event_idx++;
+    g_assert_cmpuint(x->event_idx, <, XHCI_RING_TRBS);
+    xhci_writeq(x, x->runtime + XHCI_ERDP, (addr + 16) | ERDP_EHB);
+    return true;
+}
+
+static unsigned int xhci_command(XHCITest *x, uint64_t parameter,
+                                 uint32_t control)
+{
+    uint32_t status;
+
+    g_assert_cmpuint(x->cmd_idx, <, XHCI_RING_TRBS);
+    xhci_write_trb(x, x->cmd_ring + 16 * x->cmd_idx++, parameter, 0,
+                   control | TRB_C);
+    xhci_writel(x, x->doorbell, 0);
+
+    g_assert_true(xhci_next_event(x, &status, &control));
+    g_assert_cmpuint(TRB_GET_TYPE(control), ==, ER_COMMAND_COMPLETE);
+    g_assert_cmpuint(TRB_GET_CCODE(status), ==, CC_SUCCESS);
+    return TRB_GET_SLOT(control);
+}
+
+/*
+ * Start qemu-xhci with one USB device, run the controller and bring the
+ * device to the Addressed state.
+ */
+static void xhci_test_start(XHCITest *x, const char *usb_device)
+{
+    uint64_t dcbaa, erst, ep0_ring;
+    unsigned int maxports;
+
+    memset(x, 0, sizeof(*x));
+    /* pit=off: a long clock step would run the i8254 timer all the way */
+    x->qts = qtest_initf("-machine pc,pit=off -nodefaults "
+                         "-device qemu-xhci,id=xhci,addr=04.0 %s", usb_device);
+    pc_alloc_init(&x->alloc, x->qts, ALLOC_NO_FLAGS);
+    x->bus = qpci_new_pc(x->qts, NULL);
+    qusb_pci_init_one(x->bus, &x->hc, QPCI_DEVFN(4, 0), 0);
+
+    x->oper = qpci_io_readb(x->hc.dev, x->hc.bar, XHCI_CAPLENGTH);
+    x->runtime = xhci_readl(x, XHCI_RTSOFF) & ~0x1f;
+    x->doorbell = xhci_readl(x, XHCI_DBOFF) & ~0x3;
+    maxports = xhci_readl(x, XHCI_HCSPARAMS1) >> 24;
+
+    xhci_writel(x, x->oper + XHCI_USBCMD, USBCMD_HCRST);
+    g_assert_false(xhci_readl(x, x->oper + XHCI_USBCMD) & USBCMD_HCRST);
+
+    dcbaa = xhci_alloc_page(x);
+    erst = xhci_alloc_page(x);
+    x->cmd_ring = xhci_alloc_page(x);
+    x->event_ring = xhci_alloc_page(x);
+    x->input_ctx = xhci_alloc_page(x);
+
+    xhci_writel(x, x->oper + XHCI_CONFIG, 1);
+    xhci_writeq(x, x->oper + XHCI_DCBAAP, dcbaa);
+    qtest_writeq(x->qts, erst, x->event_ring);
+    qtest_writel(x->qts, erst + 8, XHCI_RING_TRBS);
+    xhci_writel(x, x->runtime + XHCI_ERSTSZ, 1);
+    xhci_writeq(x, x->runtime + XHCI_ERSTBA, erst);
+    xhci_writeq(x, x->runtime + XHCI_ERDP, x->event_ring | ERDP_EHB);
+    xhci_writeq(x, x->oper + XHCI_CRCR, x->cmd_ring | CRCR_RCS);
+    xhci_writel(x, x->oper + XHCI_USBCMD, USBCMD_RS);
+    g_assert_false(xhci_readl(x, x->oper + XHCI_USBSTS) & USBSTS_HCH);
+
+    for (x->port = 0; x->port < maxports; x->port++) {
+        if (xhci_readl(x, x->oper + XHCI_PORTSC(x->port)) & PORTSC_CCS) {
+            break;
+        }
+    }
+    g_assert_cmpuint(x->port, <, maxports);
+    xhci_writel(x, x->oper + XHCI_PORTSC(x->port), PORTSC_PP | PORTSC_PR);
+    while (xhci_next_event(x, NULL, NULL)) {
+        /* drop the port status change events */
+    }
+
+    x->slot = xhci_command(x, 0, TRB_TYPE(CR_ENABLE_SLOT));
+    qtest_writeq(x->qts, dcbaa + 8 * x->slot, xhci_alloc_page(x));
+
+    /* input control context: add slot and ep0 */
+    qtest_writel(x->qts, x->input_ctx + 0x04, 0x3);
+    /* slot context: one context entry, root hub port */
+    qtest_writel(x->qts, x->input_ctx + 0x20, 1 << 27);
+    qtest_writel(x->qts, x->input_ctx + 0x24, (x->port + 1) << 16);
+    /* ep0 context */
+    ep0_ring = xhci_alloc_page(x);
+    qtest_writel(x->qts, x->input_ctx + 0x44,
+                 (64 << 16) | (EP_TYPE_CONTROL << 3));
+    qtest_writeq(x->qts, x->input_ctx + 0x48, ep0_ring | 1);
+    xhci_command(x, x->input_ctx,
+                 TRB_TYPE(CR_ADDRESS_DEVICE) | (x->slot << 24));
+}
+
+/* Returns the address of the transfer ring. */
+static uint64_t xhci_configure_ep(XHCITest *x, unsigned int epid,
+                                  unsigned int type, unsigned int interval,
+                                  unsigned int max_packet)
+{
+    uint64_t ring = xhci_alloc_page(x);
+    uint64_t epctx = x->input_ctx + 0x20 * (epid + 1);
+
+    qtest_memset(x->qts, x->input_ctx, 0, 0x1000);
+    qtest_writel(x->qts, x->input_ctx + 0x04, (1 << epid) | 1);
+    qtest_writel(x->qts, x->input_ctx + 0x20, epid << 27);
+    qtest_writel(x->qts, x->input_ctx + 0x24, (x->port + 1) << 16);
+    qtest_writel(x->qts, epctx + 0x00, interval << 16);
+    qtest_writel(x->qts, epctx + 0x04, (max_packet << 16) | (type << 3));
+    qtest_writeq(x->qts, epctx + 0x08, ring | 1);
+    xhci_command(x, x->input_ctx,
+                 TRB_TYPE(CR_CONFIGURE_ENDPOINT) | (x->slot << 24));
+    return ring;
+}
+
+static void xhci_test_end(XHCITest *x)
+{
+    g_free(x->hc.dev);
+    qpci_free_pc(x->bus);
+    alloc_destroy(&x->alloc);
+    qtest_quit(x->qts);
+}
+
+static bool xhci_test_supported(const char *usb_device)
+{
+    const char *arch = qtest_get_arch();
+
+    if (strcmp(arch, "i386") != 0 && strcmp(arch, "x86_64") != 0) {
+        g_test_skip("Test only runs on x86 (pc machine)");
+        return false;
+    }
+    if (!qtest_has_device("qemu-xhci") || !qtest_has_device(usb_device)) {
+        g_test_skip("Devices not available");
+        return false;
+    }
+    return true;
+}
+
+/*
+ * An isoch TD with SIA set is run at the next interval boundary. That has to
+ * hold once the microframe index no longer fits in 32 bits as well.
+ */
+static void test_xhci_isoch_mfindex_32bit(void)
+{
+    const unsigned int interval = 6;
+    uint32_t control;
+    uint64_t ring;
+    XHCITest x;
+
+    if (!xhci_test_supported("usb-audio")) {
+        return;
+    }
+
+    xhci_test_start(&x, "-audiodev none,id=snd0 "
+                        "-device usb-audio,audiodev=snd0");
+    ring = xhci_configure_ep(&x, 2, EP_TYPE_ISOCH_OUT, interval, 64);
+
+    /* Go past 2^32 microframes and stop off an interval boundary. */
+    qtest_clock_step(x.qts, (1ULL << 32) * XHCI_MICROFRAME_NS);
+    qtest_clock_step(x.qts, 5 * XHCI_MICROFRAME_NS);
+
+    xhci_write_trb(&x, ring, xhci_alloc_page(&x), 64,
+                   TRB_TYPE(TR_ISOCH) | TRB_TR_SIA | TRB_TR_IOC | TRB_C);
+    xhci_writel(&x, x.doorbell + 4 * x.slot, 2);
+    g_assert_false(xhci_next_event(&x, NULL, NULL));
+
+    /*
+     * The streaming interface has not been enabled, so usb-audio stalls the
+     * TD. What matters is when that happens.
+     */
+    qtest_clock_step(x.qts, XHCI_MICROFRAME_NS << interval);
+    g_assert_true(xhci_next_event(&x, NULL, &control));
+    g_assert_cmpuint(TRB_GET_TYPE(control), ==, ER_TRANSFER);
+
+    xhci_test_end(&x);
+}
+
+/*
+ * The endpoint type in the endpoint context is whatever the guest says. Tell
+ * the controller that the interrupt endpoint of usb-kbd is isoch. The idle
+ * keyboard NAKs, and the TD has to stay pending when first the kick timer and
+ * then a doorbell retry it.
+ */
+static void test_xhci_isoch_ep_type_mismatch(void)
+{
+    uint64_t ring;
+    XHCITest x;
+
+    if (!xhci_test_supported("usb-kbd")) {
+        return;
+    }
+
+    xhci_test_start(&x, "-device usb-kbd");
+    ring = xhci_configure_ep(&x, 3, EP_TYPE_ISOCH_IN, 0, 8);
+
+    xhci_write_trb(&x, ring, xhci_alloc_page(&x), 8,
+                   TRB_TYPE(TR_ISOCH) | TRB_TR_SIA | TRB_TR_IOC | TRB_C);
+    xhci_writel(&x, x.doorbell + 4 * x.slot, 3);
+    qtest_clock_step(x.qts, 2 * XHCI_MICROFRAME_NS);
+    xhci_writel(&x, x.doorbell + 4 * x.slot, 3);
+
+    g_assert_false(xhci_next_event(&x, NULL, NULL));
+    g_assert_cmphex(xhci_readl(&x, x.oper + XHCI_USBSTS) &
+                    (USBSTS_HCH | USBSTS_HCE), ==, 0);
+
+    xhci_test_end(&x);
+}
+
 int main(int argc, char **argv)
 {
     int ret;
@@ -123,6 +437,10 @@
     if (qtest_has_device("usb-ccid")) {
         qtest_add_func("/xhci/pci/hotplug/usb-ccid", test_usb_ccid_hotplug);
     }
+    qtest_add_func("/xhci/pci/isoch/mfindex-32bit",
+                   test_xhci_isoch_mfindex_32bit);
+    qtest_add_func("/xhci/pci/isoch/ep-type-mismatch",
+                   test_xhci_isoch_ep_type_mismatch);
 
     qtest_start("-device nec-usb-xhci,id=xhci"
                 " -drive id=drive0,if=none,file=null-co://,"
diff -Nru qemu-10.0.13+ds/tests/unit/meson.build 
qemu-10.0.14+ds/tests/unit/meson.build
--- qemu-10.0.13+ds/tests/unit/meson.build      2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/tests/unit/meson.build      2026-09-29 03:48:21.000000000 
+0300
@@ -117,6 +117,7 @@
   endif
   if host_os != 'windows'
     tests += {
+      'test-io-channel-websock': [io],
       'test-image-locking': [testblock],
       'test-nested-aio-poll': [],
     }
diff -Nru qemu-10.0.13+ds/tests/unit/test-blockjob.c 
qemu-10.0.14+ds/tests/unit/test-blockjob.c
--- qemu-10.0.13+ds/tests/unit/test-blockjob.c  2026-08-26 22:31:26.000000000 
+0300
+++ qemu-10.0.14+ds/tests/unit/test-blockjob.c  2026-09-29 03:48:21.000000000 
+0300
@@ -388,6 +388,105 @@
     cancel_common(s);
 }
 
+typedef struct PauseCountJob {
+    BlockJob common;
+    int n;
+    bool should_complete;
+} PauseCountJob;
+
+static void pause_count_job_complete(Job *job, Error **errp)
+{
+    PauseCountJob *s = container_of(job, PauseCountJob, common.job);
+    s->should_complete = true;
+}
+
+static int coroutine_fn pause_count_job_run(Job *job, Error **errp)
+{
+    PauseCountJob *s = container_of(job, PauseCountJob, common.job);
+
+    while (!s->should_complete) {
+        if (job_is_cancelled(&s->common.job)) {
+            return 0;
+        }
+        s->n++;
+        /*
+         * Yields; while a pause is pending the yield is skipped and the job
+         * parks in job_pause_point() instead.
+         */
+        job_sleep_ns(&s->common.job, 10 * 1000 * 1000);
+    }
+
+    return 0;
+}
+
+static const BlockJobDriver pause_count_job_driver = {
+    .job_driver = {
+        .instance_size = sizeof(PauseCountJob),
+        .free          = block_job_free,
+        .user_resume   = block_job_user_resume,
+        .run           = pause_count_job_run,
+        .complete      = pause_count_job_complete,
+    },
+};
+
+/*
+ * A job that has reached its pause point must stay paused while a pause is
+ * still pending (pause_count > 0). An overlapping drain re-enters the job (one
+ * drain's job_resume() wakes it while the next drain's job_pause() is already
+ * counted); the job must not run or clear job->paused, otherwise
+ * job_set_aio_context() can observe paused == false and abort.
+ */
+static void test_pause_keeps_paused(void)
+{
+    BlockBackend *blk;
+    BlockJob *bjob;
+    PauseCountJob *s;
+    Job *job;
+    int n0;
+
+    blk = create_blk(NULL);
+    bjob = mk_job(blk, "job0", &pause_count_job_driver, true,
+                  JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS);
+    s = container_of(bjob, PauseCountJob, common);
+    job = &bjob->job;
+    WITH_JOB_LOCK_GUARD() {
+        job_ref_locked(job);
+    }
+
+    job_start(job);
+
+    /* Pause the running job; it parks in job_pause_point() with paused set. */
+    WITH_JOB_LOCK_GUARD() {
+        job_pause_locked(job);
+        g_assert_true(job->paused);
+        g_assert_cmpint(job->status, ==, JOB_STATUS_PAUSED);
+    }
+    n0 = s->n;
+
+    /*
+     * Spurious wake while the pause is still pending. The job must stay 
parked:
+     * the bug clears job->paused, runs an iteration (s->n advances) and
+     * re-pauses, exposing a paused == false window.
+     */
+    job_enter(job);
+    WITH_JOB_LOCK_GUARD() {
+        g_assert_true(job->paused);
+    }
+    g_assert_cmpint(s->n, ==, n0);
+
+    /* Resume and tear down. */
+    WITH_JOB_LOCK_GUARD() {
+        job_resume_locked(job);
+    }
+    job_cancel_sync(job, true);
+    WITH_JOB_LOCK_GUARD() {
+        Job *dummy = job;
+        job_dismiss_locked(&dummy, &error_abort);
+        job_unref_locked(job);
+    }
+    destroy_blk(blk);
+}
+
 int main(int argc, char **argv)
 {
     qemu_init_main_loop(&error_abort);
@@ -402,5 +501,6 @@
     g_test_add_func("/blockjob/cancel/standby", test_cancel_standby);
     g_test_add_func("/blockjob/cancel/pending", test_cancel_pending);
     g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded);
+    g_test_add_func("/blockjob/pause/keep_paused", test_pause_keeps_paused);
     return g_test_run();
 }
diff -Nru qemu-10.0.13+ds/tests/unit/test-io-channel-websock.c 
qemu-10.0.14+ds/tests/unit/test-io-channel-websock.c
--- qemu-10.0.13+ds/tests/unit/test-io-channel-websock.c        1970-01-01 
03:00:00.000000000 +0300
+++ qemu-10.0.14+ds/tests/unit/test-io-channel-websock.c        2026-09-29 
03:48:21.000000000 +0300
@@ -0,0 +1,249 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QEMU I/O channel websock test
+ *
+ * Copyright (c) 2026 Virtuozzo International GmbH
+ */
+
+#include "qemu/osdep.h"
+#include "io/channel-websock.h"
+#include "io/channel-socket.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "qemu/sockets.h"
+#include "qom/object.h"
+
+#define TYPE_QIO_CHANNEL_STALL "qio-channel-stall"
+OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelStall, QIO_CHANNEL_STALL)
+
+/*
+ * Reports QIO_CHANNEL_ERR_BLOCK for the first @rstalls reads and @wstalls
+ * writes, the way a TLS channel does when a record arrives split across TCP
+ * segments or the socket cannot take the whole reply at once.
+ */
+struct QIOChannelStall {
+    QIOChannel parent;
+    QIOChannel *master;
+    unsigned rstalls;
+    unsigned wstalls;
+};
+
+static ssize_t qio_channel_stall_readv(QIOChannel *ioc,
+                                       const struct iovec *iov,
+                                       size_t niov,
+                                       int **fds,
+                                       size_t *nfds,
+                                       int flags,
+                                       Error **errp)
+{
+    QIOChannelStall *sioc = QIO_CHANNEL_STALL(ioc);
+
+    if (sioc->rstalls) {
+        sioc->rstalls--;
+        return QIO_CHANNEL_ERR_BLOCK;
+    }
+    return qio_channel_readv_full(sioc->master, iov, niov, fds, nfds,
+                                  flags, errp);
+}
+
+static ssize_t qio_channel_stall_writev(QIOChannel *ioc,
+                                        const struct iovec *iov,
+                                        size_t niov,
+                                        int *fds,
+                                        size_t nfds,
+                                        int flags,
+                                        Error **errp)
+{
+    QIOChannelStall *sioc = QIO_CHANNEL_STALL(ioc);
+
+    if (sioc->wstalls) {
+        sioc->wstalls--;
+        return QIO_CHANNEL_ERR_BLOCK;
+    }
+    return qio_channel_writev_full(sioc->master, iov, niov, fds, nfds,
+                                   flags, errp);
+}
+
+static int qio_channel_stall_set_blocking(QIOChannel *ioc, bool enabled,
+                                          Error **errp)
+{
+    QIOChannelStall *sioc = QIO_CHANNEL_STALL(ioc);
+
+    return qio_channel_set_blocking(sioc->master, enabled, errp) ? 0 : -1;
+}
+
+static int qio_channel_stall_close(QIOChannel *ioc, Error **errp)
+{
+    QIOChannelStall *sioc = QIO_CHANNEL_STALL(ioc);
+
+    return qio_channel_close(sioc->master, errp);
+}
+
+static GSource *qio_channel_stall_create_watch(QIOChannel *ioc,
+                                               GIOCondition condition)
+{
+    QIOChannelStall *sioc = QIO_CHANNEL_STALL(ioc);
+
+    return qio_channel_create_watch(sioc->master, condition);
+}
+
+static void qio_channel_stall_finalize(Object *obj)
+{
+    QIOChannelStall *sioc = QIO_CHANNEL_STALL(obj);
+
+    object_unref(OBJECT(sioc->master));
+}
+
+static void qio_channel_stall_class_init(ObjectClass *klass,
+                                         void *class_data G_GNUC_UNUSED)
+{
+    QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
+
+    ioc_klass->io_writev = qio_channel_stall_writev;
+    ioc_klass->io_readv = qio_channel_stall_readv;
+    ioc_klass->io_set_blocking = qio_channel_stall_set_blocking;
+    ioc_klass->io_close = qio_channel_stall_close;
+    ioc_klass->io_create_watch = qio_channel_stall_create_watch;
+}
+
+static const TypeInfo qio_channel_stall_info = {
+    .parent = TYPE_QIO_CHANNEL,
+    .name = TYPE_QIO_CHANNEL_STALL,
+    .instance_size = sizeof(QIOChannelStall),
+    .instance_finalize = qio_channel_stall_finalize,
+    .class_init = qio_channel_stall_class_init,
+};
+
+static QIOChannelStall *qio_channel_stall_new(QIOChannel *master,
+                                              unsigned rstalls,
+                                              unsigned wstalls)
+{
+    QIOChannelStall *sioc = QIO_CHANNEL_STALL(
+        object_new(TYPE_QIO_CHANNEL_STALL));
+
+    object_ref(OBJECT(master));
+    sioc->master = master;
+    sioc->rstalls = rstalls;
+    sioc->wstalls = wstalls;
+
+    return sioc;
+}
+
+typedef struct {
+    bool finished;
+    bool failed;
+} QIOChannelWebsockHandshake;
+
+static void test_websock_handshake_done(QIOTask *task, gpointer opaque)
+{
+    QIOChannelWebsockHandshake *res = opaque;
+
+    res->finished = true;
+    res->failed = qio_task_propagate_error(task, NULL);
+}
+
+/*
+ * Drives a server-side handshake against @request and returns whatever
+ * the server wrote back, NUL terminated. The handshake is expected to
+ * fail; the point of the test is the HTTP response that goes with it.
+ */
+static char *test_websock_handshake_reply(const char *request,
+                                          unsigned rstalls, unsigned wstalls)
+{
+    QIOChannelWebsockHandshake res = { false, false };
+    QIOChannelSocket *cli, *srv;
+    QIOChannelStall *stall;
+    QIOChannelWebsock *wioc;
+    GMainContext *mainloop;
+    int channel[2];
+    char *reply;
+    ssize_t got;
+
+    g_assert(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0);
+
+    cli = qio_channel_socket_new_fd(channel[0], &error_abort);
+    srv = qio_channel_socket_new_fd(channel[1], &error_abort);
+    qio_channel_set_blocking(QIO_CHANNEL(srv), false, &error_abort);
+    qio_channel_set_blocking(QIO_CHANNEL(cli), false, &error_abort);
+
+    stall = qio_channel_stall_new(QIO_CHANNEL(srv), rstalls, wstalls);
+    wioc = qio_channel_websock_new_server(QIO_CHANNEL(stall));
+    qio_channel_websock_handshake(wioc, test_websock_handshake_done,
+                                  &res, NULL);
+
+    qio_channel_write_all(QIO_CHANNEL(cli), request, strlen(request),
+                          &error_abort);
+
+    mainloop = g_main_context_default();
+    while (!res.finished) {
+        g_main_context_iteration(mainloop, TRUE);
+    }
+    g_assert(res.failed);
+
+    reply = g_malloc0(1024);
+    got = qio_channel_read(QIO_CHANNEL(cli), reply, 1023, &error_abort);
+    if (got > 0) {
+        reply[got] = '\0';
+    }
+
+    object_unref(OBJECT(wioc));
+    object_unref(OBJECT(stall));
+    object_unref(OBJECT(srv));
+    object_unref(OBJECT(cli));
+
+    return reply;
+}
+
+static void test_websock_bad_request(const void *opaque)
+{
+    const char *request = opaque;
+    g_autofree char *reply = test_websock_handshake_reply(request, 0, 0);
+
+    g_assert_true(g_str_has_prefix(reply, "HTTP/1.1 400 Bad Request\r\n"));
+}
+
+static void test_websock_stalled_read(const void *opaque)
+{
+    const char *request = opaque;
+    g_autofree char *reply = test_websock_handshake_reply(request, 1, 0);
+
+    g_assert_true(g_str_has_prefix(reply, "HTTP/1.1 400 Bad Request\r\n"));
+}
+
+static void test_websock_stalled_write(const void *opaque)
+{
+    const char *request = opaque;
+    g_autofree char *reply = test_websock_handshake_reply(request, 0, 1);
+
+    g_assert_true(g_str_has_prefix(reply, "HTTP/1.1 400 Bad Request\r\n"));
+}
+
+int main(int argc, char **argv)
+{
+    module_call_init(MODULE_INIT_QOM);
+    type_register_static(&qio_channel_stall_info);
+    g_test_init(&argc, &argv, NULL);
+
+#define TEST_BAD_REQUEST(name, request)                         \
+    g_test_add_data_func("/io/channel/websock/bad-request/" name, \
+                         request, test_websock_bad_request)
+
+    /*
+     * A greeting with no space at all used to leave the response buffer
+     * empty, which drove the handshake into a zero length write.
+     */
+    TEST_BAD_REQUEST("no-space", "stats\r\nx\r\n\r\n");
+    TEST_BAD_REQUEST("method-only", "GET\r\nx\r\n\r\n");
+    TEST_BAD_REQUEST("no-version", "GET /\r\nx\r\n\r\n");
+    TEST_BAD_REQUEST("bad-method", "POST / HTTP/1.1\r\nx: y\r\n\r\n");
+    TEST_BAD_REQUEST("bad-version", "GET / HTTP/1.0\r\nx: y\r\n\r\n");
+
+    /* A read which blocks before any header arrives is not a fatal error. */
+    g_test_add_data_func("/io/channel/websock/stalled-read",
+                         "stats\r\nx\r\n\r\n", test_websock_stalled_read);
+    g_test_add_data_func("/io/channel/websock/stalled-write",
+                         "stats\r\nx\r\n\r\n", test_websock_stalled_write);
+
+    return g_test_run();
+}
diff -Nru qemu-10.0.13+ds/ui/cursor.c qemu-10.0.14+ds/ui/cursor.c
--- qemu-10.0.13+ds/ui/cursor.c 2026-08-26 22:31:26.000000000 +0300
+++ qemu-10.0.14+ds/ui/cursor.c 2026-09-29 03:48:21.000000000 +0300
@@ -1,4 +1,5 @@
 #include "qemu/osdep.h"
+#include "qemu/atomic.h"
 #include "ui/console.h"
 
 #include "cursor_hidden.xpm"
@@ -103,24 +104,28 @@
     c = g_malloc0(sizeof(QEMUCursor) + datasize);
     c->width  = width;
     c->height = height;
-    c->refcount = 1;
+    qatomic_set(&c->refcount, 1);
     return c;
 }
 
 QEMUCursor *cursor_ref(QEMUCursor *c)
 {
-    c->refcount++;
+    qatomic_inc(&c->refcount);
     return c;
 }
 
 void cursor_unref(QEMUCursor *c)
 {
+    int refcount;
+
     if (c == NULL)
         return;
-    c->refcount--;
-    if (c->refcount)
-        return;
-    g_free(c);
+
+    refcount = qatomic_fetch_dec(&c->refcount);
+    assert(refcount > 0);
+    if (refcount == 1) {
+        g_free(c);
+    }
 }
 
 int cursor_get_mono_bpl(QEMUCursor *c)

Reply via email to