[ 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 <mjt@tls.msk.ru> 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(®ion);
+ 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']