Bug#1146097: trixie-pu: package qemu/1:10.0.13+ds-0+deb13u1
Michael Tokarev <[email protected]>
| Newsgroups | gmane.linux.debian.devel.release |
|---|---|
| Message-ID | <178802738717.14545.8345953651327423814.reportbug__12481.6086304904$1788027601$gmane$org@localhost> |
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 minor/stable/bugfix release of qemu, fixing a fair number of various issues in all areas, including numerous security issues found so far. [ Tests ] This release passes the usual upstream testsuite, which is the main indicator of its correctness. The majority of fixes are small and touches just the problematic areas, and all of them are additionally tested in the upstream master branch before being back-ported to the stable branches. Additionally I tested it lightly on my collection of various guest images (several windows VMs, a few linux VMs, a FreeBSD VM and a Hurd VM) - the basic functionality is ok. [ Risks ] This is a release with a rather big number of changes, so it might be more risky than usual. However, history of previous qemu stable/bugfix releases shows it's usually non-problematic. I don't expect issues from this release too. [ 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 ] There's a debian/changelog in the debdiff below, with a list of upstream commits between upstream releases with the tags v10.0.12 and v10.0.13. Individual commits with all explanations are available on the salsa qemu repository, starting from https://salsa.debian.org/qemu-team/qemu/-/commits/v10.0.13 . There's no changes in debian packaging, only reference to the new version in d/control and d/changelog. [ Other info ] The debdiff is against previous release of qemu which is proposed for trixie, but which hasn't been accepted yet -- 1:10.0.12+ds-0+deb13u1. It would be nice if this release of qemu is available in trixie-proposed-updates before entering actual release, - despite there's low risk, but just in case. Thanks, /mjt diff -Nru qemu-10.0.12+ds/debian/changelog qemu-10.0.13+ds/debian/changelog --- qemu-10.0.12+ds/debian/changelog 2026-07-25 09:35:02.000000000 +0300 +++ qemu-10.0.13+ds/debian/changelog 2026-08-29 20:55:40.000000000 +0300 @@ -1,3 +1,295 @@ +qemu (1:10.0.13+ds-0+deb13u1) trixie; urgency=medium + + * new upstream stable/bugfix release, including multiple security fixes: + - Update version for 10.0.13 release + - target/riscv/tcg: sret in virtual user mode + raises virtual instruction exception + https://gitlab.com/qemu-project/qemu/-/work_items/3622 + - target/riscv: enforce even register constraints for Zdinx fcvt pairs + https://gitlab.com/qemu-project/qemu/-/work_items/4109 + - target/riscv: reject FMV.X.W/FMV.W.X under Zfinx + https://gitlab.com/qemu-project/qemu/-/work_items/4108 + - target/riscv: honor zicbo* envcfg gating in linux-user mode + https://gitlab.com/qemu-project/qemu/-/work_items/4107 + - disas/riscv: Fix typo in th.lbib format + - disas/riscv: Fix isa decoding of rev8 + - disas/riscv: Fix rv32 encoding of zext.h + - target/riscv: allow menvcfg/henvcfg LPE and SSE bits on RV32 + https://gitlab.com/qemu-project/qemu/-/work_items/4045 + - hw/riscv/riscv-iommu: preserve requested perm in spa_fetch() + - hw/riscv/riscv-iommu: fix U-bit check to apply only to + leaf S/VS-stage PTEs + - disas/riscv: Decode unsigned vector immediates as unsigned + - disas/riscv: Use signed type for vector immediates + - disas/riscv: Fix 6-bit immediate extraction + - disas/riscv: Fix th.srri decoding + - target/riscv: use SXL instead of MXL for read_sstatus + - target/riscv: Fix PC sync in trans_sspopchk for CFI exception handling + https://gitlab.com/qemu-project/qemu/-/work_items/4118 + - hw/watchdog: Add lower bound check for watchdogNumber + https://gitlab.com/qemu-project/qemu/-/work_items/3600 + - tcg: Export tcg_gen_ussub_i{32,64,tl} + - tcg: Defer tb_flush when initial thread region alloc fails + https://gitlab.com/qemu-project/qemu/-/work_items/2984 + - tcg: Return success from tcg_region_alloc + - tcg: Return success from tcg_region_alloc__locked + - target/loongarch: check FPE before reading fcc in bceqz/bcnez + https://gitlab.com/qemu-project/qemu/-/work_items/4209 + - meson: make linker warnings non-fatal on Linux + - serial: clear transmit retry callback on unrealize + https://gitlab.com/qemu-project/qemu/-/work_items/4125 + - target/i386: decode opcode extensions group 3 /1 as TEST + https://gitlab.com/qemu-project/qemu/-/work_items/3580 + - target/i386: allow transition to virtual-8086 mode + only if CPL == 0 and CPU is not in long mode + https://gitlab.com/qemu-project/qemu/-/work_items/3583 + - target/i386: fix long mode segment override prefix decoding + https://gitlab.com/qemu-project/qemu/-/work_items/3391 + - target/i386: fix incorrect decoding of EXTRQ_i + https://gitlab.com/qemu-project/qemu/-/work_items/3611 + - target/i386: Clear OF, SF, and AF for fcomi/fucomi + https://gitlab.com/qemu-project/qemu/-/work_items/4133 + - target/i386: Use correct type for get_float_exception_flags() values + - tcg/optimize: Fix s_mask computation for shifts + - tcg/optimize: INDEX_op_mul is commutative + - hw/elf_ops: defend against weird elf headers + - hw/nvme: add SPDM_SOCKET Kconfig dependency + - hw/block/pflash_cfi01: Restore ROMD mode after migration + https://gitlab.com/qemu-project/qemu/-/work_items/4042 + - hw/net/rtl8139: Send whole of vlan-tagged packet when doing loopback + - hw/net/rtl8139: Fix handling of VLAN tags on incoming short packets + https://gitlab.com/qemu-project/qemu/-/work_items/3518 + - tests/qtest/ahci: regression test for ATAPI read vs. drain + - hw/ide/atapi: read the whole elementary transfer asynchronously + - tests/qtest/ahci: cover raw (2352-byte) ATAPI CD reads + - tests/qtest/libqos/ahci: support raw (2352-byte) READ CD + - tests/qtest/ide-test: cover raw (2352-byte) ATAPI CD reads + - tests/qtest/ide-test: add a multi-sector ATAPI DMA read test + - tests/qtest/ide-test: parametrize the ATAPI CD-ROM read test + - hw/net/vmxnet3: Do not abort if guest provides bad interrupt numbers + https://gitlab.com/qemu-project/qemu/-/work_items/539 + - hw/usb/dev-uas: Don't abort if guest provided an undersized buffer + for status + https://gitlab.com/qemu-project/qemu/-/work_items/3900 + - hw/intc/loongarch_pch_pic: Validate htmsi_vector before indexing + parent_irq + https://gitlab.com/qemu-project/qemu/-/work_items/4114 + - migration/multifd: Replace assert() with error_setg() in recv paths + - migration/multifd: Validate next_packet_size in zlib/zstd recv + https://gitlab.com/qemu-project/qemu/-/work_items/3737 + - tests/tcg/s390x: Test STCKF condition code on a faulting store + - target/s390x/tcg: Set STCK/STCKF condition code after the store + - pc-bios/s390-ccw.img: update s390x bios + - pc-bios/s390-ccw: bound zipl menu strlen and replace VLA + in zipl_print_entry + - pc-bios/s390-ccw: bounds-check zipl menu entry index before array write + - pc-bios/s390-ccw: fix out-of-bounds read in iso_get_file_size() + - s390x/ipl: validate num_comp against iplb length before iterating + - hw/char/sclpconsole-lm: avoid guest triggerable assert + - tests/tcg/s390x: Test DR overflow (INT64_MIN / -1) + - target/s390x: Fix DR/D INT64_MIN / -1 host crash + - tests/tcg/s390x: Test PRNO TRNG interruptibility + - target/s390x: Make PRNO TRNG interruptible + - target/s390x: Have MSA helper pass a mmu_idx argument + - hw/arm/aspeed: Add missing Kconfig dependencies on required components + - linux-user: implement mount_setattr(2) + - linux-user/sh4: Fix crashes on signal delivery in conditional delay slot + - linux-user/sh4: Initialize the FPSCR register on signal + - linux-user/sh4: Deliver SIGILL on invalid instruction + - gitlab: disable provenance attestations to work around CI bug + - hw/nvme: fix leak on copy ranges + - hw/nvme: cancel inflight requests on controller reset + https://gitlab.com/qemu-project/qemu/-/work_items/3398 + https://gitlab.com/qemu-project/qemu/-/work_items/3883 + https://gitlab.com/qemu-project/qemu/-/work_items/4068 + https://gitlab.com/qemu-project/qemu/-/work_items/4072 + - hw/nvme: factor out nvme_sq_cancel_inflight() + - hw/nvme: drop AER requests without aiocb in nvme_del_sq() + - hw/display/virtio-gpu: Unmap DMA regions on reset + https://gitlab.com/qemu-project/qemu/-/work_items/3467 + - hw/display/virtio-gpu: Always reject invalid scanout bounds + - virtio-gpu: reject requests with short/truncated control headers + Closes: CVE-2026-18054 + - hw/display/virtio-gpu: fix offset wraparound in scanout_blob_to_fb + - vhost-user-gpu: fix integer overflow in buffer allocation + Closes: CVE-2026-15264 + - hw/display/vga: fix panning_buf OOB after text/graphics switch + https://gitlab.com/qemu-project/qemu/-/work_items/4085 + Closes: CVE-2026-17516 + - hw/display/virtio-gpu: validate blob iov size + https://gitlab.com/qemu-project/qemu/-/work_items/3945 + Closes: CVE-2026-66021 + - coroutine: fix lost wakeup in qemu_co_sleep_wake() + - iotests: run the test pool with the 'fork' start method + - qcow2: do not try to clear the dirty bit on a read-only node + - dmg: reject inconsistent UDRW chunk sector count and length + https://gitlab.com/qemu-project/qemu/-/work_items/3846 + Closes: CVE-2026-65928 + - dmg: refuse to open files with no chunks + https://gitlab.com/qemu-project/qemu/-/work_items/4021 + - dmg: fix out-of-bounds load in search_chunk() + https://gitlab.com/qemu-project/qemu/-/work_items/3844 + Closes: CVE-2026-65929 + - tests/unit: add reproducer for BlockAcctStats histogram locking race + - block/qapi: take stats->lock when reading BlockAcctStats + for query-blockstats + - block/accounting: take stats->lock in latency histogram setters + - block: Fix crash after setting latency historygram with single bin + - block/cloop: fix integer overflow in total_sectors calculation + - linux-user: fix incorrect msg_l[sr]pid members of target_msqid_ds + - linux-user: Fix msqid_ds struct wrt 32-bit big endian architectures + - hw/nvme: fix assertion failure on subregion removal + - hw/nvme: fix unintentional integer overflow in shift + - hw/nvme: fix cross-namespace copy dif buffer overflow + - virtio-mmio: fix QUEUE_NUM_MAX + https://gitlab.com/qemu-project/qemu/-/work_items/3882 + Closes: CVE-2026-50626 + - hw/display/exynos4210_fimd: Clamp windows to screen size + https://gitlab.com/qemu-project/qemu/-/work_items/3795 + - hw/display/exynos4210_fimd: Pass width to draw_line functions + - hw/display/exynos4210_fimd: Factor out finding screen width/height + - hw/i2c/bcm2835_i2c: Correct CLKT register offset + - linux-user/sh4: allow full 32-bit address space + - target/sparc: set reg window data structures currently + after vmstate load + - hw/net/igb: recalculate rx_desc_len on migration load + - hw/net/e1000e: recalculate rx_desc_len on migration load + - linux-user: fix guards for the fsmount(2) syscall series + - hw/net/xilinx_axienet: Don't write checksums off end of packet + - linux-user: Guard local FUTEX_CMD_MASK definition + - virtio: avoid packed vring virtio_queue_empty() infinite loops + https://gitlab.com/qemu-project/qemu/-/work_items/3968 + Closes: CVE-2026-16457 + - backends/rng: cap request size to avoid oversized allocation + https://gitlab.com/qemu-project/qemu/-/work_items/3983 + - hw/virtio-rng: Fix host use-after-free + https://gitlab.com/qemu-project/qemu/-/work_items/3917 + Closes: CVE-2026-50624 + - hw/net/virtio-net: Protect from DMA re-entrancy bugs + https://gitlab.com/qemu-project/qemu/-/work_items/4073 + Closes: CVE-2026-66022 (again) + - intel_iommu: Check address mask before using it in + pasid-based iotlb invalidation + https://gitlab.com/qemu-project/qemu/-/work_items/3619 + - hw/cxl: fix OOB access in cxl_doe_cdat_rsp via entry_handle + - hw/virtio/vdpa-dev: pass set_config buffer to vhost backend + - hw/pci-host/q35.c: Avoid early return in mch_write_config() + - hw/pci-host/q35.c: Factor out creation of SMRAM MRs + - hw/pci-host/q35.c: Always initialize smram-region even if SMM disabled + - virtio-iommu: fix OOM due to unbounded call_rcu + - libvduse: validate vq size + https://gitlab.com/qemu-project/qemu/-/work_items/3652 + Closes: CVE-2026-61402 + - libvhost-user: fix heap overflow in vu_check_queue_inflights + https://gitlab.com/qemu-project/qemu/-/work_items/3974 + Closes: CVE-2026-63110 + - libvhost-user: validate last_batch_head in vu_check_queue_inflights + https://gitlab.com/qemu-project/qemu/-/work_items/3974 + - virtio-pmem: wait for flush requests on unrealize + https://gitlab.com/qemu-project/qemu/-/work_items/3938 + Closes: CVE-2026-63323 + - vhost-user: assert nregions within limit + https://gitlab.com/qemu-project/qemu/-/work_items/3910 + - virtio: fail early on bad config_len in migration + https://gitlab.com/qemu-project/qemu/-/work_items/3891 + - virtio-scsi: fix SCSIRequest leak on a bad request + https://gitlab.com/qemu-project/qemu/-/work_items/3875 + Closes: CVE-2026-61476 + - vhost: do not crash on ring map failure + https://gitlab.com/qemu-project/qemu/-/work_items/3783 + - hw/virtio: reject zero-length packed indirect descriptor table + https://gitlab.com/qemu-project/qemu/-/work_items/3984 + - libvhost-user: protect against OOB vring queue access + https://gitlab.com/qemu-project/qemu/-/work_items/3741 + - libvhost-user: protect against OOB writes in vu_set_inflight_fd + https://gitlab.com/qemu-project/qemu/-/work_items/3740 + - virtio-net: fix short frame OOB read in receive_filter() + https://gitlab.com/qemu-project/qemu/-/work_items/3626 + Closes: CVE-2026-63320 + - virtio-net: fix OOB read in RSC receive path + https://gitlab.com/qemu-project/qemu/-/work_items/3623 + Closes: CVE-2026-63321 + - hw/display/virtio-gpu: Block Rutabaga migration + - rutabaga: improve error handling, fix potential crash during init + - hw/display/virtio-gpu: Initialize blob mapping for ATTACH_BACKING + - hw/display/virtio-gpu: Fix empty blob discrimination + - virtio-gpu: fix NULL deref in rutabaga set_scanout + https://gitlab.com/qemu-project/qemu/-/work_items/3897 + - target/i386/sev: fix MemoryRegion reference leaks in gpa2hva callers + - net/colo: fix g_hash_table_destroy assertion on uninitialized filter + - hw/display/qxl: unregister vm_change_state handler and BHs + https://gitlab.com/qemu-project/qemu/-/work_items/3607 + Closes: CVE-2026-63322 + - ui/vnc: remove redundant rows computation + - hw/display/vhost-user-gpu: validate message payload sizes + https://gitlab.com/qemu-project/qemu/-/work_items/3866 + - hw/display/virtio-gpu: Remove the bytes_pp field + - hw/display/virtio-gpu: reject strides exceeding INT_MAX + - hw/display/virtio-gpu: validate stride against width on scanout + https://gitlab.com/qemu-project/qemu/-/work_items/3989 + Closes: CVE-2026-63109 + - hw/usb/hcd-xhci: Check return value of xhci_xfer_create_sgl() + for errors + https://gitlab.com/qemu-project/qemu/-/work_items/3786 + - hw/usb/core: Avoid possible assert() in do_parameter() --> + usb_packet_copy() + https://gitlab.com/qemu-project/qemu/-/work_items/3746 + - hw/ide/core: Fix possible crash via NULL pointer + in ide_cancel_dma_sync() + https://gitlab.com/qemu-project/qemu/-/work_items/905 + https://gitlab.com/qemu-project/qemu/-/work_items/4052 + - hw/usb/dev-uas: Fix guest-triggerable heap OOB access + https://gitlab.com/qemu-project/qemu/-/work_items/3612 + https://gitlab.com/qemu-project/qemu/-/work_items/3986 + - hw/9pfs/xen: drain in-flight PDUs before xen-9p disconnect + - hw/9pfs/virtio: drain in-flight PDUs before virtio-9p unrealize + https://gitlab.com/qemu-project/qemu/-/work_items/3937 + - hw/9pfs: fix O_TRUNC bypass on read-only export + https://gitlab.com/qemu-project/qemu/-/work_items/4000 + Closes: CVE-2026-63318 + - hw/display/vmware_vga: Don't allow guest to trigger + long running loop in host + https://gitlab.com/qemu-project/qemu/-/work_items/3782 + https://gitlab.com/qemu-project/qemu/-/work_items/4026 + https://gitlab.com/qemu-project/qemu/-/work_items/4076 + - hw/ide: replace assert with proper error handling + https://gitlab.com/qemu-project/qemu/-/work_items/2777 + - scsi-disk: fix off by one in assertion + - scsi-disk: protect against guest sending truncated data + for MODE SELECT commands + https://gitlab.com/qemu-project/qemu/-/work_items/4051 + - target/i386: helper_sysret(): Check that RCX contains + a canonical address when emulating an Intel CPU + https://gitlab.com/qemu-project/qemu/-/work_items/3223 + - vfio/pci: reject invalid PCI_INTERRUPT_PIN values + - vfio/pci: don't narrow a failed config read to a plausible value + - vfio/igd: Clear saved BDSM in legacy VBIOS ROM at load time + https://gitlab.com/qemu-project/qemu/-/work_items/3093 + - vfio/pci: Initialize rom_read_failed in vfio_pci_load_rom() + - hw/uefi: make SetupMode read-only + https://gitlab.com/qemu-project/qemu/-/work_items/4039 + Closes: CVE-2026-16288 + - hw/uefi: add post_load checks + https://gitlab.com/qemu-project/qemu/-/work_items/3837 + https://gitlab.com/qemu-project/qemu/-/work_items/3838 + https://gitlab.com/qemu-project/qemu/-/work_items/3839 + https://gitlab.com/qemu-project/qemu/-/work_items/3885 + Closes: CVE-2026-61404 + - hw/uefi: account variable policy entries against storage size + https://gitlab.com/qemu-project/qemu/-/work_items/3890 + Closes: CVE-2026-61405 + - hw/uefi: check lower limit for signature list size + https://gitlab.com/qemu-project/qemu/-/work_items/3899 + Closes: CVE-2026-61406 + - hw/uefi: remove debug function + https://gitlab.com/qemu-project/qemu/-/work_items/3615 + Closes: CVE-2026-58582 + - hw/uefi: add sanity check + https://gitlab.com/qemu-project/qemu/-/work_items/3614 + Closes: CVE-2026-58581 + + -- Michael Tokarev <[email protected]> Sat, 29 Aug 2026 20:55:40 +0300 + qemu (1:10.0.12+ds-0+deb13u1) trixie; urgency=medium * new upstream stable/bugfix release: diff -Nru qemu-10.0.12+ds/debian/control.mk qemu-10.0.13+ds/debian/control.mk --- qemu-10.0.12+ds/debian/control.mk 2026-07-25 09:35:02.000000000 +0300 +++ qemu-10.0.13+ds/debian/control.mk 2026-08-29 20:55:40.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.12+ds +checked-version := 10.0.13+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.12+ds/.gitlab-ci.d/container-template.yml qemu-10.0.13+ds/.gitlab-ci.d/container-template.yml --- qemu-10.0.12+ds/.gitlab-ci.d/container-template.yml 2026-07-25 01:10:12.000000000 +0300 +++ qemu-10.0.13+ds/.gitlab-ci.d/container-template.yml 2026-08-26 22:31:25.000000000 +0300 @@ -14,7 +14,7 @@ - echo "TAG:$TAG" - echo "COMMON_TAG:$COMMON_TAG" - docker build --tag "$TAG" --cache-from "$TAG" --cache-from "$COMMON_TAG" - --build-arg BUILDKIT_INLINE_CACHE=1 + --build-arg BUILDKIT_INLINE_CACHE=1 --provenance=false -f "tests/docker/dockerfiles/$NAME.docker" "." - docker push "$TAG" after_script: diff -Nru qemu-10.0.12+ds/VERSION qemu-10.0.13+ds/VERSION --- qemu-10.0.12+ds/VERSION 2026-07-25 01:10:12.000000000 +0300 +++ qemu-10.0.13+ds/VERSION 2026-08-26 22:31:25.000000000 +0300 @@ -1 +1 @@ -10.0.12 +10.0.13 diff -Nru qemu-10.0.12+ds/backends/rng.c qemu-10.0.13+ds/backends/rng.c --- qemu-10.0.12+ds/backends/rng.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/backends/rng.c 2026-08-26 22:31:25.000000000 +0300 @@ -11,11 +11,14 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "system/rng.h" #include "qapi/error.h" #include "qemu/module.h" #include "qom/object_interfaces.h" +#define RNG_MAX_REQUEST_SIZE (64 * KiB) + void rng_backend_request_entropy(RngBackend *s, size_t size, EntropyReceiveFunc *receive_entropy, void *opaque) @@ -27,7 +30,7 @@ req = g_malloc(sizeof(*req)); req->offset = 0; - req->size = size; + req->size = MIN(size, RNG_MAX_REQUEST_SIZE); req->receive_entropy = receive_entropy; req->opaque = opaque; req->data = g_malloc(req->size); @@ -68,6 +71,22 @@ g_free(req); } +void rng_backend_cancel_requests(RngBackend *s, + EntropyReceiveFunc *receive_entropy, + const void *opaque) +{ + RngRequest *req, *next; + + QSIMPLEQ_FOREACH_SAFE(req, &s->requests, next, next) { + if (req->receive_entropy != receive_entropy || + req->opaque != opaque) { + continue; + } + QSIMPLEQ_REMOVE(&s->requests, req, RngRequest, next); + rng_backend_free_request(req); + } +} + static void rng_backend_free_requests(RngBackend *s) { RngRequest *req, *next; diff -Nru qemu-10.0.12+ds/block/accounting.c qemu-10.0.13+ds/block/accounting.c --- qemu-10.0.12+ds/block/accounting.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/block/accounting.c 2026-08-26 22:31:25.000000000 +0300 @@ -173,6 +173,17 @@ prev = entry->value; } + /* + * block_latency_histogram_account() assumes that it can always access + * hist->boundaries[0], so require at least one boundary. A histogram with + * a single bin is useless anyway. + */ + if (new_nbins <= 1) { + return -EINVAL; + } + + qemu_mutex_lock(&stats->lock); + hist->nbins = new_nbins; g_free(hist->boundaries); hist->boundaries = g_new(uint64_t, hist->nbins - 1); @@ -185,6 +196,8 @@ g_free(hist->bins); hist->bins = g_new0(uint64_t, hist->nbins); + qemu_mutex_unlock(&stats->lock); + return 0; } @@ -192,12 +205,16 @@ { int i; + qemu_mutex_lock(&stats->lock); + for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { BlockLatencyHistogram *hist = &stats->latency_histogram[i]; g_free(hist->bins); g_free(hist->boundaries); memset(hist, 0, sizeof(*hist)); } + + qemu_mutex_unlock(&stats->lock); } static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, @@ -289,10 +306,9 @@ uint64_t sum, elapsed; assert(type < BLOCK_MAX_IOTYPE); + assert(qemu_mutex_trylock(&stats->stats->lock) == -EBUSY); - qemu_mutex_lock(&stats->stats->lock); sum = timed_average_sum(&stats->latency[type], &elapsed); - qemu_mutex_unlock(&stats->stats->lock); return (double) sum / elapsed; } diff -Nru qemu-10.0.12+ds/block/cloop.c qemu-10.0.13+ds/block/cloop.c --- qemu-10.0.12+ds/block/cloop.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/block/cloop.c 2026-08-26 22:31:25.000000000 +0300 @@ -202,7 +202,8 @@ s->current_block = s->n_blocks; s->sectors_per_block = s->block_size/512; - bs->total_sectors = s->n_blocks * s->sectors_per_block; + /* Cast to uint64_t to prevent uint32_t overflow */ + bs->total_sectors = (uint64_t)s->n_blocks * s->sectors_per_block; qemu_co_mutex_init(&s->lock); return 0; diff -Nru qemu-10.0.12+ds/block/dmg.c qemu-10.0.13+ds/block/dmg.c --- qemu-10.0.12+ds/block/dmg.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/block/dmg.c 2026-08-26 22:31:25.000000000 +0300 @@ -312,6 +312,21 @@ goto fail; } + /* + * Uncompressed chunk length must match sector count. Compressed chunks + * are validated during dmg_read_chunk() since the uncompressed size is + * not known ahead of time. + */ + if (s->types[i] == UDRW) { + if (s->sectorcounts[i] != DIV_ROUND_UP(s->lengths[i], 512)) { + error_report("length %" PRIu64 " for chunk %" PRIu32 + " is inconsistent with sector count %" PRIu64, + s->lengths[i], i, s->sectorcounts[i]); + ret = -EINVAL; + goto fail; + } + } + update_max_chunk_size(s, i, &ds->max_compressed_size, &ds->max_sectors_per_chunk); offset += 40; @@ -559,6 +574,12 @@ goto fail; } + /* There must be at least one chunk */ + if (s->n_chunks == 0) { + ret = -EINVAL; + goto fail; + } + /* initialize zlib engine */ s->compressed_chunk = qemu_try_blockalign(bs->file->bs, ds.max_compressed_size + 1); @@ -609,7 +630,10 @@ static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num) { /* binary search */ - uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; + uint32_t chunk1 = 0, chunk2 = s->n_chunks - 1, chunk3; + if (s->n_chunks == 0) { + goto err; /* should never happen */ + } while (chunk1 <= chunk2) { chunk3 = (chunk1 + chunk2) / 2; if (s->sectors[chunk3] > sector_num) { @@ -713,6 +737,16 @@ if (ret < 0) { return -1; } + + /* + * Zero the unread part of the last sector when chunk length is + * unaligned to avoid exposing uninitialized memory. Valid image + * files may never hit this case, but cover it to be safe. + */ + if (s->lengths[chunk] & 511) { + size_t trailing_bytes = 512 - (s->lengths[chunk] & 511); + memset(s->uncompressed_chunk + s->lengths[chunk], 0, trailing_bytes); + } break; case UDZE: /* zeros */ case UDIG: /* ignore */ diff -Nru qemu-10.0.12+ds/block/qapi.c qemu-10.0.13+ds/block/qapi.c --- qemu-10.0.12+ds/block/qapi.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/block/qapi.c 2026-08-26 22:31:25.000000000 +0300 @@ -498,6 +498,8 @@ BlockAcctTimedStats *ts = NULL; BlockLatencyHistogram *hgram; + qemu_mutex_lock(&stats->lock); + ds->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ]; ds->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE]; ds->zone_append_bytes = stats->nr_bytes[BLOCK_ACCT_ZONE_APPEND]; @@ -587,6 +589,7 @@ = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_ZONE_APPEND]); ds->flush_latency_histogram = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_FLUSH]); + qemu_mutex_unlock(&stats->lock); } static BlockStats * GRAPH_RDLOCK diff -Nru qemu-10.0.12+ds/block/qcow2.c qemu-10.0.13+ds/block/qcow2.c --- qemu-10.0.12+ds/block/qcow2.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/block/qcow2.c 2026-08-26 22:31:25.000000000 +0300 @@ -2784,7 +2784,11 @@ strerror(-ret)); } - if (result == 0) { + /* + * A read-only node cannot resolve an inherited dirty bit here; + * leave it dirty, same as plain read access already does. + */ + if (result == 0 && !bdrv_is_read_only(bs)) { qcow2_mark_clean(bs); } diff -Nru qemu-10.0.12+ds/contrib/vhost-user-gpu/vhost-user-gpu.c qemu-10.0.13+ds/contrib/vhost-user-gpu/vhost-user-gpu.c --- qemu-10.0.12+ds/contrib/vhost-user-gpu/vhost-user-gpu.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/contrib/vhost-user-gpu/vhost-user-gpu.c 2026-08-26 22:31:25.000000000 +0300 @@ -388,7 +388,13 @@ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return; } - vugbm_buffer_create(&res->buffer, &g->gdev, c2d.width, c2d.height); + if (!vugbm_buffer_create(&res->buffer, &g->gdev, c2d.width, c2d.height)) { + g_critical("%s: buffer creation failed %d %d %d", + __func__, c2d.resource_id, c2d.width, c2d.height); + g_free(res); + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } res->image = pixman_image_create_bits(pformat, c2d.width, c2d.height, @@ -924,16 +930,19 @@ if (len != sizeof(cmd->cmd_hdr)) { g_warning("%s: command size incorrect %zu vs %zu\n", __func__, len, sizeof(cmd->cmd_hdr)); - } - - virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); - g_debug("%d %s\n", cmd->cmd_hdr.type, - vg_cmd_to_string(cmd->cmd_hdr.type)); - - if (vg->virgl) { - vg_virgl_process_cmd(vg, cmd); + memset(&cmd->cmd_hdr, 0, sizeof(cmd->cmd_hdr)); + vg_ctrl_response_nodata( + vg, cmd, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER); } else { - vg_process_cmd(vg, cmd); + virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); + g_debug("%d %s\n", cmd->cmd_hdr.type, + vg_cmd_to_string(cmd->cmd_hdr.type)); + + if (vg->virgl) { + vg_virgl_process_cmd(vg, cmd); + } else { + vg_process_cmd(vg, cmd); + } } if (cmd->state != VG_CMD_STATE_FINISHED) { diff -Nru qemu-10.0.12+ds/contrib/vhost-user-gpu/vugbm.c qemu-10.0.13+ds/contrib/vhost-user-gpu/vugbm.c --- qemu-10.0.12+ds/contrib/vhost-user-gpu/vugbm.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/contrib/vhost-user-gpu/vugbm.c 2026-08-26 22:31:25.000000000 +0300 @@ -13,7 +13,7 @@ static bool mem_alloc_bo(struct vugbm_buffer *buf) { - buf->mmap = g_malloc(buf->width * buf->height * 4); + buf->mmap = g_malloc((uint64_t)buf->width * buf->height * 4); buf->stride = buf->width * 4; return true; } @@ -53,7 +53,8 @@ static size_t udmabuf_get_size(struct vugbm_buffer *buf) { - return ROUND_UP(buf->width * buf->height * 4, qemu_real_host_page_size()); + return ROUND_UP((uint64_t)buf->width * buf->height * 4, + qemu_real_host_page_size()); } static bool @@ -293,6 +294,12 @@ vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev, uint32_t width, uint32_t height) { + uint64_t size = (uint64_t)width * height * 4; + if (size > UINT32_MAX) { + g_warning("buffer dimensions too large: %ux%u", width, height); + return false; + } + buffer->dev = dev; buffer->width = width; buffer->height = height; diff -Nru qemu-10.0.12+ds/contrib/vhost-user-gpu/vugpu.h qemu-10.0.13+ds/contrib/vhost-user-gpu/vugpu.h --- qemu-10.0.12+ds/contrib/vhost-user-gpu/vugpu.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/contrib/vhost-user-gpu/vugpu.h 2026-08-26 22:31:25.000000000 +0300 @@ -179,6 +179,7 @@ if (vugpufillcmd_s_ != sizeof(out)) { \ g_critical("%s: command size incorrect %zu vs %zu", \ __func__, vugpufillcmd_s_, sizeof(out)); \ + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; \ return; \ } \ } while (0) diff -Nru qemu-10.0.12+ds/disas/riscv-xthead.c qemu-10.0.13+ds/disas/riscv-xthead.c --- qemu-10.0.12+ds/disas/riscv-xthead.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/disas/riscv-xthead.c 2026-08-26 22:31:25.000000000 +0300 @@ -189,7 +189,7 @@ { "th.mulsh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, /* XTheadMemIdx */ { "th.lbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, + { "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, { "th.lbuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, { "th.lbuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, { "th.lhia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, @@ -315,8 +315,10 @@ op = rv_op_th_revw; } break; - case 0b0000100: - case 0b0000101: op = rv_op_th_srri; break; + case 0b0001000: + case 0b0001001: + op = rv_op_th_srri; + break; } break; case 2: op = rv_op_th_ext; break; diff -Nru qemu-10.0.12+ds/disas/riscv.c qemu-10.0.13+ds/disas/riscv.c --- qemu-10.0.12+ds/disas/riscv.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/disas/riscv.c 2026-08-26 22:31:25.000000000 +0300 @@ -1801,19 +1801,19 @@ { "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, { "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsll.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vsrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vsra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnsrl.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnsrl.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnsra.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnsra.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, { "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, @@ -1907,16 +1907,16 @@ { "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, { "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vssrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vssrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vssra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vssra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnclipu.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnclipu.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnclip.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnclip.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, { "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, @@ -2046,15 +2046,15 @@ { "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2, NULL, 0, 0, 0 }, { "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, 0, 0, 0 }, { "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vslideup.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vslideup.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, { "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vslidedown.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vslidedown.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, { "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vrgather.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vrgather.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, { "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, { "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, @@ -2142,8 +2142,8 @@ { "vaesef.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, { "vaesem.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, { "vaesem.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaeskf1.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, - { "vaeskf2.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vaeskf1.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vaeskf2.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, { "vaesz.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, { "vandn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vandn.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, @@ -2167,14 +2167,14 @@ { "vsha2ch.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, { "vsha2cl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, { "vsha2ms.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vsm3c.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vsm3c.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, { "vsm3me.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vsm4k.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vsm4k.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, { "vsm4r.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, { "vsm4r.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, { "vwsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vwsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwsll.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vwsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, { "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, { "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, @@ -2972,8 +2972,16 @@ case 0b01100: op = rv_op_rori; break; case 0b01101: switch ((inst >> 20) & 0b1111111) { - case 0b0011000: op = rv_op_rev8; break; - case 0b0111000: op = rv_op_rev8; break; + case 0b0011000: + if (isa == rv32) { + op = rv_op_rev8; + } + break; + case 0b0111000: + if (isa == rv64) { + op = rv_op_rev8; + } + break; case 0b0000111: op = rv_op_brev8; break; } break; @@ -3182,9 +3190,10 @@ case 14: op = rv_op_rem; break; case 15: op = rv_op_remu; break; case 36: - switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_zext_h; break; - default: op = rv_op_pack; break; + if (isa == rv32 && !((inst >> 20) & 0b11111)) { + op = rv_op_zext_h; + } else { + op = rv_op_pack; } break; case 39: op = rv_op_packh; break; @@ -4479,11 +4488,16 @@ ((inst << 57) >> 62) << 6; } -static uint32_t operand_vimm(rv_inst inst) +static int32_t operand_vimm(rv_inst inst) { return (int64_t)(inst << 44) >> 59; } +static uint32_t operand_vuimm(rv_inst inst) +{ + return extract32(inst, 15, 5); +} + static uint32_t operand_vzimm11(rv_inst inst) { return (inst << 33) >> 53; @@ -4538,7 +4552,7 @@ static uint32_t operand_imm6(rv_inst inst) { - return (inst << 38) >> 60; + return extract32(inst, 20, 6); } static uint32_t operand_imm2(rv_inst inst) @@ -4887,6 +4901,12 @@ dec->imm = operand_vimm(inst); dec->vm = operand_vm(inst); break; + case rv_codec_v_i_u: + dec->rd = operand_rd(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = operand_vuimm(inst); + dec->vm = operand_vm(inst); + break; case rv_codec_vror_vi: dec->rd = operand_rd(inst); dec->rs2 = operand_rs2(inst); diff -Nru qemu-10.0.12+ds/disas/riscv.h qemu-10.0.13+ds/disas/riscv.h --- qemu-10.0.12+ds/disas/riscv.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/disas/riscv.h 2026-08-26 22:31:25.000000000 +0300 @@ -149,6 +149,7 @@ rv_codec_v_r, rv_codec_v_ldst, rv_codec_v_i, + rv_codec_v_i_u, rv_codec_vsetvli, rv_codec_vsetivli, rv_codec_vror_vi, diff -Nru qemu-10.0.12+ds/hw/9pfs/9p.c qemu-10.0.13+ds/hw/9pfs/9p.c --- qemu-10.0.12+ds/hw/9pfs/9p.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/9pfs/9p.c 2026-08-26 22:31:25.000000000 +0300 @@ -2144,8 +2144,8 @@ flags = omode_to_uflags(mode); } if (is_ro_export(&s->ctx)) { - if (mode & O_WRONLY || mode & O_RDWR || - mode & O_APPEND || mode & O_TRUNC) { + if (flags & O_WRONLY || flags & O_RDWR || + flags & O_APPEND || flags & O_TRUNC) { err = -EROFS; goto out; } @@ -4525,6 +4525,7 @@ qp_table_destroy(&s->qpp_table); qp_table_destroy(&s->qpf_table); g_free(s->ctx.fs_root); + s->transport = NULL; } typedef struct VirtfsCoResetData { diff -Nru qemu-10.0.12+ds/hw/9pfs/virtio-9p-device.c qemu-10.0.13+ds/hw/9pfs/virtio-9p-device.c --- qemu-10.0.12+ds/hw/9pfs/virtio-9p-device.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/9pfs/virtio-9p-device.c 2026-08-26 22:31:25.000000000 +0300 @@ -243,6 +243,7 @@ V9fsVirtioState *v = VIRTIO_9P(dev); V9fsState *s = &v->state; + v9fs_reset(s); /* cancel all in-flight PDUs to prevent UAF */ virtio_delete_queue(v->vq); virtio_cleanup(vdev); v9fs_device_unrealize_common(s); diff -Nru qemu-10.0.12+ds/hw/9pfs/xen-9p-backend.c qemu-10.0.13+ds/hw/9pfs/xen-9p-backend.c --- qemu-10.0.12+ds/hw/9pfs/xen-9p-backend.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/9pfs/xen-9p-backend.c 2026-08-26 22:31:25.000000000 +0300 @@ -68,6 +68,11 @@ static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev); +static void xen_9pfs_disconnect_bh(void *opaque) +{ + xen_9pfs_disconnect(opaque); +} + static void xen_9pfs_in_sg(Xen9pfsRing *ring, struct iovec *in_sg, int *num, @@ -150,7 +155,8 @@ "Failed to encode VirtFS reply type %d\n", pdu->id + 1); xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); - xen_9pfs_disconnect(&xen_9pfs->xendev); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + xen_9pfs_disconnect_bh, &xen_9pfs->xendev); } return ret; } @@ -173,7 +179,8 @@ xen_pv_printf(&xen_9pfs->xendev, 0, "Failed to decode VirtFS request type %d\n", pdu->id); xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); - xen_9pfs_disconnect(&xen_9pfs->xendev); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + xen_9pfs_disconnect_bh, &xen_9pfs->xendev); } return ret; } @@ -368,10 +375,16 @@ static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev) { Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); + V9fsState *s = &xen_9pdev->state; int i; trace_xen_9pfs_disconnect(xendev->name); + if (s->transport) { + v9fs_reset(s); /* cancel all in-flight PDUs to prevent UAF */ + v9fs_device_unrealize_common(s); + } + for (i = 0; i < xen_9pdev->num_rings; i++) { if (xen_9pdev->rings[i].evtchndev != NULL) { qemu_set_fd_handler(qemu_xen_evtchn_fd(xen_9pdev->rings[i].evtchndev), diff -Nru qemu-10.0.12+ds/hw/arm/Kconfig qemu-10.0.13+ds/hw/arm/Kconfig --- qemu-10.0.12+ds/hw/arm/Kconfig 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/arm/Kconfig 2026-08-26 22:31:25.000000000 +0300 @@ -541,12 +541,15 @@ select TMP105 select TMP421 select EMC141X + select OR_IRQ select UNIMP select LED select PMBUS select MAX31785 select FSI_APB2OPB_ASPEED select AT24C + select USB_EHCI_SYSBUS + select SDHCI config MPS2 bool diff -Nru qemu-10.0.12+ds/hw/block/pflash_cfi01.c qemu-10.0.13+ds/hw/block/pflash_cfi01.c --- qemu-10.0.12+ds/hw/block/pflash_cfi01.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/block/pflash_cfi01.c 2026-08-26 22:31:25.000000000 +0300 @@ -1038,6 +1038,16 @@ { PFlashCFI01 *pfl = opaque; + /* + * ROMD mode is not in the VMState; derive it from the migrated + * cmd and wcycle. Only (wcycle == 0, cmd == 0x00) is read-array. + */ + if (pfl->wcycle == 0 && pfl->cmd == 0x00) { + memory_region_rom_device_set_romd(&pfl->mem, true); + } else { + memory_region_rom_device_set_romd(&pfl->mem, false); + } + if (!pfl->ro) { pfl->vmstate = qemu_add_vm_change_state_handler(postload_update_cb, pfl); diff -Nru qemu-10.0.12+ds/hw/char/sclpconsole-lm.c qemu-10.0.13+ds/hw/char/sclpconsole-lm.c --- qemu-10.0.12+ds/hw/char/sclpconsole-lm.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/char/sclpconsole-lm.c 2026-08-26 22:31:25.000000000 +0300 @@ -243,7 +243,8 @@ SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); len = be16_to_cpu(data->mdb.header.length); - if (len < sizeof(data->mdb.header)) { + if (len < sizeof(data->mdb.header) || + len > be16_to_cpu(data->header.length) - sizeof(EventBufferHeader)) { return SCLP_RC_INCONSISTENT_LENGTHS; } len -= sizeof(data->mdb.header); diff -Nru qemu-10.0.12+ds/hw/char/serial.c qemu-10.0.13+ds/hw/char/serial.c --- qemu-10.0.12+ds/hw/char/serial.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/char/serial.c 2026-08-26 22:31:25.000000000 +0300 @@ -939,6 +939,7 @@ { SerialState *s = SERIAL(dev); + g_clear_handle_id(&s->watch_tag, g_source_remove); qemu_chr_fe_deinit(&s->chr, false); timer_free(s->modem_status_poll); diff -Nru qemu-10.0.12+ds/hw/display/exynos4210_fimd.c qemu-10.0.13+ds/hw/display/exynos4210_fimd.c --- qemu-10.0.12+ds/hw/display/exynos4210_fimd.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/display/exynos4210_fimd.c 2026-08-26 22:31:25.000000000 +0300 @@ -282,7 +282,7 @@ pixel_to_rgb_func *pixel_to_rgb; void (*draw_line)(Exynos4210fimdWindow *w, uint8_t *src, uint8_t *dst, - bool blend); + uint32_t width, bool blend); uint32_t (*get_alpha)(Exynos4210fimdWindow *w, uint32_t pix_a); uint16_t lefttop_x, lefttop_y; /* VIDOSD0 register */ uint16_t rightbot_x, rightbot_y; /* VIDOSD1 register */ @@ -781,9 +781,9 @@ /* Draw line with index in palette table in RAM frame buffer data */ #define DEF_DRAW_LINE_PALETTE(N) \ static void glue(draw_line_palette_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ - uint8_t *dst, bool blend) \ + uint8_t *dst, uint32_t width, \ + bool blend) \ { \ - int width = w->rightbot_x - w->lefttop_x + 1; \ uint8_t *ifb = dst; \ uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ uint64_t data; \ @@ -810,9 +810,8 @@ /* Draw line with direct color value in RAM frame buffer data */ #define DEF_DRAW_LINE_NOPALETTE(N) \ static void glue(draw_line_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ - uint8_t *dst, bool blend) \ + uint8_t *dst, uint32_t width, bool blend) \ { \ - int width = w->rightbot_x - w->lefttop_x + 1; \ uint8_t *ifb = dst; \ uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ uint64_t data; \ @@ -845,11 +844,10 @@ /* Special draw line routine for window color map case */ static void draw_line_mapcolor(Exynos4210fimdWindow *w, uint8_t *src, - uint8_t *dst, bool blend) + uint8_t *dst, uint32_t width, bool blend) { rgba p, p_old; uint8_t *ifb = dst; - int width = w->rightbot_x - w->lefttop_x + 1; uint32_t map_color = w->winmap & FIMD_WINMAP_COLOR_MASK; do { @@ -1249,15 +1247,25 @@ } } +static uint32_t exynos4210_fimd_global_width(Exynos4210fimdState *s) +{ + return ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) & + FIMD_VIDTCON2_SIZE_MASK) + 1; +} + +static uint32_t exynos4210_fimd_global_height(Exynos4210fimdState *s) +{ + return ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & + FIMD_VIDTCON2_SIZE_MASK) + 1; +} + static void exynos4210_update_resolution(Exynos4210fimdState *s) { DisplaySurface *surface = qemu_console_surface(s->console); /* LCD resolution is stored in VIDEO TIME CONTROL REGISTER 2 */ - uint32_t width = ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) & - FIMD_VIDTCON2_SIZE_MASK) + 1; - uint32_t height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & - FIMD_VIDTCON2_SIZE_MASK) + 1; + uint32_t width = exynos4210_fimd_global_width(s); + uint32_t height = exynos4210_fimd_global_height(s); if (s->ifb == NULL || surface_width(surface) != width || surface_height(surface) != height) { @@ -1283,22 +1291,37 @@ bool blend = false; uint8_t *host_fb_addr; bool is_dirty = false; - int global_width; + uint32_t global_width, global_height; + uint32_t window_width; if (!s || !s->console || !s->enabled || surface_bits_per_pixel(qemu_console_surface(s->console)) == 0) { return; } - global_width = (s->vidtcon[2] & FIMD_VIDTCON2_SIZE_MASK) + 1; + global_width = exynos4210_fimd_global_width(s); + global_height = exynos4210_fimd_global_height(s); exynos4210_update_resolution(s); surface = qemu_console_surface(s->console); for (i = 0; i < NUM_OF_WINDOWS; i++) { w = &s->window[i]; if ((w->wincon & FIMD_WINCON_ENWIN) && w->host_fb_addr) { - scrn_height = w->rightbot_y - w->lefttop_y + 1; + uint32_t rightbot_x, rightbot_y; + + if (w->lefttop_x >= global_width || + w->lefttop_y >= global_height) { + /* Guest has put the window entirely offscreen: ignore */ + continue; + } + + /* Clamp right corner coords to be within the screen */ + rightbot_x = MIN(w->rightbot_x, global_width - 1); + rightbot_y = MIN(w->rightbot_y, global_height - 1); + scrn_height = rightbot_y - w->lefttop_y + 1; scrn_width = w->virtpage_width; + /* Number of bytes to actually draw */ + window_width = rightbot_x - w->lefttop_x + 1; /* Total width of virtual screen page in bytes */ inc_size = scrn_width + w->virtpage_offsize; host_fb_addr = w->host_fb_addr; @@ -1317,7 +1340,8 @@ last_line = line; w->draw_line(w, host_fb_addr, s->ifb + w->lefttop_x * RGBA_SIZE + (w->lefttop_y + line) * - global_width * RGBA_SIZE, blend); + global_width * RGBA_SIZE, + window_width, blend); } host_fb_addr += inc_size; fb_line_addr += inc_size; diff -Nru qemu-10.0.12+ds/hw/display/qxl.c qemu-10.0.13+ds/hw/display/qxl.c --- qemu-10.0.12+ds/hw/display/qxl.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/display/qxl.c 2026-08-26 22:31:25.000000000 +0300 @@ -2223,7 +2223,8 @@ } #endif - qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); + qxl->vmstate_handler = + qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); qxl->update_irq = qemu_bh_new_guarded(qxl_update_irq_bh, qxl, &DEVICE(qxl)->mem_reentrancy_guard); @@ -2498,6 +2499,18 @@ DEFINE_PROP_BOOL("global-vmstate", PCIQXLDevice, vga.global_vmstate, false), }; +static void qxl_exit(PCIDevice *dev) +{ + PCIQXLDevice *qxl = PCI_QXL(dev); + + /* TODO: complete cleanup, error paths etc */ + g_clear_pointer(&qxl->vmstate_handler, qemu_del_vm_change_state_handler); + g_clear_pointer(&qxl->update_irq, qemu_bh_delete); + g_clear_pointer(&qxl->update_area_bh, qemu_bh_delete); + g_clear_pointer(&qxl->ssd.cursor_bh, qemu_bh_delete); + g_clear_pointer(&qxl->guest_surfaces.cmds, g_free); +} + static void qxl_pci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -2505,6 +2518,7 @@ k->vendor_id = REDHAT_PCI_VENDOR_ID; k->device_id = QXL_DEVICE_ID_STABLE; + k->exit = qxl_exit; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); device_class_set_legacy_reset(dc, qxl_reset_handler); dc->vmsd = &qxl_vmstate; diff -Nru qemu-10.0.12+ds/hw/display/qxl.h qemu-10.0.13+ds/hw/display/qxl.h --- qemu-10.0.12+ds/hw/display/qxl.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/display/qxl.h 2026-08-26 22:31:25.000000000 +0300 @@ -83,6 +83,7 @@ /* thread signaling */ QEMUBH *update_irq; + VMChangeStateEntry *vmstate_handler; /* ram pci bar */ QXLRam *ram; diff -Nru qemu-10.0.12+ds/hw/display/vga.c qemu-10.0.13+ds/hw/display/vga.c --- qemu-10.0.12+ds/hw/display/vga.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/display/vga.c 2026-08-26 22:31:25.000000000 +0300 @@ -1629,11 +1629,12 @@ s->last_line_offset = s->params.line_offset; s->last_depth = depth; s->last_byteswap = byteswap; - /* 16 extra pixels are needed for double-width planar modes. */ - s->panning_buf = g_realloc(s->panning_buf, - (disp_width + 16) * sizeof(uint32_t)); full_update = 1; } + + /* 16 extra pixels are needed for double-width planar modes. */ + s->panning_buf = g_realloc(s->panning_buf, + (disp_width + 16) * sizeof(uint32_t)); if (surface_data(surface) != s->vram_ptr + (s->params.start_addr * 4) && !surface_is_allocated(surface)) { /* base address changed (page flip) -> shared display surfaces diff -Nru qemu-10.0.12+ds/hw/display/vhost-user-gpu.c qemu-10.0.13+ds/hw/display/vhost-user-gpu.c --- qemu-10.0.12+ds/hw/display/vhost-user-gpu.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/display/vhost-user-gpu.c 2026-08-26 22:31:25.000000000 +0300 @@ -118,6 +118,31 @@ static void vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked); +static size_t +vhost_user_gpu_min_payload_size(VhostUserGpuRequest request) +{ + switch (request) { + case VHOST_USER_GPU_CURSOR_POS: + case VHOST_USER_GPU_CURSOR_POS_HIDE: + return sizeof(VhostUserGpuCursorPos); + case VHOST_USER_GPU_CURSOR_UPDATE: + return sizeof(VhostUserGpuCursorUpdate); + case VHOST_USER_GPU_GET_EDID: + return sizeof(VhostUserGpuEdidRequest); + case VHOST_USER_GPU_SCANOUT: + return sizeof(VhostUserGpuScanout); + case VHOST_USER_GPU_DMABUF_SCANOUT: + return sizeof(VhostUserGpuDMABUFScanout); + case VHOST_USER_GPU_DMABUF_SCANOUT2: + return sizeof(VhostUserGpuDMABUFScanout2); + case VHOST_USER_GPU_DMABUF_UPDATE: + case VHOST_USER_GPU_UPDATE: + return sizeof(VhostUserGpuUpdate); + default: + return 0; + } +} + static void vhost_user_gpu_handle_cursor(VhostUserGPU *g, VhostUserGpuMsg *msg) { @@ -319,6 +344,14 @@ if (m->scanout_id >= g->parent_obj.conf.max_outputs) { break; } + + if ((uint64_t)m->width * m->height > + (msg->size - sizeof(VhostUserGpuUpdate)) / sizeof(uint32_t)) { + error_report("vhost-user-gpu: update payload too small" + " for %ux%u", m->width, m->height); + break; + } + s = &g->parent_obj.scanout[m->scanout_id]; con = s->con; pixman_image_t *image = @@ -393,6 +426,11 @@ msg->flags = flags; msg->size = size; + if (size < vhost_user_gpu_min_payload_size(request)) { + error_report("vhost-user-gpu: message %d payload too small", request); + goto end; + } + if (request == VHOST_USER_GPU_CURSOR_UPDATE || request == VHOST_USER_GPU_CURSOR_POS || request == VHOST_USER_GPU_CURSOR_POS_HIDE) { diff -Nru qemu-10.0.12+ds/hw/display/virtio-gpu-rutabaga.c qemu-10.0.13+ds/hw/display/virtio-gpu-rutabaga.c --- qemu-10.0.12+ds/hw/display/virtio-gpu-rutabaga.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/display/virtio-gpu-rutabaga.c 2026-08-26 22:31:25.000000000 +0300 @@ -9,6 +9,7 @@ #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-gpu-pixman.h" #include "hw/virtio/virtio-iommu.h" +#include "migration/blocker.h" #include <glib/gmem.h> #include <rutabaga_gfx/rutabaga_gfx_ffi.h> @@ -302,7 +303,7 @@ trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id, ss.r.width, ss.r.height, ss.r.x, ss.r.y); - CHECK(ss.scanout_id < VIRTIO_GPU_MAX_SCANOUTS, cmd); + CHECK(ss.scanout_id < vb->conf.max_outputs, cmd); scanout = &vb->scanout[ss.scanout_id]; if (ss.resource_id == 0) { @@ -314,6 +315,12 @@ res = virtio_gpu_find_resource(g, ss.resource_id); CHECK(res, cmd); + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + res->width, res->height, &ss.r, + &cmd->error)) { + return; + } + if (!res->image) { pixman_format_code_t pformat; pformat = virtio_gpu_get_pixman_format(res->format); @@ -1040,19 +1047,19 @@ return true; } -static int virtio_gpu_rutabaga_get_num_capsets(VirtIOGPU *g) +static bool +virtio_gpu_rutabaga_get_num_capsets(VirtIOGPU *g, uint32_t *num_capsets, Error **errp) { int result; - uint32_t num_capsets; VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); - result = rutabaga_get_num_capsets(vr->rutabaga, &num_capsets); + result = rutabaga_get_num_capsets(vr->rutabaga, num_capsets); if (result) { - error_report("Failed to get capsets"); - return 0; + error_setg_errno(errp, -result, "Failed to get num_capsets"); + return false; } - vr->num_capsets = num_capsets; - return num_capsets; + vr->num_capsets = *num_capsets; + return true; } static void virtio_gpu_rutabaga_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) @@ -1078,7 +1085,8 @@ static void virtio_gpu_rutabaga_realize(DeviceState *qdev, Error **errp) { - int num_capsets; + ERRP_GUARD(); + uint32_t num_capsets; VirtIOGPUBase *bdev = VIRTIO_GPU_BASE(qdev); VirtIOGPU *gpudev = VIRTIO_GPU(qdev); @@ -1087,13 +1095,17 @@ return; #endif - if (!virtio_gpu_rutabaga_init(gpudev, errp)) { + error_setg(&bdev->migration_blocker, "rutabaga is not yet migratable"); + if (migrate_add_blocker(&bdev->migration_blocker, errp) < 0) { return; } - num_capsets = virtio_gpu_rutabaga_get_num_capsets(gpudev); - if (!num_capsets) { - return; + if (!virtio_gpu_rutabaga_init(gpudev, errp)) { + goto fail; + } + + if (!virtio_gpu_rutabaga_get_num_capsets(gpudev, &num_capsets, errp)) { + goto fail; } bdev->conf.flags |= (1 << VIRTIO_GPU_FLAG_RUTABAGA_ENABLED); @@ -1102,6 +1114,12 @@ bdev->virtio_config.num_capsets = num_capsets; virtio_gpu_device_realize(qdev, errp); + if (!*errp) { + return; + } + +fail: + migrate_del_blocker(&bdev->migration_blocker); } static const Property virtio_gpu_rutabaga_properties[] = { diff -Nru qemu-10.0.12+ds/hw/display/virtio-gpu-virgl.c qemu-10.0.13+ds/hw/display/virtio-gpu-virgl.c --- qemu-10.0.12+ds/hw/display/virtio-gpu-virgl.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/display/virtio-gpu-virgl.c 2026-08-26 22:31:25.000000000 +0300 @@ -438,7 +438,7 @@ } g->parent_obj.enable = 1; - if (ss.resource_id && ss.r.width && ss.r.height) { + if (ss.resource_id) { struct virgl_renderer_resource_info info; void *d3d_tex2d = NULL; @@ -459,6 +459,11 @@ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; return; } + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + info.width, info.height, &ss.r, + &cmd->error)) { + return; + } qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con, ss.r.width, ss.r.height); virgl_renderer_force_ctx_0(); @@ -856,16 +861,9 @@ return; } - if (ss.width < 16 || - ss.height < 16 || - ss.r.x + ss.r.width > ss.width || - ss.r.y + ss.r.height > ss.height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" - " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n", - __func__, ss.scanout_id, ss.resource_id, - ss.r.x, ss.r.y, ss.r.width, ss.r.height, - ss.width, ss.height); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + ss.width, ss.height, &ss.r, + &cmd->error)) { return; } diff -Nru qemu-10.0.12+ds/hw/display/virtio-gpu.c qemu-10.0.13+ds/hw/display/virtio-gpu.c --- qemu-10.0.12+ds/hw/display/virtio-gpu.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/display/virtio-gpu.c 2026-08-26 22:31:25.000000000 +0300 @@ -56,18 +56,18 @@ return; } - if (res->blob_size) { - if (res->blob_size < (s->current_cursor->width * - s->current_cursor->height * 4)) { - return; - } - data = res->blob; - } else { + if (res->image) { if (pixman_image_get_width(res->image) != s->current_cursor->width || pixman_image_get_height(res->image) != s->current_cursor->height) { return; } data = pixman_image_get_data(res->image); + } else { + if (res->blob_size < (s->current_cursor->width * + s->current_cursor->height * 4)) { + return; + } + data = res->blob; } pixels = s->current_cursor->width * s->current_cursor->height; @@ -368,6 +368,17 @@ return; } + if (res->iov_cnt > 0 && + iov_size(res->iov, res->iov_cnt) < res->blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: backing storage smaller than blob size\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + virtio_gpu_cleanup_mapping(g, res); + g_free(res); + return; + } + virtio_gpu_init_udmabuf(res); QTAILQ_INSERT_HEAD(&g->reslist, res, next); } @@ -613,6 +624,31 @@ scanout->fb = *fb; } +static uint32_t virtio_gpu_format_bytes_pp(pixman_format_code_t format) +{ + return DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8); +} + +bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + const struct virtio_gpu_rect *r, + uint32_t *error) +{ + if (r->width < 16 || + r->height < 16 || + (uint64_t)r->x + r->width > width || + (uint64_t)r->y + r->height > height) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" + " resource %d, fb %d %d, rect (%d,%d)+%d,%d\n", + __func__, scanout_id, resource_id, width, height, + r->x, r->y, r->width, r->height); + *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return false; + } + + return true; +} + static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_framebuffer *fb, @@ -621,23 +657,28 @@ uint32_t *error) { struct virtio_gpu_scanout *scanout; + uint32_t bytes_pp = virtio_gpu_format_bytes_pp(fb->format); uint8_t *data; scanout = &g->parent_obj.scanout[scanout_id]; - if (r->x > fb->width || - r->y > fb->height || - r->width < 16 || - r->height < 16 || - r->width > fb->width || - r->height > fb->height || - r->x + r->width > fb->width || - r->y + r->height > fb->height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" - " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n", - __func__, scanout_id, res->resource_id, - r->x, r->y, r->width, r->height, - fb->width, fb->height); + if (!virtio_gpu_check_scanout_bounds(scanout_id, res->resource_id, + fb->width, fb->height, r, error)) { + return false; + } + + if (fb->stride < (uint64_t)fb->width * bytes_pp) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: stride %u too small for width %u at %u bpp\n", + __func__, fb->stride, fb->width, bytes_pp); + *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return false; + } + + if (fb->stride > INT_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: stride is %" PRIu32 + ", larger than the supported maximum (%d)\n", + __func__, fb->stride, INT_MAX); *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return false; } @@ -700,6 +741,7 @@ struct virtio_gpu_simple_resource *res; struct virtio_gpu_framebuffer fb = { 0 }; struct virtio_gpu_set_scanout ss; + uint32_t bytes_pp; VIRTIO_GPU_FILL_CMD(ss); virtio_gpu_bswap_32(&ss, sizeof(ss)); @@ -725,11 +767,11 @@ } fb.format = pixman_image_get_format(res->image); - fb.bytes_pp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(fb.format), 8); + bytes_pp = virtio_gpu_format_bytes_pp(fb.format); fb.width = pixman_image_get_width(res->image); fb.height = pixman_image_get_height(res->image); fb.stride = pixman_image_get_stride(res->image); - fb.offset = ss.r.x * fb.bytes_pp + ss.r.y * fb.stride; + fb.offset = ss.r.x * bytes_pp + ss.r.y * fb.stride; virtio_gpu_do_set_scanout(g, ss.scanout_id, &fb, res, &ss.r, &cmd->error); @@ -739,7 +781,8 @@ struct virtio_gpu_set_scanout_blob *ss, uint64_t blob_size) { - uint64_t fbend; + uint64_t fbend, offset; + uint32_t bytes_pp; fb->format = virtio_gpu_get_pixman_format(ss->format); if (!fb->format) { @@ -749,22 +792,39 @@ return false; } - fb->bytes_pp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(fb->format), 8); + bytes_pp = virtio_gpu_format_bytes_pp(fb->format); fb->width = ss->width; fb->height = ss->height; fb->stride = ss->strides[0]; - fb->offset = ss->offsets[0] + ss->r.x * fb->bytes_pp + ss->r.y * fb->stride; - fbend = fb->offset; - fbend += (uint64_t) fb->stride * ss->r.height; + if (fb->stride < (uint64_t)fb->width * bytes_pp) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: stride %u too small for width %u at %u bpp\n", + __func__, fb->stride, fb->width, bytes_pp); + return false; + } + + if (fb->stride > INT_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: stride is %" PRIu32 + ", larger than the supported maximum (%d)\n", + __func__, fb->stride, INT_MAX); + return false; + } - if (fbend > blob_size) { + offset = (uint64_t)ss->offsets[0] + (uint64_t)ss->r.x * bytes_pp + + (uint64_t)ss->r.y * fb->stride; + + fbend = offset + (uint64_t)fb->stride * ss->r.height; + + if (offset > UINT32_MAX || fbend > blob_size) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: fb end out of range\n", + "%s: invalid fb bounds\n", __func__); return false; } + fb->offset = offset; + return true; } @@ -949,6 +1009,19 @@ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; return; } + + if (iov_size(res->iov, res->iov_cnt) < res->blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: backing storage smaller than blob size\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + virtio_gpu_cleanup_mapping(g, res); + return; + } + + if (!res->image) { + virtio_gpu_init_udmabuf(res); + } } static void @@ -1203,8 +1276,7 @@ VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout), VMSTATE_UINT32_TEST(fb.format, struct virtio_gpu_scanout, scanout_vmstate_after_v2), - VMSTATE_UINT32_TEST(fb.bytes_pp, struct virtio_gpu_scanout, - scanout_vmstate_after_v2), + VMSTATE_UNUSED_TEST(scanout_vmstate_after_v2, 4), VMSTATE_UINT32_TEST(fb.width, struct virtio_gpu_scanout, scanout_vmstate_after_v2), VMSTATE_UINT32_TEST(fb.height, struct virtio_gpu_scanout, @@ -1243,7 +1315,7 @@ assert(QTAILQ_EMPTY(&g->cmdq)); QTAILQ_FOREACH(res, &g->reslist, next) { - if (res->blob_size) { + if (!res->image) { continue; } qemu_put_be32(f, res->resource_id); @@ -1384,7 +1456,7 @@ assert(QTAILQ_EMPTY(&g->cmdq)); QTAILQ_FOREACH(res, &g->reslist, next) { - if (!res->blob_size) { + if (res->image) { continue; } assert(!res->image); @@ -1436,6 +1508,14 @@ res->iov[i].iov_len = qemu_get_be32(f); } + if (res->iov_cnt > 0 && + iov_size(res->iov, res->iov_cnt) < res->blob_size) { + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } + if (!virtio_gpu_load_restore_mapping(g, res)) { g_free(res); return -EINVAL; @@ -1615,12 +1695,14 @@ while (!QTAILQ_EMPTY(&g->cmdq)) { cmd = QTAILQ_FIRST(&g->cmdq); QTAILQ_REMOVE(&g->cmdq, cmd, next); + virtqueue_detach_element(cmd->vq, &cmd->elem, 0); g_free(cmd); } while (!QTAILQ_EMPTY(&g->fenceq)) { cmd = QTAILQ_FIRST(&g->fenceq); QTAILQ_REMOVE(&g->fenceq, cmd, next); + virtqueue_detach_element(cmd->vq, &cmd->elem, 0); g->inflight--; g_free(cmd); } diff -Nru qemu-10.0.12+ds/hw/display/vmware_vga.c qemu-10.0.13+ds/hw/display/vmware_vga.c --- qemu-10.0.12+ds/hw/display/vmware_vga.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/display/vmware_vga.c 2026-08-26 22:31:25.000000000 +0300 @@ -737,6 +737,10 @@ vmsvga_fifo_read(s); x = vmsvga_fifo_read(s); y = vmsvga_fifo_read(s); + if (x < 0 || x >= SVGA_MAX_WIDTH || + y < 0 || y >= SVGA_MAX_HEIGHT) { + goto rewind; + } args = x * y; goto badcmd; case SVGA_CMD_RECT_ROP_FILL: @@ -776,7 +780,7 @@ if (len < 0) { goto rewind; } - while (args--) { + while (args-- > 0) { vmsvga_fifo_read(s); } printf("%s: Unknown command 0x%02x in SVGA command FIFO\n", diff -Nru qemu-10.0.12+ds/hw/i386/intel_iommu.c qemu-10.0.13+ds/hw/i386/intel_iommu.c --- qemu-10.0.12+ds/hw/i386/intel_iommu.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/i386/intel_iommu.c 2026-08-26 22:31:25.000000000 +0300 @@ -2999,6 +2999,8 @@ { VTDIOTLBPageInvInfo info; + assert(am <= VTD_MAMV); + info.domain_id = domain_id; info.pasid = pasid; info.addr = addr; @@ -3037,6 +3039,13 @@ case VTD_INV_DESC_PIOTLB_PSI_IN_PASID: am = VTD_INV_DESC_PIOTLB_AM(inv_desc->val[1]); + if (am > VTD_MAMV) { + error_report_once("%s: invalid piotlb inv desc: hi=0x%"PRIx64 + ", lo=0x%"PRIx64" (am=%u > VTD_MAMV=%llu)", + __func__, inv_desc->val[1], inv_desc->val[0], + am, VTD_MAMV); + return false; + } addr = (hwaddr) VTD_INV_DESC_PIOTLB_ADDR(inv_desc->val[1]); vtd_piotlb_page_invalidate(s, domain_id, pasid, addr, am); break; diff -Nru qemu-10.0.12+ds/hw/ide/atapi.c qemu-10.0.13+ds/hw/ide/atapi.c --- qemu-10.0.12+ds/hw/ide/atapi.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/ide/atapi.c 2026-08-26 22:31:25.000000000 +0300 @@ -88,46 +88,14 @@ memset(buf, 0, 288); } -static int -cd_read_sector_sync(IDEState *s) -{ - int ret; - block_acct_start(blk_get_stats(s->blk), &s->acct, - ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ); - - trace_cd_read_sector_sync(s->lba); - - switch (s->cd_sector_size) { - case 2048: - ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS, - ATAPI_SECTOR_SIZE, s->io_buffer, 0); - break; - case 2352: - ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS, - ATAPI_SECTOR_SIZE, s->io_buffer + 16, 0); - if (ret >= 0) { - cd_data_to_raw(s->io_buffer, s->lba); - } - break; - default: - block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ); - return -EIO; - } - - if (ret < 0) { - block_acct_failed(blk_get_stats(s->blk), &s->acct); - } else { - block_acct_done(blk_get_stats(s->blk), &s->acct); - s->lba++; - s->io_buffer_index = 0; - } - - return ret; -} - static void cd_read_sector_cb(void *opaque, int ret) { IDEState *s = opaque; + int et = s->elementary_transfer_size; + int skip = s->io_buffer_index; + int nsec = DIV_ROUND_UP(skip + et, s->cd_sector_size); + uint8_t *buf; + int i; trace_cd_read_sector_cb(s->lba, ret); @@ -140,34 +108,64 @@ block_acct_done(blk_get_stats(s->blk), &s->acct); if (s->cd_sector_size == 2352) { - cd_data_to_raw(s->io_buffer, s->lba); + /* unpack back-to-front so a sector never clobbers an unmoved one */ + for (i = nsec - 1; i >= 0; i--) { + memmove(s->io_buffer + i * 2352 + 16, s->io_buffer + i * 2048, + ATAPI_SECTOR_SIZE); + cd_data_to_raw(s->io_buffer + i * 2352, s->lba + i); + } } - s->lba++; - s->io_buffer_index = 0; s->status &= ~BUSY_STAT; - ide_atapi_cmd_reply_end(s); + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; + s->lcyl = et & 0xff; + s->hcyl = (et >> 8) & 0xff; + ide_bus_set_irq(s->bus); + + /* a boundary sector shared with the next burst is re-read there */ + buf = s->io_buffer + skip; + s->packet_transfer_size -= et; + s->lba += (skip + et) / s->cd_sector_size; + s->io_buffer_index = (skip + et) % s->cd_sector_size; + s->elementary_transfer_size = 0; + + if (ide_transfer_start_norecurse(s, buf, et, ide_atapi_cmd_reply_end)) { + ide_atapi_cmd_reply_end(s); + } } +/* + * Read the whole elementary transfer (one DRQ burst) in a single async + * request. No read is issued mid-burst, so unlike the old synchronous + * rebuffer it cannot deadlock against a concurrent drain. + */ static int cd_read_sector(IDEState *s) { - void *buf; + int et = s->elementary_transfer_size; + int skip = s->io_buffer_index; + int nsec = DIV_ROUND_UP(skip + et, s->cd_sector_size); if (s->cd_sector_size != 2048 && s->cd_sector_size != 2352) { block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ); return -EINVAL; } - buf = (s->cd_sector_size == 2352) ? s->io_buffer + 16 : s->io_buffer; - qemu_iovec_init_buf(&s->qiov, buf, ATAPI_SECTOR_SIZE); + /* a burst is bounded by the byte count limit, so it fits io_buffer */ + assert(nsec * s->cd_sector_size <= s->io_buffer_total_len); + + /* + * Read the payload packed at the front of io_buffer; the 2352 raw case is + * unpacked into place on completion. + */ + qemu_iovec_init_buf(&s->qiov, s->io_buffer, nsec * ATAPI_SECTOR_SIZE); trace_cd_read_sector(s->lba); block_acct_start(blk_get_stats(s->blk), &s->acct, - ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ); + nsec * ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ); - ide_buffered_readv(s, (int64_t)s->lba << 2, &s->qiov, 4, + ide_buffered_readv(s, (int64_t)s->lba << 2, &s->qiov, nsec * 4, cd_read_sector_cb, s); s->status |= BUSY_STAT; @@ -222,59 +220,49 @@ void ide_atapi_cmd_reply_end(IDEState *s) { int byte_count_limit, size, ret; - while (s->packet_transfer_size > 0) { - trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size, - s->elementary_transfer_size, - s->io_buffer_index); - - /* see if a new sector must be read */ - if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) { - if (!s->elementary_transfer_size) { - ret = cd_read_sector(s); - if (ret < 0) { - ide_atapi_io_error(s, ret); - } - return; - } else { - /* rebuffering within an elementary transfer is - * only possible with a sync request because we - * end up with a race condition otherwise */ - ret = cd_read_sector_sync(s); - if (ret < 0) { - ide_atapi_io_error(s, ret); - return; - } + + trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size, + s->elementary_transfer_size, + s->io_buffer_index); + + if (s->lba != -1 && s->packet_transfer_size > 0) { + byte_count_limit = atapi_byte_count_limit(s); + trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); + size = s->packet_transfer_size; + if (size > byte_count_limit) { + /* byte count limit must be even if this case */ + if (byte_count_limit & 1) { + byte_count_limit--; } + size = byte_count_limit; } - if (s->elementary_transfer_size > 0) { - /* there are some data left to transmit in this elementary - transfer */ - size = s->cd_sector_size - s->io_buffer_index; - if (size > s->elementary_transfer_size) - size = s->elementary_transfer_size; - } else { - /* a new transfer is needed */ - s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; - ide_bus_set_irq(s->bus); - byte_count_limit = atapi_byte_count_limit(s); - trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); - size = s->packet_transfer_size; - if (size > byte_count_limit) { - /* byte count limit must be even if this case */ - if (byte_count_limit & 1) - byte_count_limit--; - size = byte_count_limit; - } - s->lcyl = size & 0xff; - s->hcyl = size >> 8; - s->elementary_transfer_size = size; - /* we cannot transmit more than one sector at a time */ - if (s->lba != -1) { - if (size > (s->cd_sector_size - s->io_buffer_index)) - size = (s->cd_sector_size - s->io_buffer_index); + s->elementary_transfer_size = size; + ret = cd_read_sector(s); + if (ret < 0) { + ide_atapi_io_error(s, ret); + } + return; + } + + while (s->packet_transfer_size > 0) { + /* a new transfer is needed */ + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; + ide_bus_set_irq(s->bus); + byte_count_limit = atapi_byte_count_limit(s); + trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); + size = s->packet_transfer_size; + if (size > byte_count_limit) { + /* byte count limit must be even if this case */ + if (byte_count_limit & 1) { + byte_count_limit--; } - trace_ide_atapi_cmd_reply_end_new(s, s->status); + size = byte_count_limit; } + s->lcyl = size & 0xff; + s->hcyl = size >> 8; + s->elementary_transfer_size = size; + trace_ide_atapi_cmd_reply_end_new(s, s->status); + s->packet_transfer_size -= size; s->elementary_transfer_size -= size; s->io_buffer_index += size; @@ -329,7 +317,7 @@ s->lba = lba; s->packet_transfer_size = nb_sectors * sector_size; s->elementary_transfer_size = 0; - s->io_buffer_index = sector_size; + s->io_buffer_index = 0; s->cd_sector_size = sector_size; ide_atapi_cmd_reply_end(s); diff -Nru qemu-10.0.12+ds/hw/ide/core.c qemu-10.0.13+ds/hw/ide/core.c --- qemu-10.0.12+ds/hw/ide/core.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/ide/core.c 2026-08-26 22:31:25.000000000 +0300 @@ -741,10 +741,17 @@ * In the future we'll be able to safely cancel the I/O if the * whole DMA operation will be submitted to disk with a single * aio operation with preadv/pwritev. + * + * Note: s->bus->dma->aiocb might belong to the adjacent IDEState, + * so we have to drain both drives to get it cleared. */ if (s->bus->dma->aiocb) { trace_ide_cancel_dma_sync_remaining(); - blk_drain(s->blk); + for (int i = 0; i < 2; i++) { + if (s->bus->ifs[i].blk) { + blk_drain(s->bus->ifs[i].blk); + } + } assert(s->bus->dma->aiocb == NULL); } } @@ -921,8 +928,12 @@ s->io_buffer_index = 0; s->io_buffer_size = n * 512; prep_size = s->bus->dma->ops->prepare_buf(s->bus->dma, s->io_buffer_size); - /* prepare_buf() must succeed and respect the limit */ - assert(prep_size >= 0 && prep_size <= n * 512); + if (prep_size < 0) { + ide_dma_error(s); + return; + } + /* If prepare_buf() succeeds, it must respect the limit. */ + assert(prep_size <= n * 512); /* * Now prep_size stores the number of bytes in the sglist, and diff -Nru qemu-10.0.12+ds/hw/intc/loongarch_pch_pic.c qemu-10.0.13+ds/hw/intc/loongarch_pch_pic.c --- qemu-10.0.12+ds/hw/intc/loongarch_pch_pic.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/intc/loongarch_pch_pic.c 2026-08-26 22:31:25.000000000 +0300 @@ -7,6 +7,7 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" +#include "qemu/log.h" #include "hw/irq.h" #include "hw/intc/loongarch_pch_pic.h" #include "trace.h" @@ -17,13 +18,21 @@ { uint64_t val; int irq; + uint8_t vector; if (level) { val = mask & s->intirr & ~s->int_mask; if (val) { irq = ctz64(val); + vector = s->htmsi_vector[irq]; + if (vector >= s->irq_num) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: htmsi_vector[%d]=%u out of range\n", + __func__, irq, vector); + return; + } s->intisr |= MAKE_64BIT_MASK(irq, 1); - qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1); + qemu_set_irq(s->parent_irq[vector], 1); } } else { /* @@ -33,8 +42,15 @@ val = mask & s->intisr & ~s->intirr; if (val) { irq = ctz64(val); + vector = s->htmsi_vector[irq]; + if (vector >= s->irq_num) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: htmsi_vector[%d]=%u out of range\n", + __func__, irq, vector); + return; + } s->intisr &= ~MAKE_64BIT_MASK(irq, 1); - qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0); + qemu_set_irq(s->parent_irq[vector], 0); } } } diff -Nru qemu-10.0.12+ds/hw/mem/cxl_type3.c qemu-10.0.13+ds/hw/mem/cxl_type3.c --- qemu-10.0.12+ds/hw/mem/cxl_type3.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/mem/cxl_type3.c 2026-08-26 22:31:25.000000000 +0300 @@ -271,6 +271,9 @@ } ent = req->entry_handle; + if (ent >= cdat->entry_len) { + return false; + } base = cdat->entry[ent].base; len = cdat->entry[ent].length; diff -Nru qemu-10.0.12+ds/hw/net/e1000e_core.c qemu-10.0.13+ds/hw/net/e1000e_core.c --- qemu-10.0.12+ds/hw/net/e1000e_core.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/net/e1000e_core.c 2026-08-26 22:31:25.000000000 +0300 @@ -1949,17 +1949,23 @@ } static void +e1000e_calc_rxconf(E1000ECore *core) +{ + e1000e_parse_rxbufsize(core); + e1000e_calc_rxdesclen(core); + core->rxbuf_min_shift = + ((core->mac[RCTL] / E1000_RCTL_RDMTS_QUAT) & 3) + 1 + + E1000_RING_DESC_LEN_SHIFT; +} + +static void e1000e_set_rx_control(E1000ECore *core, int index, uint32_t val) { core->mac[RCTL] = val; trace_e1000e_rx_set_rctl(core->mac[RCTL]); if (val & E1000_RCTL_EN) { - e1000e_parse_rxbufsize(core); - e1000e_calc_rxdesclen(core); - core->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1 + - E1000_RING_DESC_LEN_SHIFT; - + e1000e_calc_rxconf(core); e1000e_start_recv(core); } } @@ -3556,5 +3562,7 @@ e1000e_intrmgr_resume(core); e1000e_autoneg_resume(core); + e1000e_calc_rxconf(core); + return 0; } diff -Nru qemu-10.0.12+ds/hw/net/igb_core.c qemu-10.0.13+ds/hw/net/igb_core.c --- qemu-10.0.12+ds/hw/net/igb_core.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/net/igb_core.c 2026-08-26 22:31:25.000000000 +0300 @@ -4547,5 +4547,7 @@ igb_intrmgr_resume(core); igb_autoneg_resume(core); + igb_calc_rxdesclen(core); + return 0; } diff -Nru qemu-10.0.12+ds/hw/net/rtl8139.c qemu-10.0.13+ds/hw/net/rtl8139.c --- qemu-10.0.12+ds/hw/net/rtl8139.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/net/rtl8139.c 2026-08-26 22:31:25.000000000 +0300 @@ -777,7 +777,6 @@ s->RxBufAddr += size; } -#define MIN_BUF_SIZE 60 static inline dma_addr_t rtl8139_addr64(uint32_t low, uint32_t high) { return low | ((uint64_t)high << 32); @@ -1005,10 +1004,6 @@ lduw_be_p(&buf[ETH_ALEN * 2]) == ETH_P_VLAN) { dot1q_buf = &buf[ETH_ALEN * 2]; size -= VLAN_HLEN; - /* if too small buffer, use the tailroom added duing expansion */ - if (size < MIN_BUF_SIZE) { - size = MIN_BUF_SIZE; - } rxdw1 &= ~CP_RX_VLAN_TAG_MASK; /* BE + ~le_to_cpu()~ + cpu_to_le() = BE */ @@ -1777,6 +1772,7 @@ buf2 = g_malloc(buf2_size); iov_to_buf(iov, 3, 0, buf2, buf2_size); buf = buf2; + size = buf2_size; } DPRINTF("+++ transmit loopback mode\n"); diff -Nru qemu-10.0.12+ds/hw/net/virtio-net.c qemu-10.0.13+ds/hw/net/virtio-net.c --- qemu-10.0.12+ds/hw/net/virtio-net.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/net/virtio-net.c 2026-08-26 22:31:25.000000000 +0300 @@ -1750,10 +1750,21 @@ if (n->promisc) return 1; + if (size < n->host_hdr_len + 14) { + /* Truncated ethernet packet */ + return 0; + } + ptr += n->host_hdr_len; if (!memcmp(&ptr[12], vlan, sizeof(vlan))) { - int vid = lduw_be_p(ptr + 14) & 0xfff; + int vid; + + /* Truncated vlan packet */ + if (size < n->host_hdr_len + 16) { + return 0; + } + vid = lduw_be_p(ptr + 14) & 0xfff; if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f)))) return 0; } @@ -2677,6 +2688,13 @@ { VirtIONet *n = qemu_get_nic_opaque(nc); if ((n->rsc4_enabled || n->rsc6_enabled)) { + /* this never happens with existing backends, but just in case. */ + if (n->host_hdr_len != n->guest_hdr_len) { + warn_report_once("virtio-net: host_hdr_len %zu != guest_hdr_len %zu, " + "skipping RSC", + n->host_hdr_len, n->guest_hdr_len); + return virtio_net_do_receive(nc, buf, size); + } return virtio_net_rsc_receive(nc, buf, size); } else { return virtio_net_do_receive(nc, buf, size); @@ -2994,8 +3012,9 @@ n->vqs[index].tx_vq = virtio_add_queue(vdev, n->net_conf.tx_queue_size, virtio_net_handle_tx_bh); - n->vqs[index].tx_bh = qemu_bh_new_guarded(virtio_net_tx_bh, &n->vqs[index], - &DEVICE(vdev)->mem_reentrancy_guard); + n->vqs[index].tx_bh = virtio_bh_new_guarded(DEVICE(vdev), + virtio_net_tx_bh, + &n->vqs[index]); } n->vqs[index].tx_waiting = 0; diff -Nru qemu-10.0.12+ds/hw/net/vmxnet3.c qemu-10.0.13+ds/hw/net/vmxnet3.c --- qemu-10.0.12+ds/hw/net/vmxnet3.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/net/vmxnet3.c 2026-08-26 22:31:25.000000000 +0300 @@ -1352,32 +1352,46 @@ || intx == pci_get_byte(s->parent_obj.config + PCI_INTERRUPT_PIN) - 1; } -static void vmxnet3_validate_interrupt_idx(bool is_msix, int idx) +static bool vmxnet3_validate_irq_idx(const char *type, bool is_msix, int idx) { int max_ints = is_msix ? VMXNET3_MAX_INTRS : VMXNET3_MAX_NMSIX_INTRS; + if (idx >= max_ints) { - hw_error("Bad interrupt index: %d\n", idx); + qemu_log_mask(LOG_GUEST_ERROR, + "vmxnet3: Bad %s queue interrupt index: %d\n", + type, idx); + return false; } + + return true; } -static void vmxnet3_validate_interrupts(VMXNET3State *s) +static bool vmxnet3_validate_interrupts(VMXNET3State *s) { int i; VMW_CFPRN("Verifying event interrupt index (%d)", s->event_int_idx); - vmxnet3_validate_interrupt_idx(s->msix_used, s->event_int_idx); + if (!vmxnet3_validate_irq_idx("event", s->msix_used, s->event_int_idx)) { + return false; + } for (i = 0; i < s->txq_num; i++) { int idx = s->txq_descr[i].intr_idx; VMW_CFPRN("Verifying TX queue %d interrupt index (%d)", i, idx); - vmxnet3_validate_interrupt_idx(s->msix_used, idx); + if (!vmxnet3_validate_irq_idx("TX", s->msix_used, idx)) { + return false; + } } for (i = 0; i < s->rxq_num; i++) { int idx = s->rxq_descr[i].intr_idx; VMW_CFPRN("Verifying RX queue %d interrupt index (%d)", i, idx); - vmxnet3_validate_interrupt_idx(s->msix_used, idx); + if (!vmxnet3_validate_irq_idx("RX", s->msix_used, idx)) { + return false; + } } + + return true; } static bool vmxnet3_validate_queues(VMXNET3State *s) @@ -1570,7 +1584,9 @@ sizeof(s->rxq_descr[i].rxq_stats)); } - vmxnet3_validate_interrupts(s); + if (!vmxnet3_validate_interrupts(s)) { + return; + } /* Make sure everything is in place before device activation */ smp_wmb(); @@ -2407,7 +2423,9 @@ if (!vmxnet3_validate_queues(s)) { return -1; } - vmxnet3_validate_interrupts(s); + if (!vmxnet3_validate_interrupts(s)) { + return -1; + } return 0; } diff -Nru qemu-10.0.12+ds/hw/net/xilinx_axienet.c qemu-10.0.13+ds/hw/net/xilinx_axienet.c --- qemu-10.0.12+ds/hw/net/xilinx_axienet.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/net/xilinx_axienet.c 2026-08-26 22:31:25.000000000 +0300 @@ -918,20 +918,27 @@ if (s->hdr[0] & 1) { unsigned int start_off = s->hdr[1] >> 16; unsigned int write_off = s->hdr[1] & 0xffff; - uint32_t tmp_csum; - uint16_t csum; - tmp_csum = net_checksum_add(s->txpos - start_off, - buf + start_off); - /* Accumulate the seed. */ - tmp_csum += s->hdr[2] & 0xffff; + if (start_off > s->txpos || write_off + 2 > s->txpos) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offsets outside packet, skipping checksum\n", + TYPE_XILINX_AXI_ENET); + } else { + uint32_t tmp_csum; + uint16_t csum; - /* Fold the 32bit partial checksum. */ - csum = net_checksum_finish(tmp_csum); + tmp_csum = net_checksum_add(s->txpos - start_off, + buf + start_off); + /* Accumulate the seed. */ + tmp_csum += s->hdr[2] & 0xffff; - /* Writeback. */ - buf[write_off] = csum >> 8; - buf[write_off + 1] = csum & 0xff; + /* Fold the 32bit partial checksum. */ + csum = net_checksum_finish(tmp_csum); + + /* Writeback. */ + buf[write_off] = csum >> 8; + buf[write_off + 1] = csum & 0xff; + } } qemu_send_packet(qemu_get_queue(s->nic), buf, s->txpos); diff -Nru qemu-10.0.12+ds/hw/nvme/Kconfig qemu-10.0.13+ds/hw/nvme/Kconfig --- qemu-10.0.12+ds/hw/nvme/Kconfig 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/nvme/Kconfig 2026-08-26 22:31:25.000000000 +0300 @@ -2,3 +2,4 @@ bool default y if PCI_DEVICES || PCIE_DEVICES depends on PCI + select SPDM_SOCKET diff -Nru qemu-10.0.12+ds/hw/nvme/ctrl.c qemu-10.0.13+ds/hw/nvme/ctrl.c --- qemu-10.0.12+ds/hw/nvme/ctrl.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/nvme/ctrl.c 2026-08-26 22:31:25.000000000 +0300 @@ -194,6 +194,7 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/log.h" @@ -2790,6 +2791,7 @@ qemu_iovec_destroy(&iocb->iov); g_free(iocb->bounce); + g_free(iocb->ranges); if (iocb->ret < 0) { block_acct_failed(stats, &iocb->acct.read); @@ -3190,7 +3192,7 @@ uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); uint64_t slba; uint32_t nlb; - size_t len; + size_t len, blen; uint16_t status; uint32_t dnsid = le32_to_cpu(req->cmd.nsid); uint32_t snsid = dnsid; @@ -3311,10 +3313,13 @@ } g_free(iocb->bounce); - iocb->bounce = g_malloc_n(le16_to_cpu(sns->id_ns.mssrl), - sns->lbasz + sns->lbaf.ms); + assert(g_size_checked_mul(&blen, le16_to_cpu(sns->id_ns.mssrl), + sns->lbasz + MAX(sns->lbaf.ms, dns->lbaf.ms))); + + iocb->bounce = g_malloc(blen); qemu_iovec_reset(&iocb->iov); + assert(len <= blen); qemu_iovec_add(&iocb->iov, iocb->bounce, len); block_acct_start(blk_get_stats(sns->blkconf.blk), &iocb->acct.read, 0, @@ -4801,6 +4806,26 @@ return 0; } +/* + * A pending Async Event Request has no aiocb (nvme_aer() parks it without + * issuing any block I/O), so there is nothing to cancel; just drop it. + */ +static void nvme_sq_cancel_inflight(NvmeSQueue *sq, uint16_t status) +{ + NvmeRequest *r; + + while (!QTAILQ_EMPTY(&sq->out_req_list)) { + r = QTAILQ_FIRST(&sq->out_req_list); + r->status = status; + + if (r->aiocb) { + blk_aio_cancel(r->aiocb); + } else { + QTAILQ_REMOVE(&sq->out_req_list, r, entry); + } + } +} + static void nvme_free_sq(NvmeSQueue *sq, NvmeCtrl *n) { uint16_t offset = sq->sqid << 3; @@ -4835,14 +4860,7 @@ trace_pci_nvme_del_sq(qid); sq = n->sq[qid]; - while (!QTAILQ_EMPTY(&sq->out_req_list)) { - r = QTAILQ_FIRST(&sq->out_req_list); - assert(r->aiocb); - r->status = NVME_CMD_ABORT_SQ_DEL; - blk_aio_cancel(r->aiocb); - } - - assert(QTAILQ_EMPTY(&sq->out_req_list)); + nvme_sq_cancel_inflight(sq, NVME_CMD_ABORT_SQ_DEL); if (!nvme_check_cqid(n, sq->cqid)) { cq = n->cq[sq->cqid]; @@ -6535,7 +6553,8 @@ if (!shift && event_type) { continue; } - event_mask |= (1 << nvme_fdp_evf_shifts[events[i]]); + event_mask = + deposit64(event_mask, nvme_fdp_evf_shifts[events[i]], 1, 1); } if (enable) { @@ -7639,6 +7658,18 @@ nvme_ns_drain(ns); } + /* + * Cancel and wait out every inflight command on every queue first. A + * reset is not required to be preceded by the guest's graceful + * Delete I/O SQ/CQ sequence, so sq/cq must not be freed below while a + * blk_aio_* completion for them could still be in flight. + */ + for (i = 0; i < n->params.max_ioqpairs + 1; i++) { + if (n->sq[i] != NULL) { + nvme_sq_cancel_inflight(n->sq[i], NVME_CMD_ABORT_SQ_DEL); + } + } + for (i = 0; i < n->params.max_ioqpairs + 1; i++) { if (n->sq[i] != NULL) { nvme_free_sq(n->sq[i], n); @@ -9078,9 +9109,9 @@ msix_uninit_exclusive_bar(pci_dev); } else { msix_uninit(pci_dev, &n->bar0, &n->bar0); + memory_region_del_subregion(&n->bar0, &n->iomem); } - memory_region_del_subregion(&n->bar0, &n->iomem); } static const Property nvme_props[] = { diff -Nru qemu-10.0.12+ds/hw/pci-bridge/cxl_upstream.c qemu-10.0.13+ds/hw/pci-bridge/cxl_upstream.c --- qemu-10.0.12+ds/hw/pci-bridge/cxl_upstream.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/pci-bridge/cxl_upstream.c 2026-08-26 22:31:25.000000000 +0300 @@ -156,6 +156,9 @@ } ent = req->entry_handle; + if (ent >= cdat->entry_len) { + return false; + } base = cdat->entry[ent].base; len = cdat->entry[ent].length; diff -Nru qemu-10.0.12+ds/hw/pci-host/q35.c qemu-10.0.13+ds/hw/pci-host/q35.c --- qemu-10.0.12+ds/hw/pci-host/q35.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/pci-host/q35.c 2026-08-26 22:31:25.000000000 +0300 @@ -475,22 +475,20 @@ mch_update_pciexbar(mch); } - if (!mch->has_smm_ranges) { - return; - } - - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_SMRAM, - MCH_HOST_BRIDGE_SMRAM_SIZE)) { - mch_update_smram(mch); - } + if (mch->has_smm_ranges) { + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_SMRAM, + MCH_HOST_BRIDGE_SMRAM_SIZE)) { + mch_update_smram(mch); + } - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_EXT_TSEG_MBYTES, - MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_SIZE)) { - mch_update_ext_tseg_mbytes(mch); - } + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_EXT_TSEG_MBYTES, + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_SIZE)) { + mch_update_ext_tseg_mbytes(mch); + } - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_F_SMBASE, 1)) { - mch_update_smbase_smram(mch); + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_F_SMBASE, 1)) { + mch_update_smbase_smram(mch); + } } } @@ -562,42 +560,9 @@ mch_update(mch); } -static void mch_realize(PCIDevice *d, Error **errp) +static void mch_init_smram_regions(MCHPCIState *mch) { - int i; - MCHPCIState *mch = MCH_PCI_DEVICE(d); - - if (mch->ext_tseg_mbytes > MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_MAX) { - error_setg(errp, "invalid extended-tseg-mbytes value: %" PRIu16, - mch->ext_tseg_mbytes); - return; - } - - /* setup pci memory mapping */ - pc_pci_as_mapping_init(mch->system_memory, mch->pci_address_space); - - /* PAM */ - init_pam(&mch->pam_regions[0], OBJECT(mch), mch->ram_memory, - mch->system_memory, mch->pci_address_space, - PAM_BIOS_BASE, PAM_BIOS_SIZE); - for (i = 0; i < ARRAY_SIZE(mch->pam_regions) - 1; ++i) { - init_pam(&mch->pam_regions[i + 1], OBJECT(mch), mch->ram_memory, - mch->system_memory, mch->pci_address_space, - PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); - } - - if (!mch->has_smm_ranges) { - return; - } - - /* if *disabled* show SMRAM to all CPUs */ - memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", - mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, - MCH_HOST_BRIDGE_SMRAM_C_SIZE); - memory_region_add_subregion_overlap(mch->system_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, - &mch->smram_region, 1); - memory_region_set_enabled(&mch->smram_region, true); - + /* Initialize all the SMRAM specific MemoryRegions */ memory_region_init_alias(&mch->open_high_smram, OBJECT(mch), "smram-open-high", mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, MCH_HOST_BRIDGE_SMRAM_C_SIZE); @@ -653,9 +618,52 @@ memory_region_set_enabled(&mch->smbase_window, false); memory_region_add_subregion(&mch->smram, MCH_HOST_BRIDGE_SMBASE_ADDR, &mch->smbase_window); +} + +static void mch_realize(PCIDevice *d, Error **errp) +{ + int i; + MCHPCIState *mch = MCH_PCI_DEVICE(d); + + if (mch->ext_tseg_mbytes > MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_MAX) { + error_setg(errp, "invalid extended-tseg-mbytes value: %" PRIu16, + mch->ext_tseg_mbytes); + return; + } + + /* setup pci memory mapping */ + pc_pci_as_mapping_init(mch->system_memory, mch->pci_address_space); + + /* PAM */ + init_pam(&mch->pam_regions[0], OBJECT(mch), mch->ram_memory, + mch->system_memory, mch->pci_address_space, + PAM_BIOS_BASE, PAM_BIOS_SIZE); + for (i = 0; i < ARRAY_SIZE(mch->pam_regions) - 1; ++i) { + init_pam(&mch->pam_regions[i + 1], OBJECT(mch), mch->ram_memory, + mch->system_memory, mch->pci_address_space, + PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); + } + + /* + * This memory region looks like it's SMM specific, but it is not. + * It's an alias that makes the pci_address_space appear in system + * memory at the SMRAM_C_BASE address. The alias is enabled when the + * CPU should not see SMRAM, and *disabled* when the low SMRAM should be + * visible. So for non-SMM configs we need to create the alias, and + * leave it permanently enabled. + */ + memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", + mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); + memory_region_add_subregion_overlap(mch->system_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + &mch->smram_region, 1); + memory_region_set_enabled(&mch->smram_region, true); - object_property_add_const_link(qdev_get_machine(), "smram", - OBJECT(&mch->smram)); + if (mch->has_smm_ranges) { + mch_init_smram_regions(mch); + object_property_add_const_link(qdev_get_machine(), "smram", + OBJECT(&mch->smram)); + } } static const Property mch_props[] = { diff -Nru qemu-10.0.12+ds/hw/riscv/riscv-iommu.c qemu-10.0.13+ds/hw/riscv/riscv-iommu.c --- qemu-10.0.12+ds/hw/riscv/riscv-iommu.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/riscv/riscv-iommu.c 2026-08-26 22:31:25.000000000 +0300 @@ -278,7 +278,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, IOMMUTLBEntry *iotlb) { - IOMMUAccessFlags pte_perm; + IOMMUAccessFlags trans_perm = IOMMU_NONE; dma_addr_t addr, base; uint64_t satp, gatp, pte; bool en_s, en_g; @@ -295,6 +295,14 @@ } pass; MemTxResult ret; bool pv = !!ctx->process_id; + /* + * Keep the request permission separate from iotlb->perm. G-stage + * walks translate S-stage PTE addresses before the real leaf is + * reached, but permission checks and fault types must still use the + * original request. A successful walk leaves iotlb->perm with the + * effective leaf permission for the translation cache. + */ + const IOMMUAccessFlags req_perm = iotlb->perm; satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD); gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); @@ -313,7 +321,7 @@ * means we can't do an early MSI check unless we have * strictly !en_s. */ - if (!en_s && (iotlb->perm & IOMMU_WO) && + if (!en_s && (req_perm & IOMMU_WO) && riscv_iommu_msi_check(s, ctx, iotlb->iova)) { iotlb->target_as = &s->trap_as; iotlb->translated_addr = iotlb->iova; @@ -431,13 +439,13 @@ masked_msbs = (addr >> (va_len - 1)) & mask; if (masked_msbs != 0 && masked_msbs != mask) { - return (iotlb->perm & IOMMU_WO) ? + return (req_perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S : RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S; } } else { if ((addr & va_mask) != addr) { - return (iotlb->perm & IOMMU_WO) ? + return (req_perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS; } @@ -462,8 +470,8 @@ MEMTXATTRS_UNSPECIFIED); } if (ret != MEMTX_OK) { - return (iotlb->perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT - : RISCV_IOMMU_FQ_CAUSE_RD_FAULT; + return (req_perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT + : RISCV_IOMMU_FQ_CAUSE_RD_FAULT; } sc[pass].step++; @@ -473,13 +481,6 @@ break; /* Invalid PTE */ } else if (pte & PTE_RESERVED) { break; /* Reserved PTE bits set */ - } else if (!(pte & PTE_U) && !pv) { - /* - * All accesses are assumed to be User mode unless - * process_id is valid (pv). In case we have a - * non-user mode PTE and !pv we need to fault. - */ - break; } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { base = PPN_PHYS(ppn); /* Inner PTE, continue walking */ } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) { @@ -488,13 +489,20 @@ break; /* Reserved leaf PTE flags: PTE_W + PTE_X */ } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) { break; /* Misaligned PPN */ - } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) { + } else if (!(pte & PTE_U) && !pv) { + /* + * All accesses are assumed to be User mode unless + * process_id is valid (pv). In case we have a + * non-user mode leaf PTE and !pv we need to fault. + */ + break; + } else if ((req_perm & IOMMU_RO) && !(pte & PTE_R)) { break; /* Read access check failed */ - } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) { + } else if ((req_perm & IOMMU_WO) && !(pte & PTE_W)) { break; /* Write access check failed */ } else if (!ade && !(pte & PTE_A)) { break; /* Access bit not set */ - } else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) { + } else if ((req_perm & IOMMU_WO) && !ade && !(pte & PTE_D)) { break; /* Dirty bit not set */ } else if (pass == G_STAGE && !(pte & PTE_U)) { /* @@ -529,21 +537,20 @@ addr = iotlb->iova; continue; } + + /* Cache the effective permission, not this request's subset. */ + IOMMUAccessFlags leaf_perm = (pte & PTE_W) ? + ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO) : + IOMMU_RO; + + trans_perm = trans_perm == IOMMU_NONE ? + leaf_perm : trans_perm & leaf_perm; + /* Translation phase completed (GPA or SPA) */ iotlb->translated_addr = base; - /* - * Do a bit_and between the PTE bits and the original - * request flags to determine the exact permission we - * need, i.e. if the original request is RO and the - * PTE has RW flags the actual perm is RO. - */ - pte_perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO) - : IOMMU_RO; - iotlb->perm &= pte_perm; - /* Check MSI GPA address match */ - if (pass == S_STAGE && (iotlb->perm & IOMMU_WO) && + if (pass == S_STAGE && (req_perm & IOMMU_WO) && riscv_iommu_msi_check(s, ctx, base)) { /* Trap MSI writes and return GPA address. */ iotlb->target_as = &s->trap_as; @@ -560,6 +567,7 @@ continue; } + iotlb->perm = trans_perm; return 0; } @@ -584,7 +592,7 @@ */ iotlb->translated_addr = addr; - return (iotlb->perm & IOMMU_WO) ? + return (req_perm & IOMMU_WO) ? (pass ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S) : (pass ? RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS : diff -Nru qemu-10.0.12+ds/hw/s390x/ipl.h qemu-10.0.13+ds/hw/s390x/ipl.h --- qemu-10.0.12+ds/hw/s390x/ipl.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/s390x/ipl.h 2026-08-26 22:31:25.000000000 +0300 @@ -124,6 +124,12 @@ return false; } + if (offsetof(IplParameterBlock, pv.components) + + ipib_pv->num_comp * sizeof(IPLBlockPVComp) > + be32_to_cpu(iplb->len)) { + return false; + } + for (i = 0; i < ipib_pv->num_comp; i++) { /* Addr must be 4k aligned */ if (ipib_pv->components[i].addr & ~TARGET_PAGE_MASK) { diff -Nru qemu-10.0.12+ds/hw/scsi/scsi-bus.c qemu-10.0.13+ds/hw/scsi/scsi-bus.c --- qemu-10.0.12+ds/hw/scsi/scsi-bus.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/scsi/scsi-bus.c 2026-08-26 22:31:25.000000000 +0300 @@ -1515,6 +1515,13 @@ } } +void scsi_req_unref_detach_hba(SCSIRequest *req) +{ + /* Unref when the HBA frees hba_private separately (e.g. virtio_scsi_free_req) */ + req->hba_private = NULL; + scsi_req_unref(req); +} + /* Tell the device that we finished processing this chunk of I/O. It will start the next chunk or complete the command. */ void scsi_req_continue(SCSIRequest *req) diff -Nru qemu-10.0.12+ds/hw/scsi/scsi-disk.c qemu-10.0.13+ds/hw/scsi/scsi-disk.c --- qemu-10.0.12+ds/hw/scsi/scsi-disk.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/scsi/scsi-disk.c 2026-08-26 22:31:25.000000000 +0300 @@ -1343,7 +1343,7 @@ return -1; } - assert(length < 256); + assert(length + 2 <= SCSI_MAX_MODE_LEN); (*p_outbuf)[0] = page; (*p_outbuf)[1] = length; *p_outbuf += length + 2; @@ -1546,7 +1546,7 @@ static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, uint8_t *inbuf, int inlen) { - uint8_t mode_current[SCSI_MAX_MODE_LEN]; + uint8_t mode_current[SCSI_MAX_MODE_LEN] = { 0 }; uint8_t mode_changeable[SCSI_MAX_MODE_LEN]; uint8_t *p; int len, expected_len, changeable_len, i; @@ -1565,21 +1565,21 @@ } p = mode_current; - memset(mode_current, 0, inlen + 2); len = mode_sense_page(s, page, &p, 0); - if (len < 0 || len != expected_len) { + /* The guest may send a truncated page, but not a longer one. */ + if (len < 0 || expected_len > len) { return -1; } p = mode_changeable; - memset(mode_changeable, 0, inlen + 2); + memset(mode_changeable, 0, len); changeable_len = mode_sense_page(s, page, &p, 1); assert(changeable_len == len); /* Check that unchangeable bits are the same as what MODE SENSE * would return. */ - for (i = 2; i < len; i++) { + for (i = 2; i < expected_len; i++) { if (((mode_current[i] ^ inbuf[i - 2]) & ~mode_changeable[i]) != 0) { return -1; } @@ -1587,11 +1587,15 @@ return 0; } -static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, uint8_t *p) +/* Note p may be truncated, so check any bytes you access against len. */ +static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, + uint8_t *p, int len) { switch (page) { case MODE_PAGE_CACHING: - blk_set_enable_write_cache(s->qdev.conf.blk, (p[0] & 4) != 0); + if (len > 0) { + blk_set_enable_write_cache(s->qdev.conf.blk, (p[0] & 4) != 0); + } break; default: @@ -1634,6 +1638,7 @@ goto invalid_param_len; } trace_scsi_disk_mode_select_page_truncated(page, page_len, len); + page_len = len; } if (!change) { @@ -1641,7 +1646,7 @@ goto invalid_param; } } else { - scsi_disk_apply_mode_select(s, page, p); + scsi_disk_apply_mode_select(s, page, p, page_len); } p += page_len; diff -Nru qemu-10.0.12+ds/hw/scsi/virtio-scsi.c qemu-10.0.13+ds/hw/scsi/virtio-scsi.c --- qemu-10.0.12+ds/hw/scsi/virtio-scsi.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/scsi/virtio-scsi.c 2026-08-26 22:31:25.000000000 +0300 @@ -935,7 +935,9 @@ req = QTAILQ_FIRST(&reqs); QTAILQ_REMOVE(&reqs, req, next); defer_call_end(); + /* Drop both the ref from _prepare and the initial ref */ scsi_req_unref(req->sreq); + scsi_req_unref_detach_hba(req->sreq); virtqueue_detach_element(req->vq, &req->elem, 0); virtio_scsi_free_req(req); } diff -Nru qemu-10.0.12+ds/hw/uefi/var-service-auth.c qemu-10.0.13+ds/hw/uefi/var-service-auth.c --- qemu-10.0.12+ds/hw/uefi/var-service-auth.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/uefi/var-service-auth.c 2026-08-26 22:31:25.000000000 +0300 @@ -298,6 +298,13 @@ return EFI_WRITE_PROTECTED; } + /* reject SetupMode updates */ + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && + uefi_str_equal(var->name, var->name_size, + name_setup_mode, sizeof(name_setup_mode))) { + return EFI_WRITE_PROTECTED; + } + return EFI_SUCCESS; } diff -Nru qemu-10.0.12+ds/hw/uefi/var-service-core.c qemu-10.0.13+ds/hw/uefi/var-service-core.c --- qemu-10.0.12+ds/hw/uefi/var-service-core.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/uefi/var-service-core.c 2026-08-26 22:31:25.000000000 +0300 @@ -5,6 +5,7 @@ */ #include "qemu/osdep.h" #include "qemu/crc32c.h" +#include "qemu/error-report.h" #include "system/dma.h" #include "migration/vmstate.h" @@ -28,9 +29,20 @@ { uefi_vars_state *uv = opaque; + if (uv->buf_size > MAX_BUFFER_SIZE) { + error_report("invalid buffer size"); + return -1; + } + uv->buffer = g_malloc(uv->buf_size); + uefi_vars_update_storage(uv); + if (uv->used_storage > uv->max_storage) { + error_report("out of variable memory (%" PRId64 " > %" PRId64 ")", + uv->used_storage, uv->max_storage); + return -1; + } + uefi_vars_json_save(uv); - uv->buffer = g_malloc(uv->buf_size); return 0; } diff -Nru qemu-10.0.12+ds/hw/uefi/var-service-json.c qemu-10.0.13+ds/hw/uefi/var-service-json.c --- qemu-10.0.12+ds/hw/uefi/var-service-json.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/uefi/var-service-json.c 2026-08-26 22:31:25.000000000 +0300 @@ -18,6 +18,7 @@ #include "qobject/qobject.h" #include "qobject/qjson.h" +#include "qapi/error.h" #include "qapi/dealloc-visitor.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" @@ -249,6 +250,10 @@ if (!(*errp)) { uefi_vars_from_qapi(uv, vs); uefi_vars_update_storage(uv); + if (uv->used_storage > uv->max_storage) { + error_setg(errp, "out of variable memory (%" PRId64 " > %" PRId64 ")", + uv->used_storage, uv->max_storage); + } } qapi_free_UefiVarStore(vs); diff -Nru qemu-10.0.12+ds/hw/uefi/var-service-policy.c qemu-10.0.13+ds/hw/uefi/var-service-policy.c --- qemu-10.0.12+ds/hw/uefi/var-service-policy.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/uefi/var-service-policy.c 2026-08-26 22:31:25.000000000 +0300 @@ -7,6 +7,7 @@ * https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "system/dma.h" #include "migration/vmstate.h" @@ -16,14 +17,18 @@ #include "trace/trace-hw_uefi.h" -static void calc_policy(uefi_var_policy *pol); +static int check_calc_policy(uefi_var_policy *pol); static int uefi_var_policy_post_load(void *opaque, int version_id) { uefi_var_policy *pol = opaque; + int rc; - calc_policy(pol); - return 0; + rc = check_calc_policy(pol); + if (rc != 0) { + error_report("invalid uefi variable policy"); + } + return rc; } const VMStateDescription vmstate_uefi_var_policy = { @@ -37,37 +42,6 @@ }, }; -static void print_policy_entry(variable_policy_entry *pe) -{ - uint16_t *name = (void *)pe + pe->offset_to_name; - - fprintf(stderr, "%s:\n", __func__); - - fprintf(stderr, " name ´"); - while (*name) { - fprintf(stderr, "%c", *name); - name++; - } - fprintf(stderr, "', version=%d.%d, size=%d\n", - pe->version >> 16, pe->version & 0xffff, pe->size); - - if (pe->min_size) { - fprintf(stderr, " size min=%d\n", pe->min_size); - } - if (pe->max_size != UINT32_MAX) { - fprintf(stderr, " size max=%u\n", pe->max_size); - } - if (pe->attributes_must_have) { - fprintf(stderr, " attr must=0x%x\n", pe->attributes_must_have); - } - if (pe->attributes_cant_have) { - fprintf(stderr, " attr cant=0x%x\n", pe->attributes_cant_have); - } - if (pe->lock_policy_type) { - fprintf(stderr, " lock policy type %d\n", pe->lock_policy_type); - } -} - static gboolean wildcard_str_equal(uefi_var_policy *pol, uefi_variable *var) { @@ -111,32 +85,45 @@ return NULL; } -static void calc_policy(uefi_var_policy *pol) +static int check_calc_policy(uefi_var_policy *pol) { variable_policy_entry *pe = pol->entry; unsigned int i; + if (pol->entry_size != pe->size || + pe->offset_to_name >= pe->size) { + return -1; + } + pol->name = (void *)pol->entry + pe->offset_to_name; pol->name_size = pe->size - pe->offset_to_name; + if (!uefi_str_is_valid(pol->name, pol->name_size, false)) { + return -1; + } + for (i = 0; i < pol->name_size / 2; i++) { if (pol->name[i] == '#') { pol->hashmarks++; } } + + return 0; } uefi_var_policy *uefi_vars_add_policy(uefi_vars_state *uv, variable_policy_entry *pe) { uefi_var_policy *pol, *p; + int rc; pol = g_new0(uefi_var_policy, 1); pol->entry = g_malloc(pe->size); memcpy(pol->entry, pe, pe->size); pol->entry_size = pe->size; - calc_policy(pol); + rc = check_calc_policy(pol); + g_assert(rc == 0); /* keep list sorted by priority, add to tail of priority group */ QTAILQ_FOREACH(p, &uv->var_policies, next) { @@ -173,7 +160,6 @@ pe = pol->entry; uefi_trace_variable(__func__, var->guid, var->name, var->name_size); - print_policy_entry(pe); if ((var->attributes & pe->attributes_must_have) != pe->attributes_must_have) { trace_uefi_vars_policy_deny("must-have-attr"); @@ -276,6 +262,9 @@ uefi_var_policy *pol; uint64_t length; + if (mhdr->length < sizeof(*mchk) + sizeof(*pe)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } if (uadd64_overflow(sizeof(*mchk), pe->size, &length)) { return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); } @@ -312,7 +301,12 @@ return uefi_vars_mm_policy_error(mhdr, mchk, EFI_ALREADY_STARTED); } + if (uv->used_storage + pe->size > uv->max_storage) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_OUT_OF_RESOURCES); + } + uefi_vars_add_policy(uv, pe); + uv->used_storage += pe->size; mchk->result = EFI_SUCCESS; return sizeof(*mchk); diff -Nru qemu-10.0.12+ds/hw/uefi/var-service-siglist.c qemu-10.0.13+ds/hw/uefi/var-service-siglist.c --- qemu-10.0.12+ds/hw/uefi/var-service-siglist.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/uefi/var-service-siglist.c 2026-08-26 22:31:25.000000000 +0300 @@ -94,6 +94,9 @@ break; } efilist = data; + if (efilist->siglist_size < sizeof(*efilist)) { + break; + } if (size < efilist->siglist_size) { break; } diff -Nru qemu-10.0.12+ds/hw/uefi/var-service-vars.c qemu-10.0.13+ds/hw/uefi/var-service-vars.c --- qemu-10.0.12+ds/hw/uefi/var-service-vars.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/uefi/var-service-vars.c 2026-08-26 22:31:25.000000000 +0300 @@ -37,8 +37,23 @@ }, }; +static int uefi_vars_post_load(void *opaque, int version_id) +{ + uefi_variable *var = opaque; + + if (!uefi_str_is_valid(var->name, var->name_size, true) || + var->attributes & ~EFI_VARIABLE_ATTRIBUTE_SUPPORTED || + (var->digest_size != 0 && + var->digest_size != 32 /* AUTHVAR_DIGEST_SIZE */)) { + error_report("invalid uefi variable"); + return -1; + } + return 0; +} + const VMStateDescription vmstate_uefi_variable = { .name = "uefi-variable", + .post_load = uefi_vars_post_load, .fields = (VMStateField[]) { VMSTATE_UINT8_ARRAY_V(guid.data, uefi_variable, sizeof(QemuUUID), 0), VMSTATE_UINT32(name_size, uefi_variable), @@ -164,11 +179,15 @@ void uefi_vars_update_storage(uefi_vars_state *uv) { uefi_variable *var; + uefi_var_policy *pol; uv->used_storage = 0; QTAILQ_FOREACH(var, &uv->variables, next) { uv->used_storage += variable_size(var); } + QTAILQ_FOREACH(pol, &uv->var_policies, next) { + uv->used_storage += pol->entry->size; + } } static gboolean check_access(uefi_vars_state *uv, uefi_variable *var) diff -Nru qemu-10.0.12+ds/hw/usb/core.c qemu-10.0.13+ds/hw/usb/core.c --- qemu-10.0.12+ds/hw/usb/core.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/usb/core.c 2026-08-26 22:31:25.000000000 +0300 @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "hw/usb.h" #include "qemu/iov.h" +#include "qemu/log.h" #include "trace.h" void usb_pick_speed(USBPort *port) @@ -288,6 +289,15 @@ p->status = USB_RET_STALL; return; } + if ((p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_IN) && + setup_len > p->iov.size) { + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: setup state param length %u > iov size %zu\n", + setup_len, p->iov.size); + p->status = USB_RET_STALL; + return; + } + s->setup_len = setup_len; if (p->pid == USB_TOKEN_OUT) { diff -Nru qemu-10.0.12+ds/hw/usb/dev-uas.c qemu-10.0.13+ds/hw/usb/dev-uas.c --- qemu-10.0.12+ds/hw/usb/dev-uas.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/usb/dev-uas.c 2026-08-26 22:31:25.000000000 +0300 @@ -360,9 +360,11 @@ UASDevice *uas = opaque; UASStatus *st; USBPacket *p; + uint32_t length; while ((st = QTAILQ_FIRST(&uas->results)) != NULL) { if (uas_using_streams(uas)) { + assert(st->stream <= UAS_MAX_STREAMS); p = uas->status3[st->stream]; uas->status3[st->stream] = NULL; } else { @@ -373,7 +375,14 @@ break; } - usb_packet_copy(p, &st->status, st->length); + length = st->length; + if (length > p->iov.size) { + qemu_log_mask(LOG_GUEST_ERROR, + "usb uas: packet (%zd) too small for status (%d)\n", + p->iov.size, length); + length = p->iov.size; + } + usb_packet_copy(p, &st->status, length); QTAILQ_REMOVE(&uas->results, st, next); g_free(st); @@ -384,8 +393,14 @@ static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length) { - USBPacket *p = uas_using_streams(uas) ? - uas->status3[st->stream] : uas->status2; + USBPacket *p; + + if (uas_using_streams(uas)) { + assert(st->stream <= UAS_MAX_STREAMS); + p = uas->status3[st->stream]; + } else { + p = uas->status2; + } st->length += length; QTAILQ_INSERT_TAIL(&uas->results, st, next); @@ -701,14 +716,22 @@ uint16_t tag = be16_to_cpu(iu->hdr.tag); size_t cdb_len = sizeof(iu->command.cdb) + iu->command.add_cdb_length; + if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { + /* + * Our status delivery only works with valid tags, so in case the + * stream ID is out of bounds, we have to return immediately here + * without sending a fake sense_code_INVALID_TAG to the guest. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "invalid tag 0x%x for USB UAS command\n", tag); + return; + } + if (iu->command.add_cdb_length > 0) { qemu_log_mask(LOG_UNIMP, "additional adb length not yet supported\n"); goto unsupported_len; } - if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { - goto invalid_tag; - } req = usb_uas_find_request(uas, tag); if (req) { goto overlapped_tag; @@ -745,10 +768,6 @@ usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_PARAM_VALUE); return; -invalid_tag: - usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_TAG); - return; - overlapped_tag: usb_uas_queue_fake_sense(uas, tag, sense_code_OVERLAPPED_COMMANDS); return; @@ -865,7 +884,14 @@ break; } } - usb_packet_copy(p, &st->status, st->length); + length = st->length; + if (length > p->iov.size) { + qemu_log_mask(LOG_GUEST_ERROR, + "usb uas: packet (%zd) too small for status (%d)\n", + p->iov.size, length); + length = p->iov.size; + } + usb_packet_copy(p, &st->status, length); QTAILQ_REMOVE(&uas->results, st, next); g_free(st); break; diff -Nru qemu-10.0.12+ds/hw/usb/hcd-xhci.c qemu-10.0.13+ds/hw/usb/hcd-xhci.c --- qemu-10.0.12+ds/hw/usb/hcd-xhci.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/usb/hcd-xhci.c 2026-08-26 22:31:25.000000000 +0300 @@ -1458,7 +1458,8 @@ switch (TRB_TYPE(*trb)) { case TR_DATA: if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) { - DPRINTF("xhci: data direction mismatch for TR_DATA\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: data direction mismatch for TR_DATA\n"); goto err; } /* fallthrough */ @@ -1468,7 +1469,8 @@ chunk = trb->status & 0x1ffff; if (trb->control & TRB_TR_IDT) { if (chunk > 8 || in_xfer) { - DPRINTF("xhci: invalid immediate data TRB\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: invalid immediate data TRB\n"); goto err; } qemu_sglist_add(&xfer->sgl, trb->addr, chunk); @@ -1617,7 +1619,9 @@ } } - xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */ + if (xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN) < 0) { /* Also sets int_req */ + return -1; + } usb_packet_setup(&xfer->packet, dir, ep, xfer->streamid, xfer->trbs[0].addr, false, xfer->int_req); if (usb_packet_map(&xfer->packet, &xfer->sgl)) { diff -Nru qemu-10.0.12+ds/hw/vfio/igd.c qemu-10.0.13+ds/hw/vfio/igd.c --- qemu-10.0.12+ds/hw/vfio/igd.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/vfio/igd.c 2026-08-26 22:31:25.000000000 +0300 @@ -680,3 +680,118 @@ return vfio_pci_igd_config_quirk(vdev, errp); } + +/* + * IGD ROM BAR read from kernel is actually the host VBIOS shadow RAM region, + * which contains host modifications. In Gen 6-9 VBIOS, the routine below is + * used to get BDSM value when programming the initial GTT. + * xx xx xx xx v: .long ? # saved value + * 66 53 push %ebx + * 66 2e 83 3e xx xx 00 cmpl $0x0,%cs:v # is saved value empty? + * 74 07 je 1f # if zero, go compute + * 66 2e a1 xx xx mov %cs:v,%eax # else return saved value + * eb 0f jmp 2f + * b8 5e 10 1: mov $0x105e,%ax # dev 00:02.0, offset 5E + * e8 xx xx call pci_read_cfg_word + * 66 c1 e0 10 shl $0x10,%eax # left shift 16 bits + * 66 2e a3 xx xx mov %eax,%cs:v # save the result + * 66 5b 2: pop %ebx + * c3 ret + * When running the VBIOS in guest, saved value still reflects the host stolen + * memory base address, which is not correct in guest. So we need to patch the + * VBIOS to clear the saved value. + * + * The unique 19-byte starts at `cmpl $0,%cs:v` and ends at `mov $0x105e,%ax` + * anchors the match to the routine. Both `cs:` displacements must reference + * the same offset. + */ +static int igd_vbios_find_saved_bdsm(const uint8_t *rom, size_t rom_size, + uint16_t *bdsm_offset) +{ + static const uint8_t start[] = { 0x66, 0x2e, 0x83, 0x3e }; + static const uint8_t middle[] = { 0x00, 0x74, 0x07, 0x66, 0x2e, 0xa1 }; + static const uint8_t end[] = { 0xeb, 0x0f, 0xb8, 0x5e, 0x10 }; + uint16_t val; + size_t i; + bool found = false; + + if (rom_size < 19) { + return -ENOENT; + } + + for (i = 0; i + 19 <= rom_size; i++) { + if (memcmp(rom + i, start, sizeof(start)) != 0 || + memcmp(rom + i + 6, middle, sizeof(middle)) != 0 || + memcmp(rom + i + 14, end, sizeof(end)) != 0) { + continue; + } + + /* same saved value address? */ + if (rom[i + 4] != rom[i + 12] || rom[i + 5] != rom[i + 13]) { + continue; + } + + if (found) { + return -EEXIST; + } + + val = rom[i + 4] | ((uint16_t)rom[i + 5] << 8); + if (val + sizeof(uint32_t) <= rom_size) { + *bdsm_offset = val; + found = true; + } + } + + if (!found) { + return -ENOENT; + } + + return 0; +} + +void vfio_igd_legacy_rom_quirk(VFIOPCIDevice *vdev) +{ + uint8_t *rom = vdev->rom; + int gen; + uint16_t pcir_offset; + uint16_t bdsm_offset = 0; + uint8_t checksum = 0; + uint32_t i; + + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || + !vfio_is_vga(vdev) || !vdev->vga) { + return; + } + + /* Only Gen 6~9 devices have legacy VBIOS as Option ROM */ + gen = igd_gen(vdev); + if (gen < 6 || gen > 9) { + return; + } + + if (pci_get_word(rom) != 0xaa55) { + return; + } + + /* Must be a legacy ROM */ + pcir_offset = pci_get_word(rom + 0x18); + if (pcir_offset + 0x14 >= vdev->rom_size || + memcmp(rom + pcir_offset, "PCIR", 4) || + pci_get_byte(rom + pcir_offset + 0x14) != 0x00) { + return; + } + + /* Search and clear the saved BDSM value */ + if (igd_vbios_find_saved_bdsm(rom, vdev->rom_size, &bdsm_offset)) { + return; + } + memset(rom + bdsm_offset, 0, sizeof(uint32_t)); + + /* Recalculate checksum and patch it. */ + for (i = 0; i < vdev->rom_size; i++) { + checksum += rom[i]; + } + rom[6] -= checksum; + + trace_vfio_pci_igd_vbios_patched(vdev->vbasedev.name); +} diff -Nru qemu-10.0.12+ds/hw/vfio/pci-quirks.c qemu-10.0.13+ds/hw/vfio/pci-quirks.c --- qemu-10.0.12+ds/hw/vfio/pci-quirks.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/vfio/pci-quirks.c 2026-08-26 22:31:25.000000000 +0300 @@ -1594,3 +1594,10 @@ return true; } + +void vfio_rom_quirk_setup(VFIOPCIDevice *vdev) +{ +#ifdef CONFIG_VFIO_IGD + vfio_igd_legacy_rom_quirk(vdev); +#endif +} diff -Nru qemu-10.0.12+ds/hw/vfio/pci.c qemu-10.0.13+ds/hw/vfio/pci.c --- qemu-10.0.12+ds/hw/vfio/pci.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/vfio/pci.c 2026-08-26 22:31:25.000000000 +0300 @@ -263,16 +263,27 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) { - uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); + uint32_t val = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); + uint8_t pin; Error *err = NULL; int32_t fd; int ret; + if (val == (uint32_t)-1) { + error_setg(errp, "failed to read PCI_INTERRUPT_PIN"); + return false; + } + pin = val; if (!pin) { return true; } + if (pin > PCI_NUM_PINS) { + error_setg(errp, "invalid PCI interrupt pin %d", pin); + return false; + } + vfio_disable_interrupts(vdev); vdev->intx.pin = pin - 1; /* Pin A (1) -> irq[0] */ @@ -877,7 +888,7 @@ } } -static void vfio_pci_load_rom(VFIOPCIDevice *vdev) +static bool vfio_pci_load_rom(VFIOPCIDevice *vdev) { g_autofree struct vfio_region_info *reg_info = NULL; uint64_t size; @@ -887,7 +898,7 @@ if (vfio_get_region_info(&vdev->vbasedev, VFIO_PCI_ROM_REGION_INDEX, ®_info)) { error_report("vfio: Error getting ROM info: %m"); - return; + return false; } trace_vfio_pci_load_rom(vdev->vbasedev.name, (unsigned long)reg_info->size, @@ -898,13 +909,12 @@ vdev->rom_offset = reg_info->offset; if (!vdev->rom_size) { - vdev->rom_read_failed = true; error_report("vfio-pci: Cannot read device rom at " "%s", vdev->vbasedev.name); error_printf("Device option ROM contents are probably invalid " "(check dmesg).\nSkip option ROM probe with rombar=0, " "or load from file with romfile=\n"); - return; + return false; } vdev->rom = g_malloc(size); @@ -956,6 +966,10 @@ data[6] = -csum; } } + + vfio_rom_quirk_setup(vdev); + + return true; } static uint64_t vfio_rom_read(void *opaque, hwaddr addr, unsigned size) @@ -971,7 +985,7 @@ /* Load the ROM lazily when the guest tries to read it */ if (unlikely(!vdev->rom && !vdev->rom_read_failed)) { - vfio_pci_load_rom(vdev); + vdev->rom_read_failed = !vfio_pci_load_rom(vdev); } memcpy(&val, vdev->rom + addr, @@ -2406,6 +2420,7 @@ void vfio_pci_pre_reset(VFIOPCIDevice *vdev) { PCIDevice *pdev = &vdev->pdev; + uint32_t val; uint16_t cmd; vfio_disable_interrupts(vdev); @@ -2414,23 +2429,34 @@ * Stop any ongoing DMA by disconnecting I/O, MMIO, and bus master. * Also put INTx Disable in known state. */ - cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2); - cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | - PCI_COMMAND_INTX_DISABLE); - vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); + val = vfio_pci_read_config(pdev, PCI_COMMAND, 2); + if (val != (uint32_t)-1) { + cmd = val; + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_INTX_DISABLE); + vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); + } /* Make sure the device is in D0 */ if (pdev->pm_cap) { uint16_t pmcsr; uint8_t state; - pmcsr = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); + val = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); + if (val == (uint32_t)-1) { + return; + } + pmcsr = val; state = pmcsr & PCI_PM_CTRL_STATE_MASK; if (state) { pmcsr &= ~PCI_PM_CTRL_STATE_MASK; vfio_pci_write_config(pdev, pdev->pm_cap + PCI_PM_CTRL, pmcsr, 2); /* vfio handles the necessary delay here */ - pmcsr = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); + val = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); + if (val == (uint32_t)-1) { + return; + } + pmcsr = val; state = pmcsr & PCI_PM_CTRL_STATE_MASK; if (state) { error_report("vfio: Unable to power on device, stuck in D%d", diff -Nru qemu-10.0.12+ds/hw/vfio/pci.h qemu-10.0.13+ds/hw/vfio/pci.h --- qemu-10.0.12+ds/hw/vfio/pci.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/vfio/pci.h 2026-08-26 22:31:25.000000000 +0300 @@ -217,10 +217,13 @@ void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr); void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev); bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp); +void vfio_rom_quirk_setup(VFIOPCIDevice *vdev); void vfio_quirk_reset(VFIOPCIDevice *vdev); VFIOQuirk *vfio_quirk_alloc(int nr_mem); + void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr); bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp); +void vfio_igd_legacy_rom_quirk(VFIOPCIDevice *vdev); extern const PropertyInfo qdev_prop_nv_gpudirect_clique; diff -Nru qemu-10.0.12+ds/hw/vfio/trace-events qemu-10.0.13+ds/hw/vfio/trace-events --- qemu-10.0.12+ds/hw/vfio/trace-events 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/vfio/trace-events 2026-08-26 22:31:25.000000000 +0300 @@ -88,6 +88,7 @@ vfio_pci_igd_bdsm_enabled(const char *name, int size) "%s %dMB" vfio_pci_igd_host_bridge_enabled(const char *name) "%s" vfio_pci_igd_lpc_bridge_enabled(const char *name) "%s" +vfio_pci_igd_vbios_patched(const char *name) "%s" # common.c vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)" diff -Nru qemu-10.0.12+ds/hw/virtio/vdpa-dev.c qemu-10.0.13+ds/hw/virtio/vdpa-dev.c --- qemu-10.0.12+ds/hw/virtio/vdpa-dev.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/virtio/vdpa-dev.c 2026-08-26 22:31:25.000000000 +0300 @@ -213,7 +213,7 @@ VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); int ret; - ret = vhost_dev_set_config(&s->dev, s->config, 0, s->config_size, + ret = vhost_dev_set_config(&s->dev, config, 0, s->config_size, VHOST_SET_CONFIG_TYPE_FRONTEND); if (ret) { error_report("set device config space failed"); diff -Nru qemu-10.0.12+ds/hw/virtio/vhost-user.c qemu-10.0.13+ds/hw/virtio/vhost-user.c --- qemu-10.0.12+ds/hw/virtio/vhost-user.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/virtio/vhost-user.c 2026-08-26 22:31:25.000000000 +0300 @@ -838,6 +838,9 @@ msg->hdr.size = sizeof(msg->payload.mem_reg); + /* Ensure nregions fits the fixed-size arrays used below. */ + assert(dev->mem->nregions <= VHOST_USER_MAX_RAM_SLOTS); + /* Find the regions which need to be removed or added. */ scrub_shadow_regions(dev, add_reg, &nr_add_reg, rem_reg, &nr_rem_reg, shadow_pcb, track_ramblocks); diff -Nru qemu-10.0.12+ds/hw/virtio/vhost.c qemu-10.0.13+ds/hw/virtio/vhost.c --- qemu-10.0.12+ds/hw/virtio/vhost.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/virtio/vhost.c 2026-08-26 22:31:25.000000000 +0300 @@ -681,8 +681,9 @@ (void *)(uintptr_t)dev->mem->regions[i].userspace_addr, dev->mem->regions[i].guest_phys_addr, dev->mem->regions[i].memory_size)) { - error_report("Verify ring failure on region %d", i); - abort(); + virtio_error(dev->vdev, + "Verify ring failure on region %d", i); + goto out; } } diff -Nru qemu-10.0.12+ds/hw/virtio/virtio-iommu.c qemu-10.0.13+ds/hw/virtio/virtio-iommu.c --- qemu-10.0.12+ds/hw/virtio/virtio-iommu.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/virtio/virtio-iommu.c 2026-08-26 22:31:25.000000000 +0300 @@ -994,6 +994,18 @@ return ret ? ret : virtio_iommu_probe(s, &req, buf); } +static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq); + +static void virtio_iommu_handle_command_timer(void *opaque) +{ + VirtIOIOMMU *s = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(s); + + if (virtio_device_started(vdev, vdev->status) && !vdev->broken) { + virtio_iommu_handle_command(vdev, s->req_vq); + } +} + static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) { VirtIOIOMMU *s = VIRTIO_IOMMU(vdev); @@ -1004,10 +1016,17 @@ struct iovec *iov; void *buf = NULL; size_t sz; + unsigned int batch = 0; for (;;) { size_t output_size = sizeof(tail); + if (++batch > virtio_queue_get_num(vdev, virtio_get_queue_index(vq))) { + timer_mod(s->cmd_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) + 1); + break; + } + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { return; @@ -1417,6 +1436,8 @@ s->req_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, virtio_iommu_handle_command); s->event_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, NULL); + s->cmd_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, + virtio_iommu_handle_command_timer, s); /* * config.bypass is needed to get initial address space early, such as @@ -1499,6 +1520,7 @@ qemu_rec_mutex_destroy(&s->mutex); + timer_free(s->cmd_timer); virtio_delete_queue(s->req_vq); virtio_delete_queue(s->event_vq); virtio_cleanup(vdev); @@ -1510,6 +1532,8 @@ trace_virtio_iommu_device_reset_exit(); + timer_del(s->cmd_timer); + if (s->domains) { g_tree_destroy(s->domains); } @@ -1628,6 +1652,11 @@ * still correct. */ virtio_iommu_switch_address_space_all(s); + + if (virtio_device_started(VIRTIO_DEVICE(s), VIRTIO_DEVICE(s)->status)) { + timer_mod(s->cmd_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) + 1); + } return 0; } diff -Nru qemu-10.0.12+ds/hw/virtio/virtio-pmem.c qemu-10.0.13+ds/hw/virtio/virtio-pmem.c --- qemu-10.0.12+ds/hw/virtio/virtio-pmem.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/virtio/virtio-pmem.c 2026-08-26 22:31:25.000000000 +0300 @@ -24,6 +24,7 @@ #include "system/hostmem.h" #include "block/aio.h" #include "block/thread-pool.h" +#include "block/aio-wait.h" #include "trace.h" typedef struct VirtIODeviceRequest { @@ -55,14 +56,20 @@ static void done_cb(void *opaque, int ret) { VirtIODeviceRequest *req_data = opaque; + VirtIOPMEM *pmem = req_data->pmem; int len = iov_from_buf(req_data->elem.in_sg, req_data->elem.in_num, 0, &req_data->resp, sizeof(struct virtio_pmem_resp)); /* Callbacks are serialized, so no need to use atomic ops. */ - virtqueue_push(req_data->pmem->rq_vq, &req_data->elem, len); - virtio_notify((VirtIODevice *)req_data->pmem, req_data->pmem->rq_vq); + virtqueue_push(pmem->rq_vq, &req_data->elem, len); + virtio_notify((VirtIODevice *)pmem, pmem->rq_vq); trace_virtio_pmem_response(); g_free(req_data); + + pmem->inflight--; + if (!pmem->inflight) { + aio_wait_kick(); + } } static void virtio_pmem_flush(VirtIODevice *vdev, VirtQueue *vq) @@ -86,6 +93,7 @@ req_data->fd = memory_region_get_fd(&backend->mr); req_data->pmem = pmem; req_data->vdev = vdev; + pmem->inflight++; thread_pool_submit_aio(worker_cb, req_data, done_cb, req_data); } @@ -123,6 +131,7 @@ host_memory_backend_set_mapped(pmem->memdev, true); virtio_init(vdev, VIRTIO_ID_PMEM, sizeof(struct virtio_pmem_config)); pmem->rq_vq = virtio_add_queue(vdev, 128, virtio_pmem_flush); + pmem->inflight = 1; } static void virtio_pmem_unrealize(DeviceState *dev) @@ -130,6 +139,10 @@ VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOPMEM *pmem = VIRTIO_PMEM(dev); + /* Release the device's own reference and wait for in-flight flushes */ + pmem->inflight--; + AIO_WAIT_WHILE(NULL, pmem->inflight > 0); + host_memory_backend_set_mapped(pmem->memdev, false); virtio_delete_queue(pmem->rq_vq); virtio_cleanup(vdev); diff -Nru qemu-10.0.12+ds/hw/virtio/virtio-rng.c qemu-10.0.13+ds/hw/virtio/virtio-rng.c --- qemu-10.0.12+ds/hw/virtio/virtio-rng.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/virtio/virtio-rng.c 2026-08-26 22:31:25.000000000 +0300 @@ -233,6 +233,8 @@ VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIORNG *vrng = VIRTIO_RNG(dev); + rng_backend_cancel_requests(vrng->rng, chr_read, vrng); + qemu_del_vm_change_state_handler(vrng->vmstate); timer_free(vrng->rate_limit_timer); virtio_del_queue(vdev, 0); diff -Nru qemu-10.0.12+ds/hw/virtio/virtio.c qemu-10.0.13+ds/hw/virtio/virtio.c --- qemu-10.0.12+ds/hw/virtio/virtio.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/virtio/virtio.c 2026-08-26 22:31:25.000000000 +0300 @@ -714,6 +714,10 @@ struct VRingPackedDesc desc; VRingMemoryRegionCaches *cache; + if (virtio_device_disabled(vq->vdev)) { + return 1; + } + if (unlikely(!vq->vring.desc)) { return 1; } @@ -1426,7 +1430,7 @@ } if (desc.flags & VRING_DESC_F_INDIRECT) { - if (desc.len % sizeof(VRingPackedDesc)) { + if (!desc.len || (desc.len % sizeof(VRingPackedDesc))) { virtio_error(vdev, "Invalid size for indirect buffer table"); goto err; } @@ -1876,7 +1880,7 @@ vring_packed_desc_read(vdev, &desc, desc_cache, i, true); id = desc.id; if (desc.flags & VRING_DESC_F_INDIRECT) { - if (desc.len % sizeof(VRingPackedDesc)) { + if (!desc.len || (desc.len % sizeof(VRingPackedDesc))) { virtio_error(vdev, "Invalid size for indirect buffer table"); goto done; } @@ -2557,6 +2561,12 @@ if (i == VIRTIO_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE) abort(); + /* + * Always set to max queue size for qemu <11.1. See discussion starting + * https://lore.kernel.org/qemu-devel/a5cff318f06cd06b37224e15ee74d64d1df8b12b.1785179875.git.mst@redhat.com/ + */ + queue_size = VIRTQUEUE_MAX_SIZE; + vdev->vq[i].vring.num = queue_size; vdev->vq[i].vring.num_default = queue_size; vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN; @@ -3216,7 +3226,7 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) { int i, ret; - int32_t config_len; + uint32_t config_len; uint32_t num; uint32_t features; BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -3263,6 +3273,9 @@ qemu_get_buffer(f, vdev->config, MIN(config_len, vdev->config_len)); while (config_len > vdev->config_len) { + if (qemu_file_get_error(f)) { + return -1; + } qemu_get_byte(f); config_len--; } diff -Nru qemu-10.0.12+ds/hw/watchdog/spapr_watchdog.c qemu-10.0.13+ds/hw/watchdog/spapr_watchdog.c --- qemu-10.0.12+ds/hw/watchdog/spapr_watchdog.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/hw/watchdog/spapr_watchdog.c 2026-08-26 22:31:25.000000000 +0300 @@ -127,6 +127,12 @@ } } +static inline bool watchdog_number_valid(target_ulong watchdogNumber, + SpaprMachineState *spapr) +{ + return watchdogNumber >= 1 && watchdogNumber <= ARRAY_SIZE(spapr->wds); +} + static target_ulong h_watchdog(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) @@ -145,7 +151,7 @@ switch (operation) { case PSERIES_WDTF_OP_START: - if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { + if (!watchdog_number_valid(watchdogNumber, spapr)) { return H_P2; } if (timeoutInMs <= WDT_MIN_TIMEOUT) { @@ -170,11 +176,11 @@ case PSERIES_WDTF_OP_STOP: if (watchdogNumber == PSERIES_WDT_STOP_ALL) { ret = watchdog_stop_all(spapr); - } else if (watchdogNumber <= ARRAY_SIZE(spapr->wds)) { + } else if (!watchdog_number_valid(watchdogNumber, spapr)) { + return H_P2; + } else { ret = watchdog_stop(watchdogNumber, &spapr->wds[watchdogNumber - 1]); - } else { - return H_P2; } break; case PSERIES_WDTF_OP_QUERY: @@ -184,7 +190,7 @@ trace_spapr_watchdog_query(args[0]); break; case PSERIES_WDTF_OP_QUERY_LPM: - if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { + if (!watchdog_number_valid(watchdogNumber, spapr)) { return H_P2; } args[0] = PSERIES_WDTQL_QUERY_NOT_STOPPED; diff -Nru qemu-10.0.12+ds/include/block/accounting.h qemu-10.0.13+ds/include/block/accounting.h --- qemu-10.0.12+ds/include/block/accounting.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/include/block/accounting.h 2026-08-26 22:31:25.000000000 +0300 @@ -115,6 +115,7 @@ void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, int num_requests); int64_t block_acct_idle_time_ns(BlockAcctStats *stats); +/* Caller must hold stats->stats->lock. */ double block_acct_queue_depth(BlockAcctTimedStats *stats, enum BlockAcctType type); int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, diff -Nru qemu-10.0.12+ds/include/exec/memory.h qemu-10.0.13+ds/include/exec/memory.h --- qemu-10.0.12+ds/include/exec/memory.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/include/exec/memory.h 2026-08-26 22:31:25.000000000 +0300 @@ -1257,6 +1257,8 @@ */ void memory_region_unref(MemoryRegion *mr); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(MemoryRegion, memory_region_unref) + /** * memory_region_init_io: Initialize an I/O memory region. * diff -Nru qemu-10.0.12+ds/include/hw/elf_ops.h.inc qemu-10.0.13+ds/include/hw/elf_ops.h.inc --- qemu-10.0.12+ds/include/hw/elf_ops.h.inc 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/include/hw/elf_ops.h.inc 2026-08-26 22:31:26.000000000 +0300 @@ -427,6 +427,11 @@ file_size = ph->p_filesz; /* Size of the allocated data */ data_offset = ph->p_offset; /* Offset where the data is located */ + if (file_size > mem_size) { + ret = ELF_LOAD_TOO_BIG; + goto fail; + } + if (file_size > 0) { if (g_mapped_file_get_length(mapped_file) < file_size + data_offset) { diff -Nru qemu-10.0.12+ds/include/hw/i2c/bcm2835_i2c.h qemu-10.0.13+ds/include/hw/i2c/bcm2835_i2c.h --- qemu-10.0.12+ds/include/hw/i2c/bcm2835_i2c.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/include/hw/i2c/bcm2835_i2c.h 2026-08-26 22:31:26.000000000 +0300 @@ -38,7 +38,7 @@ #define BCM2835_I2C_FIFO 0x10 /* FIFO */ #define BCM2835_I2C_DIV 0x14 /* Clock Divider */ #define BCM2835_I2C_DEL 0x18 /* Data Delay */ -#define BCM2835_I2C_CLKT 0x20 /* Clock Stretch Timeout */ +#define BCM2835_I2C_CLKT 0x1c /* Clock Stretch Timeout */ #define BCM2835_I2C_C_I2CEN BIT(15) /* I2C enable */ #define BCM2835_I2C_C_INTR BIT(10) /* Interrupt on RXR */ diff -Nru qemu-10.0.12+ds/include/hw/scsi/scsi.h qemu-10.0.13+ds/include/hw/scsi/scsi.h --- qemu-10.0.12+ds/include/hw/scsi/scsi.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/include/hw/scsi/scsi.h 2026-08-26 22:31:26.000000000 +0300 @@ -211,6 +211,7 @@ int32_t scsi_req_enqueue(SCSIRequest *req); SCSIRequest *scsi_req_ref(SCSIRequest *req); void scsi_req_unref(SCSIRequest *req); +void scsi_req_unref_detach_hba(SCSIRequest *req); int scsi_bus_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, size_t buf_len, void *hba_private); diff -Nru qemu-10.0.12+ds/include/hw/virtio/virtio-gpu.h qemu-10.0.13+ds/include/hw/virtio/virtio-gpu.h --- qemu-10.0.12+ds/include/hw/virtio/virtio-gpu.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/include/hw/virtio/virtio-gpu.h 2026-08-26 22:31:26.000000000 +0300 @@ -65,7 +65,6 @@ struct virtio_gpu_framebuffer { pixman_format_code_t format; - uint32_t bytes_pp; uint32_t width, height; uint32_t stride; uint32_t offset; @@ -297,6 +296,9 @@ qemu_log_mask(LOG_GUEST_ERROR, \ "%s: command size incorrect %zu vs %zu\n", \ __func__, virtiogpufillcmd_s_, sizeof(out)); \ + memset(&out, 0, sizeof(out)); \ + virtio_gpu_ctrl_response_nodata( \ + g, cmd, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER); \ return; \ } \ } while (0) @@ -345,6 +347,11 @@ struct virtio_gpu_scanout *s, uint32_t resource_id); +bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + const struct virtio_gpu_rect *r, + uint32_t *error); + /** * virtio_gpu_scanout_blob_to_fb() - fill out fb based on scanout data * fb: the frame-buffer descriptor to fill out diff -Nru qemu-10.0.12+ds/include/hw/virtio/virtio-iommu.h qemu-10.0.13+ds/include/hw/virtio/virtio-iommu.h --- qemu-10.0.12+ds/include/hw/virtio/virtio-iommu.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/include/hw/virtio/virtio-iommu.h 2026-08-26 22:31:26.000000000 +0300 @@ -65,6 +65,7 @@ GTree *domains; QemuRecMutex mutex; GTree *endpoints; + QEMUTimer *cmd_timer; bool boot_bypass; Notifier machine_done; bool granule_frozen; diff -Nru qemu-10.0.12+ds/include/hw/virtio/virtio-pmem.h qemu-10.0.13+ds/include/hw/virtio/virtio-pmem.h --- qemu-10.0.12+ds/include/hw/virtio/virtio-pmem.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/include/hw/virtio/virtio-pmem.h 2026-08-26 22:31:26.000000000 +0300 @@ -32,6 +32,7 @@ VirtQueue *rq_vq; uint64_t start; HostMemoryBackend *memdev; + unsigned int inflight; }; struct VirtIOPMEMClass { diff -Nru qemu-10.0.12+ds/include/qemu/coroutine.h qemu-10.0.13+ds/include/qemu/coroutine.h --- qemu-10.0.12+ds/include/qemu/coroutine.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/include/qemu/coroutine.h 2026-08-26 22:31:26.000000000 +0300 @@ -260,10 +260,19 @@ uint64_t timeout_ns, CleanupFunc clean); /** - * Wake a coroutine if it is sleeping in qemu_co_sleep_ns. The timer will be - * deleted. @sleep_state must be the variable whose address was given to - * qemu_co_sleep_ns() and should be checked to be non-NULL before calling - * qemu_co_sleep_wake(). + * Wake a coroutine sleeping in qemu_co_sleep() or qemu_co_sleep_ns_wakeable(). + * The timer set up by the latter is deleted on wakeup. + * + * The wake is sticky: if no sleeper is parked on @w at the time of the call, + * the wake is recorded on @w and consumed by the next qemu_co_sleep() on the + * same @w, which then returns without yielding. This closes the lost-wakeup + * window between two sleeps and is the documented behavior callers should + * rely on -- e.g. a cancellation signal raised between iterations of a + * sleep/work loop will shorten the next sleep instead of being dropped. + * + * The state persists until consumed: if no further qemu_co_sleep() is ever + * called on @w, the pending wake is harmlessly discarded when @w goes away. + * Multiple wakes coalesce -- the next sleep consumes at most one. */ void qemu_co_sleep_wake(QemuCoSleep *w); diff -Nru qemu-10.0.12+ds/include/system/rng.h qemu-10.0.13+ds/include/system/rng.h --- qemu-10.0.12+ds/include/system/rng.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/include/system/rng.h 2026-08-26 22:31:26.000000000 +0300 @@ -86,4 +86,18 @@ * deleted. */ void rng_backend_finalize_request(RngBackend *s, RngRequest *req); + +/** + * rng_backend_cancel_requests: + * @s: the backend that created the request + * @receive_entropy: the function invoked when entropy is available + * @opaque: data passed to @receive_entropy + * + * This function is used by the front-end to cancel all requests to a + * given backend. Requests to cancel are identified by the receive_entropy + * function and the data passed to the function. + */ +void rng_backend_cancel_requests(RngBackend *s, + EntropyReceiveFunc *receive_entropy, + const void *opaque); #endif diff -Nru qemu-10.0.12+ds/include/tcg/tcg-op-common.h qemu-10.0.13+ds/include/tcg/tcg-op-common.h --- qemu-10.0.12+ds/include/tcg/tcg-op-common.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/include/tcg/tcg-op-common.h 2026-08-26 22:31:26.000000000 +0300 @@ -150,6 +150,7 @@ void tcg_gen_smax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_umin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_umax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_ussub_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_abs_i32(TCGv_i32, TCGv_i32); /* Replicate a value of size @vece from @in to all the lanes in @out */ @@ -258,6 +259,7 @@ void tcg_gen_smax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_umin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_umax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_ussub_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_abs_i64(TCGv_i64, TCGv_i64); /* Replicate a value of size @vece from @in to all the lanes in @out */ diff -Nru qemu-10.0.12+ds/include/tcg/tcg-op.h qemu-10.0.13+ds/include/tcg/tcg-op.h --- qemu-10.0.12+ds/include/tcg/tcg-op.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/include/tcg/tcg-op.h 2026-08-26 22:31:26.000000000 +0300 @@ -259,6 +259,7 @@ #define tcg_gen_umin_tl tcg_gen_umin_i64 #define tcg_gen_smax_tl tcg_gen_smax_i64 #define tcg_gen_umax_tl tcg_gen_umax_i64 +#define tcg_gen_ussub_tl tcg_gen_ussub_i64 #define tcg_gen_atomic_cmpxchg_tl tcg_gen_atomic_cmpxchg_i64 #define tcg_gen_atomic_xchg_tl tcg_gen_atomic_xchg_i64 #define tcg_gen_atomic_fetch_add_tl tcg_gen_atomic_fetch_add_i64 @@ -377,6 +378,7 @@ #define tcg_gen_umin_tl tcg_gen_umin_i32 #define tcg_gen_smax_tl tcg_gen_smax_i32 #define tcg_gen_umax_tl tcg_gen_umax_i32 +#define tcg_gen_ussub_tl tcg_gen_ussub_i32 #define tcg_gen_atomic_cmpxchg_tl tcg_gen_atomic_cmpxchg_i32 #define tcg_gen_atomic_xchg_tl tcg_gen_atomic_xchg_i32 #define tcg_gen_atomic_fetch_add_tl tcg_gen_atomic_fetch_add_i32 diff -Nru qemu-10.0.12+ds/linux-user/sh4/cpu_loop.c qemu-10.0.13+ds/linux-user/sh4/cpu_loop.c --- qemu-10.0.12+ds/linux-user/sh4/cpu_loop.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/linux-user/sh4/cpu_loop.c 2026-08-26 22:31:26.000000000 +0300 @@ -64,6 +64,13 @@ cpu_exec_step_atomic(cs); arch_interrupt = false; break; + case 0x180: + /* Illegal instruction */ + /* fallthrough */ + case 0x1a0: + /* Illegal instruction in delay slot */ + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->pc); + break; default: fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, 0); diff -Nru qemu-10.0.12+ds/linux-user/sh4/signal.c qemu-10.0.13+ds/linux-user/sh4/signal.c --- qemu-10.0.12+ds/linux-user/sh4/signal.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/linux-user/sh4/signal.c 2026-08-26 22:31:26.000000000 +0300 @@ -109,7 +109,7 @@ the SP, otherwise we would be pushing the signal context to invalid memory. */ regs->gregs[15] = regs->gregs[1]; - } else if (regs->flags & TB_FLAG_DELAY_SLOT) { + } else if (regs->flags & (TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND)) { /* If we are in a delay slot, push the previous instruction. */ regs->pc -= 2; } @@ -206,6 +206,8 @@ __put_user(set->sig[i + 1], &frame->extramask[i]); } + regs->fpscr = FPSCR_PR; + /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa_flags & TARGET_SA_RESTORER) { @@ -258,6 +260,8 @@ __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } + regs->fpscr = FPSCR_PR; + /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa_flags & TARGET_SA_RESTORER) { diff -Nru qemu-10.0.12+ds/linux-user/sh4/target_mman.h qemu-10.0.13+ds/linux-user/sh4/target_mman.h --- qemu-10.0.12+ds/linux-user/sh4/target_mman.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/linux-user/sh4/target_mman.h 2026-08-26 22:31:26.000000000 +0300 @@ -1,6 +1,6 @@ /* arch/sh/include/asm/processor_32.h */ #define TASK_UNMAPPED_BASE \ - TARGET_PAGE_ALIGN((1u << TARGET_VIRT_ADDR_SPACE_BITS) / 3) + TARGET_PAGE_ALIGN((1ull << TARGET_VIRT_ADDR_SPACE_BITS) / 3) /* arch/sh/include/asm/elf.h */ #define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE * 2) diff -Nru qemu-10.0.12+ds/linux-user/strace.c qemu-10.0.13+ds/linux-user/strace.c --- qemu-10.0.12+ds/linux-user/strace.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/linux-user/strace.c 2026-08-26 22:31:26.000000000 +0300 @@ -4312,7 +4312,7 @@ } #endif -#if defined(TARGET_NR_fsconfig) && defined(NR_fsconfig) +#if defined(TARGET_NR_fsconfig) && defined(__NR_fsconfig) && defined(FSCONFIG_SET_FLAG) static void print_fsconfig_cmd_name(int cmd) { diff -Nru qemu-10.0.12+ds/linux-user/strace.list qemu-10.0.13+ds/linux-user/strace.list --- qemu-10.0.12+ds/linux-user/strace.list 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/linux-user/strace.list 2026-08-26 22:31:26.000000000 +0300 @@ -1725,7 +1725,7 @@ #ifdef TARGET_NR_fsopen { TARGET_NR_fsopen, "fsopen", "%s(%s,%d)", NULL, NULL }, #endif -#if defined(TARGET_NR_fsconfig) && defined(NR_fsconfig) +#if defined(TARGET_NR_fsconfig) && defined(__NR_fsconfig) && defined(FSCONFIG_SET_FLAG) { TARGET_NR_fsconfig, "fsconfig", NULL, print_fsconfig, NULL }, #endif #ifdef TARGET_NR_fsmount @@ -1737,3 +1737,6 @@ #ifdef TARGET_NR_fspick { TARGET_NR_fspick, "fspick", "%s(%d,%s,%d)", NULL, NULL }, #endif +#ifdef TARGET_NR_mount_setattr +{ TARGET_NR_mount_setattr, "mount_setattr", "%s(%d,%s,%d,%p,%d)", NULL, NULL }, +#endif diff -Nru qemu-10.0.12+ds/linux-user/syscall.c qemu-10.0.13+ds/linux-user/syscall.c --- qemu-10.0.12+ds/linux-user/syscall.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/linux-user/syscall.c 2026-08-26 22:31:26.000000000 +0300 @@ -4220,26 +4220,20 @@ } #endif +#define target_time64_t abi_ullong +#define target_swap_time64(x) tswap64(x) + struct target_msqid_ds { struct target_ipc_perm msg_perm; - abi_ulong msg_stime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused1; -#endif - abi_ulong msg_rtime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused2; -#endif - abi_ulong msg_ctime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused3; -#endif + target_time64_t msg_stime; + target_time64_t msg_rtime; + target_time64_t msg_ctime; abi_ulong __msg_cbytes; abi_ulong msg_qnum; abi_ulong msg_qbytes; - abi_ulong msg_lspid; - abi_ulong msg_lrpid; + abi_int msg_lspid; + abi_int msg_lrpid; abi_ulong __unused4; abi_ulong __unused5; }; @@ -4253,14 +4247,14 @@ return -TARGET_EFAULT; if (target_to_host_ipc_perm(&(host_md->msg_perm),target_addr)) return -TARGET_EFAULT; - host_md->msg_stime = tswapal(target_md->msg_stime); - host_md->msg_rtime = tswapal(target_md->msg_rtime); - host_md->msg_ctime = tswapal(target_md->msg_ctime); + host_md->msg_stime = target_swap_time64(target_md->msg_stime); + host_md->msg_rtime = target_swap_time64(target_md->msg_rtime); + host_md->msg_ctime = target_swap_time64(target_md->msg_ctime); host_md->__msg_cbytes = tswapal(target_md->__msg_cbytes); host_md->msg_qnum = tswapal(target_md->msg_qnum); host_md->msg_qbytes = tswapal(target_md->msg_qbytes); - host_md->msg_lspid = tswapal(target_md->msg_lspid); - host_md->msg_lrpid = tswapal(target_md->msg_lrpid); + host_md->msg_lspid = tswap32(target_md->msg_lspid); + host_md->msg_lrpid = tswap32(target_md->msg_lrpid); unlock_user_struct(target_md, target_addr, 0); return 0; } @@ -4274,14 +4268,14 @@ return -TARGET_EFAULT; if (host_to_target_ipc_perm(target_addr,&(host_md->msg_perm))) return -TARGET_EFAULT; - target_md->msg_stime = tswapal(host_md->msg_stime); - target_md->msg_rtime = tswapal(host_md->msg_rtime); - target_md->msg_ctime = tswapal(host_md->msg_ctime); + target_md->msg_stime = target_swap_time64(host_md->msg_stime); + target_md->msg_rtime = target_swap_time64(host_md->msg_rtime); + target_md->msg_ctime = target_swap_time64(host_md->msg_ctime); target_md->__msg_cbytes = tswapal(host_md->__msg_cbytes); target_md->msg_qnum = tswapal(host_md->msg_qnum); target_md->msg_qbytes = tswapal(host_md->msg_qbytes); - target_md->msg_lspid = tswapal(host_md->msg_lspid); - target_md->msg_lrpid = tswapal(host_md->msg_lrpid); + target_md->msg_lspid = tswap32(host_md->msg_lspid); + target_md->msg_lrpid = tswap32(host_md->msg_lrpid); unlock_user_struct(target_md, target_addr, 1); return 0; } @@ -9458,7 +9452,14 @@ int, __to_dfd, const char *, __to_pathname, unsigned int, flag) #endif -#if defined(TARGET_NR_fsopen) && defined(NR_fsopen) +#if defined(TARGET_NR_mount_setattr) && defined(__NR_mount_setattr) +#define __NR_sys_mount_setattr __NR_mount_setattr +_syscall5(int, sys_mount_setattr, int, dfd, const char *, path, + unsigned int, flags, struct mount_attr_ver0 *, uattr, + size_t, usize) +#endif + +#if defined(TARGET_NR_fsopen) && defined(__NR_fsopen) #define __NR_sys_fsopen __NR_fsopen _syscall2(int, sys_fsopen, const char *, fs_name, unsigned int, flags); #define __NR_sys_fsconfig __NR_fsconfig @@ -14184,7 +14185,44 @@ return do_riscv_hwprobe(cpu_env, arg1, arg2, arg3, arg4, arg5); #endif -#if defined(TARGET_NR_fsopen) && defined(NR_fsopen) +#if defined(TARGET_NR_mount_setattr) && defined(__NR_mount_setattr) + case TARGET_NR_mount_setattr: + { + struct mount_attr_ver0 attr = {}; + abi_ulong usize = arg5; + + if (usize < sizeof(struct target_mount_attr_ver0)) { + return -TARGET_EINVAL; + } + ret = copy_struct_from_user(&attr, sizeof(attr), arg4, usize); + if (ret) { + if (ret == -TARGET_E2BIG) { + qemu_log_mask(LOG_UNIMP, + "Unimplemented mount_setattr mount_attr " + "size: " TARGET_ABI_FMT_lu "\n", usize); + } + return ret; + } + /* + * MOUNT_ATTR_* and the MS_* propagation flags have the same + * values on all targets, so only byte order needs fixing up. + */ + attr.attr_set = tswap64(attr.attr_set); + attr.attr_clr = tswap64(attr.attr_clr); + attr.propagation = tswap64(attr.propagation); + attr.userns_fd = tswap64(attr.userns_fd); + + p = lock_user_string(arg2); + if (!p) { + return -TARGET_EFAULT; + } + ret = get_errno(sys_mount_setattr(arg1, p, arg3, &attr, + sizeof(attr))); + unlock_user(p, arg2, 0); + } + return ret; +#endif +#if defined(TARGET_NR_fsopen) && defined(__NR_fsopen) case TARGET_NR_fsopen: { p = lock_user_string(arg1); diff -Nru qemu-10.0.12+ds/linux-user/syscall_defs.h qemu-10.0.13+ds/linux-user/syscall_defs.h --- qemu-10.0.12+ds/linux-user/syscall_defs.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/linux-user/syscall_defs.h 2026-08-26 22:31:26.000000000 +0300 @@ -2591,7 +2591,9 @@ #define FUTEX_PRIVATE_FLAG 128 #define FUTEX_CLOCK_REALTIME 256 +#ifndef FUTEX_CMD_MASK #define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) +#endif #ifdef CONFIG_EPOLL #if defined(TARGET_X86_64) @@ -2765,6 +2767,19 @@ abi_ullong mode; abi_ullong resolve; }; +/* from kernel's include/uapi/linux/mount.h */ +struct mount_attr_ver0 { + uint64_t attr_set; + uint64_t attr_clr; + uint64_t propagation; + uint64_t userns_fd; +}; +struct target_mount_attr_ver0 { + abi_ullong attr_set; + abi_ullong attr_clr; + abi_ullong propagation; + abi_ullong userns_fd; +}; #ifndef RESOLVE_NO_MAGICLINKS #define RESOLVE_NO_MAGICLINKS 0x02 #endif diff -Nru qemu-10.0.12+ds/meson.build qemu-10.0.13+ds/meson.build --- qemu-10.0.12+ds/meson.build 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/meson.build 2026-08-26 22:31:26.000000000 +0300 @@ -612,6 +612,11 @@ qemu_ldflags += cc.get_supported_link_arguments('-Wl,--no-seh', '-Wl,--nxcompat') qemu_ldflags += cc.get_supported_link_arguments('-Wl,--dynamicbase', '-Wl,--high-entropy-va') endif +if host_os == 'linux' and get_option('werror') and get_option('prefer_static') + # On glibc systems, glib causes warnings about getpwuid, getpwuid_r + # and getpwnam_r not being supported with static linking + qemu_ldflags += cc.get_supported_link_arguments('-Wl,--no-fatal-warnings') +endif if get_option('fuzzing') # Specify a filter to only instrument code that is directly related to diff -Nru qemu-10.0.12+ds/migration/multifd-qatzip.c qemu-10.0.13+ds/migration/multifd-qatzip.c --- qemu-10.0.12+ds/migration/multifd-qatzip.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/migration/multifd-qatzip.c 2026-08-26 22:31:26.000000000 +0300 @@ -348,7 +348,10 @@ multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } diff -Nru qemu-10.0.12+ds/migration/multifd-qpl.c qemu-10.0.13+ds/migration/multifd-qpl.c --- qemu-10.0.12+ds/migration/multifd-qpl.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/migration/multifd-qpl.c 2026-08-26 22:31:26.000000000 +0300 @@ -664,26 +664,42 @@ } multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } /* read compressed page lengths */ len = p->normal_num * sizeof(uint32_t); - assert(len < in_size); + if (len >= in_size) { + error_setg(errp, "multifd %u: header len %"PRIu32 + " >= packet size %"PRIu32, p->id, len, in_size); + return -1; + } ret = qio_channel_read_all(p->c, (void *) qpl->zlen, len, errp); if (ret != 0) { return ret; } for (int i = 0; i < p->normal_num; i++) { qpl->zlen[i] = be32_to_cpu(qpl->zlen[i]); - assert(qpl->zlen[i] <= multifd_ram_page_size()); + if (qpl->zlen[i] > multifd_ram_page_size()) { + error_setg(errp, "multifd %u: page %d compressed len %" + PRIu32" too large", p->id, i, qpl->zlen[i]); + return -1; + } zbuf_len += qpl->zlen[i]; ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); } /* read compressed pages */ - assert(in_size == len + zbuf_len); + if (in_size != len + zbuf_len) { + error_setg(errp, "multifd %u: packet size %"PRIu32 + " != header %"PRIu32" + data %"PRIu32, + p->id, in_size, len, zbuf_len); + return -1; + } ret = qio_channel_read_all(p->c, (void *) qpl->zbuf, zbuf_len, errp); if (ret != 0) { return ret; diff -Nru qemu-10.0.12+ds/migration/multifd-uadk.c qemu-10.0.13+ds/migration/multifd-uadk.c --- qemu-10.0.12+ds/migration/multifd-uadk.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/migration/multifd-uadk.c 2026-08-26 22:31:26.000000000 +0300 @@ -245,12 +245,19 @@ multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } /* read compressed data lengths */ - assert(hdr_len < in_size); + if (hdr_len >= in_size) { + error_setg(errp, "multifd %u: header len %"PRIu32 + " >= packet size %"PRIu32, p->id, hdr_len, in_size); + return -1; + } ret = qio_channel_read_all(p->c, (void *) uadk_data->buf_hdr, hdr_len, errp); if (ret != 0) { @@ -259,12 +266,21 @@ for (int i = 0; i < p->normal_num; i++) { uadk_data->buf_hdr[i] = be32_to_cpu(uadk_data->buf_hdr[i]); + if (uadk_data->buf_hdr[i] > page_size) { + error_setg(errp, "multifd %u: page %d compressed len %"PRIu32 + " too large", p->id, i, uadk_data->buf_hdr[i]); + return -1; + } data_len += uadk_data->buf_hdr[i]; - assert(uadk_data->buf_hdr[i] <= page_size); } /* read compressed data */ - assert(in_size == hdr_len + data_len); + if (in_size != hdr_len + data_len) { + error_setg(errp, "multifd %u: packet size %"PRIu32 + " != header %"PRIu32" + data %"PRIu32, + p->id, in_size, hdr_len, data_len); + return -1; + } ret = qio_channel_read_all(p->c, (void *)buf, data_len, errp); if (ret != 0) { return ret; diff -Nru qemu-10.0.12+ds/migration/multifd-zlib.c qemu-10.0.13+ds/migration/multifd-zlib.c --- qemu-10.0.12+ds/migration/multifd-zlib.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/migration/multifd-zlib.c 2026-08-26 22:31:26.000000000 +0300 @@ -216,10 +216,19 @@ return -1; } + if (in_size > z->zbuff_len) { + error_setg(errp, "multifd %u: next_packet_size %"PRIu32 + " exceeds allocated %"PRIu32, p->id, in_size, z->zbuff_len); + return -1; + } + multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } diff -Nru qemu-10.0.12+ds/migration/multifd-zstd.c qemu-10.0.13+ds/migration/multifd-zstd.c --- qemu-10.0.12+ds/migration/multifd-zstd.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/migration/multifd-zstd.c 2026-08-26 22:31:26.000000000 +0300 @@ -210,10 +210,19 @@ return -1; } + if (in_size > z->zbuff_len) { + error_setg(errp, "multifd %u: next_packet_size %"PRIu32 + " exceeds allocated %"PRIu32, p->id, in_size, z->zbuff_len); + return -1; + } + multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } diff -Nru qemu-10.0.12+ds/net/filter-rewriter.c qemu-10.0.13+ds/net/filter-rewriter.c --- qemu-10.0.12+ds/net/filter-rewriter.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/net/filter-rewriter.c 2026-08-26 22:31:26.000000000 +0300 @@ -373,7 +373,7 @@ g_free(s->incoming_queue); } - g_hash_table_destroy(s->connection_track_table); + g_clear_pointer(&s->connection_track_table, g_hash_table_destroy); } static void colo_rewriter_setup(NetFilterState *nf, Error **errp) diff -Nru qemu-10.0.12+ds/pc-bios/s390-ccw/bootmap.c qemu-10.0.13+ds/pc-bios/s390-ccw/bootmap.c --- qemu-10.0.12+ds/pc-bios/s390-ccw/bootmap.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/pc-bios/s390-ccw/bootmap.c 2026-08-26 22:31:26.000000000 +0300 @@ -61,6 +61,7 @@ static void *s2_prev_blk = _s2; static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE; static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2; +static void *s2_end = _s2 + sizeof(_s2); static inline int verify_boot_info(BootInfo *bip) { @@ -308,7 +309,8 @@ } } - return menu_get_zipl_boot_index(s2_cur_blk + banner_offset); + return menu_get_zipl_boot_index(s2_cur_blk + banner_offset, + s2_end); } prev_block_nr = cur_block_nr; @@ -902,7 +904,7 @@ if (dir_rem[level] == 0) { /* Nothing remaining */ level--; - if (virtio_read(sec_loc[level], temp)) { + if (level >= 0 && virtio_read(sec_loc[level], temp)) { puts("Failed to read ISO directory"); return -EIO; } diff -Nru qemu-10.0.12+ds/pc-bios/s390-ccw/helper.h qemu-10.0.13+ds/pc-bios/s390-ccw/helper.h --- qemu-10.0.12+ds/pc-bios/s390-ccw/helper.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/pc-bios/s390-ccw/helper.h 2026-08-26 22:31:26.000000000 +0300 @@ -45,4 +45,14 @@ } } +static inline size_t strnlen(const char *s, size_t maxlen) +{ + size_t len = 0; + + while (len < maxlen && s[len]) { + len++; + } + return len; +} + #endif diff -Nru qemu-10.0.12+ds/pc-bios/s390-ccw/menu.c qemu-10.0.13+ds/pc-bios/s390-ccw/menu.c --- qemu-10.0.12+ds/pc-bios/s390-ccw/menu.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/pc-bios/s390-ccw/menu.c 2026-08-26 22:31:26.000000000 +0300 @@ -16,6 +16,7 @@ #include "s390-ccw.h" #include "sclp.h" #include "s390-time.h" +#include "helper.h" #define KEYCODE_NO_INP '\0' #define KEYCODE_ESCAPE '\033' @@ -26,6 +27,9 @@ #define ZIPL_TIMEOUT_OFFSET 138 #define ZIPL_FLAG_OFFSET 140 +/* Max printable chars for a zipl boot menu entry */ +#define ZIPL_ENTRY_MAX 80 + #define TOD_CLOCK_MILLISECOND 0x3e8000 #define LOW_CORE_EXTERNAL_INT_ADDR 0x86 @@ -176,21 +180,31 @@ return boot_index; } -/* Returns the entry number that was printed */ +/* Returns the entry number that was printed, or -1 on invalid entry */ static int zipl_print_entry(const char *data, size_t len) { - char buf[len + 2]; + char buf[ZIPL_ENTRY_MAX + 2]; + const char *p; + + if (len > ZIPL_ENTRY_MAX) { + len = ZIPL_ENTRY_MAX; + } ebcdic_to_ascii(data, buf, len); buf[len] = '\n'; buf[len + 1] = '\0'; + p = (buf[0] == ' ') ? buf + 1 : buf; + if (!isdigit((unsigned char)*p)) { + return -1; + } + printf("%s", buf); - return buf[0] == ' ' ? atoi(buf + 1) : atoi(buf); + return atoi(p); } -int menu_get_zipl_boot_index(const char *menu_data) +int menu_get_zipl_boot_index(const char *menu_data, const char *menu_data_end) { size_t len; int entry; @@ -206,16 +220,28 @@ timeout = zipl_timeout * 1000; } - /* Print banner */ + if (menu_data >= menu_data_end) { + return 0; /* Boot default */ + } + + /* Skip banner */ + len = strnlen(menu_data, menu_data_end - menu_data); + menu_data += len + 1; + if (menu_data >= menu_data_end || !(*menu_data)) { + return 0; /* No entries, boot default */ + } + puts("s390-ccw zIPL Boot Menu\n"); - menu_data += strlen(menu_data) + 1; /* Print entries */ - while (*menu_data) { - len = strlen(menu_data); + while (menu_data < menu_data_end && *menu_data) { + len = strnlen(menu_data, menu_data_end - menu_data); entry = zipl_print_entry(menu_data, len); menu_data += len + 1; + if (entry < 0 || entry >= MAX_BOOT_ENTRIES) { + continue; + } valid_entries[entry] = true; if (entry == 0) { diff -Nru qemu-10.0.12+ds/pc-bios/s390-ccw/s390-ccw.h qemu-10.0.13+ds/pc-bios/s390-ccw/s390-ccw.h --- qemu-10.0.12+ds/pc-bios/s390-ccw/s390-ccw.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/pc-bios/s390-ccw/s390-ccw.h 2026-08-26 22:31:26.000000000 +0300 @@ -83,7 +83,7 @@ /* menu.c */ void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout); -int menu_get_zipl_boot_index(const char *menu_data); +int menu_get_zipl_boot_index(const char *menu_data, const char *menu_data_end); bool menu_is_enabled_zipl(void); int menu_get_enum_boot_index(bool *valid_entries); bool menu_is_enabled_enum(void); diff -Nru qemu-10.0.12+ds/subprojects/libvduse/libvduse.c qemu-10.0.13+ds/subprojects/libvduse/libvduse.c --- qemu-10.0.12+ds/subprojects/libvduse/libvduse.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/subprojects/libvduse/libvduse.c 2026-08-26 22:31:26.000000000 +0300 @@ -902,6 +902,11 @@ return; } + if (vq_info.num > VIRTQUEUE_MAX_SIZE) { + fprintf(stderr, "vq[%d] vring num %u exceeds max %u\n", + vq->index, vq_info.num, VIRTQUEUE_MAX_SIZE); + return; + } vq->vring.num = vq_info.num; vq->vring.desc_addr = vq_info.desc_addr; vq->vring.avail_addr = vq_info.driver_addr; diff -Nru qemu-10.0.12+ds/subprojects/libvhost-user/libvhost-user.c qemu-10.0.13+ds/subprojects/libvhost-user/libvhost-user.c --- qemu-10.0.12+ds/subprojects/libvhost-user/libvhost-user.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/subprojects/libvhost-user/libvhost-user.c 2026-08-26 22:31:26.000000000 +0300 @@ -1200,6 +1200,12 @@ DPRINT("State.index: %u\n", index); DPRINT("State.num: %u\n", num); + + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid vring_num index: %u", index); + return false; + } + dev->vq[index].vring.num = num; return false; @@ -1210,7 +1216,7 @@ { struct vhost_vring_addr addr = vmsg->payload.addr, *vra = &addr; unsigned int index = vra->index; - VuVirtq *vq = &dev->vq[index]; + VuVirtq *vq; DPRINT("vhost_vring_addr:\n"); DPRINT(" index: %d\n", vra->index); @@ -1220,6 +1226,12 @@ DPRINT(" avail_user_addr: 0x%016" PRIx64 "\n", (uint64_t)vra->avail_user_addr); DPRINT(" log_guest_addr: 0x%016" PRIx64 "\n", (uint64_t)vra->log_guest_addr); + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid vring_addr index: %u", index); + return false; + } + + vq = &dev->vq[index]; vq->vra = *vra; vq->vring.flags = vra->flags; vq->vring.log_guest_addr = vra->log_guest_addr; @@ -1256,6 +1268,12 @@ DPRINT("State.index: %u\n", index); DPRINT("State.num: %u\n", num); + + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid vring_base index: %u", index); + return false; + } + dev->vq[index].shadow_avail_idx = dev->vq[index].last_avail_idx = num; return false; @@ -1267,6 +1285,14 @@ unsigned int index = vmsg->payload.state.index; DPRINT("State.index: %u\n", index); + + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid vring_base index: %u", index); + vmsg->payload.state.num = 0; + vmsg->size = sizeof(vmsg->payload.state); + return true; + } + vmsg->payload.state.num = dev->vq[index].last_avail_idx; vmsg->size = sizeof(vmsg->payload.state); @@ -1353,6 +1379,12 @@ vq->counter = 0; if (unlikely(vq->inflight->used_idx != vq->used_idx)) { + if (vq->inflight->last_batch_head >= vq->inflight->desc_num) { + vu_panic(dev, "vu_check_queue_inflights: last_batch_head %u " + "out of range (desc_num %u)", + vq->inflight->last_batch_head, vq->inflight->desc_num); + return -1; + } vq->inflight->desc[vq->inflight->last_batch_head].inflight = 0; barrier(); @@ -1376,6 +1408,13 @@ for (i = 0; i < vq->inflight->desc_num; i++) { if (vq->inflight->desc[i].inflight) { + /* + * We earlier counted exactly vq->inuse in flight - + * what is going on? + */ + if (vq->resubmit_num >= vq->inuse) { + return -1; + } vq->resubmit_list[vq->resubmit_num].index = i; vq->resubmit_list[vq->resubmit_num].counter = vq->inflight->desc[i].counter; @@ -1919,6 +1958,8 @@ if (vmsg->size != sizeof(vmsg->payload.inflight)) { vu_panic(dev, "Invalid get_inflight_fd message:%d", vmsg->size); + vmsg_close_fds(vmsg); + vmsg->fd_num = 0; vmsg->payload.inflight.mmap_size = 0; return true; } @@ -1926,6 +1967,15 @@ num_queues = vmsg->payload.inflight.num_queues; queue_size = vmsg->payload.inflight.queue_size; + if (num_queues > dev->max_queues) { + vu_panic(dev, "Invalid get_inflight_fd num_queues: %"PRId16, + num_queues); + vmsg_close_fds(vmsg); + vmsg->fd_num = 0; + vmsg->payload.inflight.mmap_size = 0; + return true; + } + DPRINT("set_inflight_fd num_queues: %"PRId16"\n", num_queues); DPRINT("set_inflight_fd queue_size: %"PRId16"\n", queue_size); @@ -1973,6 +2023,7 @@ vmsg->size != sizeof(vmsg->payload.inflight)) { vu_panic(dev, "Invalid set_inflight_fd message size:%d fds:%d", vmsg->size, vmsg->fd_num); + vmsg_close_fds(vmsg); return false; } @@ -1982,6 +2033,13 @@ num_queues = vmsg->payload.inflight.num_queues; queue_size = vmsg->payload.inflight.queue_size; + if (num_queues > dev->max_queues) { + vu_panic(dev, "Invalid set_inflight_fd num_queues: %"PRId16, + num_queues); + close(fd); + return false; + } + DPRINT("set_inflight_fd mmap_size: %"PRId64"\n", mmap_size); DPRINT("set_inflight_fd mmap_offset: %"PRId64"\n", mmap_offset); DPRINT("set_inflight_fd num_queues: %"PRId16"\n", num_queues); @@ -1992,6 +2050,7 @@ if (rc == MAP_FAILED) { vu_panic(dev, "set_inflight_fd mmap error: %s", strerror(errno)); + close(fd); return false; } diff -Nru qemu-10.0.12+ds/target/i386/hvf/x86_decode.c qemu-10.0.13+ds/target/i386/hvf/x86_decode.c --- qemu-10.0.12+ds/target/i386/hvf/x86_decode.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/i386/hvf/x86_decode.c 2026-08-26 22:31:26.000000000 +0300 @@ -1867,6 +1867,12 @@ case PREFIX_SS_SEG_OVERRIDE: case PREFIX_DS_SEG_OVERRIDE: case PREFIX_ES_SEG_OVERRIDE: + if (x86_is_long_mode(env_cpu(env))) { + /* ES/CS/SS/DS segment overrides are ignored in long mode */ + decode->rex.rex = 0; + break; + } + /* fall through when not in long mode */ case PREFIX_FS_SEG_OVERRIDE: case PREFIX_GS_SEG_OVERRIDE: decode->segment_override = byte; diff -Nru qemu-10.0.12+ds/target/i386/ops_sse.h qemu-10.0.13+ds/target/i386/ops_sse.h --- qemu-10.0.12+ds/target/i386/ops_sse.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/i386/ops_sse.h 2026-08-26 22:31:26.000000000 +0300 @@ -842,7 +842,7 @@ void glue(helper_rsqrtps, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); int i; for (i = 0; i < 2 << SHIFT; i++) { d->ZMM_S(i) = float32_div(float32_one, @@ -855,7 +855,7 @@ #if SHIFT == 1 void helper_rsqrtss(CPUX86State *env, ZMMReg *d, ZMMReg *v, ZMMReg *s) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); int i; d->ZMM_S(0) = float32_div(float32_one, float32_sqrt(s->ZMM_S(0), &env->sse_status), @@ -869,7 +869,7 @@ void glue(helper_rcpps, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); int i; for (i = 0; i < 2 << SHIFT; i++) { d->ZMM_S(i) = float32_div(float32_one, s->ZMM_S(i), &env->sse_status); @@ -880,7 +880,7 @@ #if SHIFT == 1 void helper_rcpss(CPUX86State *env, ZMMReg *d, ZMMReg *v, ZMMReg *s) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); int i; d->ZMM_S(0) = float32_div(float32_one, s->ZMM_S(0), &env->sse_status); for (i = 1; i < 2 << SHIFT; i++) { @@ -1714,7 +1714,7 @@ void glue(helper_roundps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mode) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; int i; @@ -1738,7 +1738,7 @@ void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mode) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; int i; @@ -1763,7 +1763,7 @@ void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, uint32_t mode) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; int i; @@ -1788,7 +1788,7 @@ void glue(helper_roundsd, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, uint32_t mode) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; int i; diff -Nru qemu-10.0.12+ds/target/i386/sev.c qemu-10.0.13+ds/target/i386/sev.c --- qemu-10.0.12+ds/target/i386/sev.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/i386/sev.c 2026-08-26 22:31:26.000000000 +0300 @@ -1269,7 +1269,7 @@ OvmfSevMetadataDesc *desc; int type, ret, i; void *hva; - MemoryRegion *mr = NULL; + g_autoptr(MemoryRegion) mr = NULL; for (i = 0; i < metadata->num_desc; i++) { desc = &metadata->descs[i]; @@ -1641,7 +1641,7 @@ int error, ret = 1; void *hva; gsize hdr_sz = 0, data_sz = 0; - MemoryRegion *mr = NULL; + g_autoptr(MemoryRegion) mr = NULL; SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); if (!sev_common) { diff -Nru qemu-10.0.12+ds/target/i386/tcg/decode-new.c.inc qemu-10.0.13+ds/target/i386/tcg/decode-new.c.inc --- qemu-10.0.12+ds/target/i386/tcg/decode-new.c.inc 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/i386/tcg/decode-new.c.inc 2026-08-26 22:31:26.000000000 +0300 @@ -479,23 +479,33 @@ static void decode_0F78(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) { - static const X86OpEntry opcodes_0F78[4] = { - {}, - X86_OP_ENTRY3(EXTRQ_i, V,x, None,None, I,w, cpuid(SSE4A)), /* AMD extension */ - {}, - X86_OP_ENTRY3(INSERTQ_i, V,x, U,x, I,w, cpuid(SSE4A)), /* AMD extension */ - }; - *entry = *decode_by_prefix(s, opcodes_0F78); + static const X86OpEntry opcodes_0F78_f2 = + X86_OP_ENTRY3(INSERTQ_i, V,x, U,x, I,w, cpuid(SSE4A)); /* AMD extension */ + static const X86OpEntry opcodes_0F78_66 = + X86_OP_ENTRY3(EXTRQ_i, U,x, None,None, I,w, cpuid(SSE4A)); /* AMD extension */ + + entry->gen = NULL; + if (s->prefix & PREFIX_REPNZ) { + *entry = opcodes_0F78_f2; + } else if (s->prefix & PREFIX_REPZ) { + /* undefined */ + } else if (s->prefix & PREFIX_DATA) { + int op = (get_modrm(s, env) >> 3) & 7; + if (op == 0) { + *entry = opcodes_0F78_66; + } + } } static void decode_0F79(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) { + entry->gen = NULL; if (s->prefix & PREFIX_REPNZ) { entry->gen = gen_INSERTQ_r; /* AMD extension */ + } else if (s->prefix & PREFIX_REPZ) { + /* undefined */ } else if (s->prefix & PREFIX_DATA) { entry->gen = gen_EXTRQ_r; /* AMD extension */ - } else { - entry->gen = NULL; }; } @@ -1510,6 +1520,7 @@ static const X86OpEntry opcodes_grp3[16] = { /* 0xf6 */ [0x00] = X86_OP_ENTRYrr(AND, E,b, I,b), + [0x01] = X86_OP_ENTRYrr(AND, E,b, I,b), [0x02] = X86_OP_ENTRY1(NOT, E,b, lock), [0x03] = X86_OP_ENTRY1(NEG, E,b, lock), [0x04] = X86_OP_ENTRYrr(MUL, E,b, 0,b, zextT0), @@ -1519,6 +1530,7 @@ /* 0xf7 */ [0x08] = X86_OP_ENTRYrr(AND, E,v, I,z), + [0x09] = X86_OP_ENTRYrr(AND, E,v, I,z), [0x0a] = X86_OP_ENTRY1(NOT, E,v, lock), [0x0b] = X86_OP_ENTRY1(NEG, E,v, lock), [0x0c] = X86_OP_ENTRYrr(MUL, E,v, 0,v, zextT0), @@ -2573,16 +2585,24 @@ s->prefix |= PREFIX_LOCK; goto next_byte; case 0x2e: - s->override = R_CS; + if (!CODE64(s)) { + s->override = R_CS; + } goto next_byte; case 0x36: - s->override = R_SS; + if (!CODE64(s)) { + s->override = R_SS; + } goto next_byte; case 0x3e: - s->override = R_DS; + if (!CODE64(s)) { + s->override = R_DS; + } goto next_byte; case 0x26: - s->override = R_ES; + if (!CODE64(s)) { + s->override = R_ES; + } goto next_byte; case 0x64: s->override = R_FS; diff -Nru qemu-10.0.12+ds/target/i386/tcg/fpu_helper.c qemu-10.0.13+ds/target/i386/tcg/fpu_helper.c --- qemu-10.0.12+ds/target/i386/tcg/fpu_helper.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/i386/tcg/fpu_helper.c 2026-08-26 22:31:26.000000000 +0300 @@ -198,16 +198,16 @@ set_float_ftz_detection(float_ftz_before_rounding, &env->sse_status); } -static inline uint8_t save_exception_flags(CPUX86State *env) +static inline int save_exception_flags(CPUX86State *env) { - uint8_t old_flags = get_float_exception_flags(&env->fp_status); + int old_flags = get_float_exception_flags(&env->fp_status); set_float_exception_flags(0, &env->fp_status); return old_flags; } -static void merge_exception_flags(CPUX86State *env, uint8_t old_flags) +static void merge_exception_flags(CPUX86State *env, int old_flags) { - uint8_t new_flags = get_float_exception_flags(&env->fp_status); + int new_flags = get_float_exception_flags(&env->fp_status); float_raise(old_flags, &env->fp_status); fpu_set_exception(env, ((new_flags & float_flag_invalid ? FPUS_IE : 0) | @@ -220,7 +220,7 @@ static inline floatx80 helper_fdiv(CPUX86State *env, floatx80 a, floatx80 b) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); floatx80 ret = floatx80_div(a, b, &env->fp_status); merge_exception_flags(env, old_flags); return ret; @@ -240,7 +240,7 @@ void helper_flds_FT0(CPUX86State *env, uint32_t val) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); union { float32 f; uint32_t i; @@ -253,7 +253,7 @@ void helper_fldl_FT0(CPUX86State *env, uint64_t val) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); union { float64 f; uint64_t i; @@ -271,7 +271,7 @@ void helper_flds_ST0(CPUX86State *env, uint32_t val) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int new_fpstt; union { float32 f; @@ -288,7 +288,7 @@ void helper_fldl_ST0(CPUX86State *env, uint64_t val) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int new_fpstt; union { float64 f; @@ -338,7 +338,7 @@ uint32_t helper_fsts_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); union { float32 f; uint32_t i; @@ -351,7 +351,7 @@ uint64_t helper_fstl_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); union { float64 f; uint64_t i; @@ -364,7 +364,7 @@ int32_t helper_fist_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int32_t val; val = floatx80_to_int32(ST0, &env->fp_status); @@ -378,7 +378,7 @@ int32_t helper_fistl_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int32_t val; val = floatx80_to_int32(ST0, &env->fp_status); @@ -391,7 +391,7 @@ int64_t helper_fistll_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int64_t val; val = floatx80_to_int64(ST0, &env->fp_status); @@ -404,7 +404,7 @@ int32_t helper_fistt_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int32_t val; val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); @@ -418,7 +418,7 @@ int32_t helper_fisttl_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int32_t val; val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); @@ -431,7 +431,7 @@ int64_t helper_fisttll_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int64_t val; val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status); @@ -527,7 +527,7 @@ void helper_fcom_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); FloatRelation ret; ret = floatx80_compare(ST0, FT0, &env->fp_status); @@ -537,7 +537,7 @@ void helper_fucom_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); FloatRelation ret; ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); @@ -549,54 +549,52 @@ void helper_fcomi_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); - int eflags; + int old_flags = save_exception_flags(env); FloatRelation ret; ret = floatx80_compare(ST0, FT0, &env->fp_status); - eflags = cpu_cc_compute_all(env) & ~(CC_Z | CC_P | CC_C); - CC_SRC = eflags | fcomi_ccval[ret + 1]; + /* OF, SF, and AF are unconditionally cleared to 0 */ + CC_SRC = fcomi_ccval[ret + 1]; CC_OP = CC_OP_EFLAGS; merge_exception_flags(env, old_flags); } void helper_fucomi_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); - int eflags; + int old_flags = save_exception_flags(env); FloatRelation ret; ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); - eflags = cpu_cc_compute_all(env) & ~(CC_Z | CC_P | CC_C); - CC_SRC = eflags | fcomi_ccval[ret + 1]; + /* OF, SF, and AF are unconditionally cleared to 0 */ + CC_SRC = fcomi_ccval[ret + 1]; CC_OP = CC_OP_EFLAGS; merge_exception_flags(env, old_flags); } void helper_fadd_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_add(ST0, FT0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fmul_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_mul(ST0, FT0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fsub_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_sub(ST0, FT0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fsubr_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_sub(FT0, ST0, &env->fp_status); merge_exception_flags(env, old_flags); } @@ -615,28 +613,28 @@ void helper_fadd_STN_ST0(CPUX86State *env, int st_index) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fmul_STN_ST0(CPUX86State *env, int st_index) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fsub_STN_ST0(CPUX86State *env, int st_index) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fsubr_STN_ST0(CPUX86State *env, int st_index) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST(st_index) = floatx80_sub(ST0, ST(st_index), &env->fp_status); merge_exception_flags(env, old_flags); } @@ -861,7 +859,7 @@ void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int v; target_ulong mem_ref, mem_end; int64_t val; @@ -1136,7 +1134,7 @@ void helper_f2xm1(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t sig = extractFloatx80Frac(ST0); int32_t exp = extractFloatx80Exp(ST0); bool sign = extractFloatx80Sign(ST0); @@ -1369,7 +1367,7 @@ void helper_fpatan(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t arg0_sig = extractFloatx80Frac(ST0); int32_t arg0_exp = extractFloatx80Exp(ST0); bool arg0_sign = extractFloatx80Sign(ST0); @@ -1808,7 +1806,7 @@ void helper_fxtract(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); CPU_LDoubleU temp; temp.d = ST0; @@ -1857,7 +1855,7 @@ static void helper_fprem_common(CPUX86State *env, bool mod) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t quotient; CPU_LDoubleU temp0, temp1; int exp0, exp1, expdiff; @@ -2053,7 +2051,7 @@ void helper_fyl2xp1(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t arg0_sig = extractFloatx80Frac(ST0); int32_t arg0_exp = extractFloatx80Exp(ST0); bool arg0_sign = extractFloatx80Sign(ST0); @@ -2151,7 +2149,7 @@ void helper_fyl2x(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t arg0_sig = extractFloatx80Frac(ST0); int32_t arg0_exp = extractFloatx80Exp(ST0); bool arg0_sign = extractFloatx80Sign(ST0); @@ -2298,7 +2296,7 @@ void helper_fsqrt(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); if (floatx80_is_neg(ST0)) { env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ env->fpus |= 0x400; @@ -2324,14 +2322,14 @@ void helper_frndint(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_round_to_int(ST0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fscale(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); if (floatx80_invalid_encoding(ST1, &env->fp_status) || floatx80_invalid_encoding(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); @@ -2369,7 +2367,7 @@ } else { int n; FloatX80RoundPrec save = env->fp_status.floatx80_rounding_precision; - uint8_t save_flags = get_float_exception_flags(&env->fp_status); + int save_flags = get_float_exception_flags(&env->fp_status); set_float_exception_flags(0, &env->fp_status); n = floatx80_to_int32_round_to_zero(ST1, &env->fp_status); set_float_exception_flags(save_flags, &env->fp_status); @@ -3269,7 +3267,7 @@ void update_mxcsr_from_sse_status(CPUX86State *env) { - uint8_t flags = get_float_exception_flags(&env->sse_status); + int flags = get_float_exception_flags(&env->sse_status); /* * The MXCSR denormal flag has opposite semantics to * float_flag_input_denormal_flushed (the softfloat code sets that flag diff -Nru qemu-10.0.12+ds/target/i386/tcg/seg_helper.c qemu-10.0.13+ds/target/i386/tcg/seg_helper.c --- qemu-10.0.12+ds/target/i386/tcg/seg_helper.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/i386/tcg/seg_helper.c 2026-08-26 22:31:26.000000000 +0300 @@ -1070,17 +1070,22 @@ selector = (env->star >> 48) & 0xffff; #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { - cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK - | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | - NT_MASK); if (dflag == 2) { + uint64_t new_rip = env->regs[R_ECX]; + if (IS_INTEL_CPU(env)) { + int shift = (get_pg_mode(env) & PG_MODE_LA57) ? 56 : 47; + int64_t sext = (int64_t)new_rip >> shift; + if (sext != 0 && sext != -1) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + } cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3, 0, 0xffffffff, DESC_G_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK); - env->eip = env->regs[R_ECX]; + env->eip = new_rip; } else { cpu_x86_load_seg_cache(env, R_CS, selector | 3, 0, 0xffffffff, @@ -1094,6 +1099,10 @@ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | DESC_W_MASK | DESC_A_MASK); + + cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK + | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | + NT_MASK); } else #endif { @@ -2037,7 +2046,8 @@ new_cs = popl(&sa) & 0xffff; if (is_iret) { new_eflags = popl(&sa); - if (new_eflags & VM_MASK) { + bool allow_vm86 = (cpl == 0) && !(env->hflags & HF_LMA_MASK); + if ((new_eflags & VM_MASK) && allow_vm86) { goto return_to_vm86; } } diff -Nru qemu-10.0.12+ds/target/loongarch/tcg/insn_trans/trans_branch.c.inc qemu-10.0.13+ds/target/loongarch/tcg/insn_trans/trans_branch.c.inc --- qemu-10.0.12+ds/target/loongarch/tcg/insn_trans/trans_branch.c.inc 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/loongarch/tcg/insn_trans/trans_branch.c.inc 2026-08-26 22:31:26.000000000 +0300 @@ -66,6 +66,8 @@ TCGv src1 = tcg_temp_new(); TCGv src2 = tcg_constant_tl(0); + CHECK_FPE; + tcg_gen_ld8u_tl(src1, tcg_env, offsetof(CPULoongArchState, cf[a->cj])); gen_bc(ctx, src1, src2, a->offs, cond); diff -Nru qemu-10.0.12+ds/target/riscv/cpu.c qemu-10.0.13+ds/target/riscv/cpu.c --- qemu-10.0.12+ds/target/riscv/cpu.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/riscv/cpu.c 2026-08-26 22:31:26.000000000 +0300 @@ -1116,6 +1116,14 @@ #else env->priv = PRV_U; env->senvcfg = 0; + /* + * Match the user-mode view of a typical firmware/kernel setup where + * cbo.zero is enabled for user mode; the CBCFE/CBIE bits stay zero, + * so the cache-management operations remain illegal in user mode. + */ + if (riscv_cpu_cfg(env)->ext_zicboz) { + env->senvcfg |= SENVCFG_CBZE; + } env->menvcfg = 0; #endif diff -Nru qemu-10.0.12+ds/target/riscv/csr.c qemu-10.0.13+ds/target/riscv/csr.c --- qemu-10.0.12+ds/target/riscv/csr.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/riscv/csr.c 2026-08-26 22:31:26.000000000 +0300 @@ -2037,6 +2037,11 @@ } if (xl != MXL_RV32 || env->debugger) { + if ((val & MSTATUS64_SXL) != 0) { + mask |= MSTATUS64_SXL; + val = riscv_write_uxl(env, val, MSTATUS64_SXL); + } + if ((val & MSTATUS64_UXL) != 0) { mask |= MSTATUS64_UXL; val = riscv_write_uxl(env, val, MSTATUS64_UXL); @@ -3191,6 +3196,19 @@ uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE | MENVCFG_CDE; + /* + * menvcfg.LPE (Zicfilp) and menvcfg.SSE (Zicfiss) reside in the low + * 32 bits and are defined for both RV32 and RV64, so they must be + * writable regardless of MXLEN. + */ + if (cfg->ext_zicfilp) { + mask |= MENVCFG_LPE; + } + + if (cfg->ext_zicfiss) { + mask |= MENVCFG_SSE; + } + if (riscv_cpu_mxl(env) == MXL_RV64) { mask |= (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | (cfg->ext_sstc ? MENVCFG_STCE : 0) | @@ -3198,14 +3216,6 @@ (cfg->ext_svadu ? MENVCFG_ADUE : 0) | (cfg->ext_ssdbltrp ? MENVCFG_DTE : 0); - if (env_archcpu(env)->cfg.ext_zicfilp) { - mask |= MENVCFG_LPE; - } - - if (env_archcpu(env)->cfg.ext_zicfiss) { - mask |= MENVCFG_SSE; - } - /* Update PMM field only if the value is valid according to Zjpm v1.0 */ if (env_archcpu(env)->cfg.ext_smnpm && get_field(val, MENVCFG_PMM) != PMM_FIELD_RESERVED) { @@ -3326,6 +3336,7 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, target_ulong val) { + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE; RISCVException ret; @@ -3334,20 +3345,24 @@ return ret; } + /* + * henvcfg.LPE (Zicfilp) and henvcfg.SSE (Zicfiss) reside in the low + * 32 bits and are defined for both RV32 and RV64, so they must be + * writable regardless of MXLEN. + */ + if (cfg->ext_zicfilp) { + mask |= HENVCFG_LPE; + } + + /* H can light up SSE for VS only if HS had it from menvcfg */ + if (cfg->ext_zicfiss && get_field(env->menvcfg, MENVCFG_SSE)) { + mask |= HENVCFG_SSE; + } + if (riscv_cpu_mxl(env) == MXL_RV64) { mask |= env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE | HENVCFG_DTE); - if (env_archcpu(env)->cfg.ext_zicfilp) { - mask |= HENVCFG_LPE; - } - - /* H can light up SSE for VS only if HS had it from menvcfg */ - if (env_archcpu(env)->cfg.ext_zicfiss && - get_field(env->menvcfg, MENVCFG_SSE)) { - mask |= HENVCFG_SSE; - } - /* Update PMM field only if the value is valid according to Zjpm v1.0 */ if (env_archcpu(env)->cfg.ext_ssnpm && get_field(val, HENVCFG_PMM) != PMM_FIELD_RESERVED) { @@ -3897,8 +3912,8 @@ if (riscv_cpu_cfg(env)->ext_ssdbltrp) { mask |= SSTATUS_SDT; } - /* TODO: Use SXL not MXL. */ - *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask); + + *val = add_status_sd(riscv_cpu_sxl(env), env->mstatus & mask); return RISCV_EXCP_NONE; } diff -Nru qemu-10.0.12+ds/target/riscv/insn_trans/trans_rvf.c.inc qemu-10.0.13+ds/target/riscv/insn_trans/trans_rvf.c.inc --- qemu-10.0.12+ds/target/riscv/insn_trans/trans_rvf.c.inc 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/riscv/insn_trans/trans_rvf.c.inc 2026-08-26 22:31:26.000000000 +0300 @@ -426,7 +426,8 @@ { /* NOTE: This was FMV.X.S in an earlier version of the ISA spec! */ REQUIRE_FPU; - REQUIRE_ZFINX_OR_F(ctx); + /* Zfinx explicitly excludes the FMV transfer instructions. */ + REQUIRE_EXT(ctx, RVF); TCGv dest = dest_gpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); @@ -529,7 +530,8 @@ { /* NOTE: This was FMV.S.X in an earlier version of the ISA spec! */ REQUIRE_FPU; - REQUIRE_ZFINX_OR_F(ctx); + /* Zfinx explicitly excludes the FMV transfer instructions. */ + REQUIRE_EXT(ctx, RVF); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO); diff -Nru qemu-10.0.12+ds/target/riscv/insn_trans/trans_rvzfh.c.inc qemu-10.0.13+ds/target/riscv/insn_trans/trans_rvzfh.c.inc --- qemu-10.0.12+ds/target/riscv/insn_trans/trans_rvzfh.c.inc 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/riscv/insn_trans/trans_rvzfh.c.inc 2026-08-26 22:31:26.000000000 +0300 @@ -409,6 +409,7 @@ REQUIRE_FPU; REQUIRE_ZFHMIN_OR_ZHINXMIN(ctx); REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); @@ -443,6 +444,7 @@ REQUIRE_FPU; REQUIRE_ZFHMIN_OR_ZHINXMIN(ctx); REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rs1); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); diff -Nru qemu-10.0.12+ds/target/riscv/insn_trans/trans_rvzicfiss.c.inc qemu-10.0.13+ds/target/riscv/insn_trans/trans_rvzicfiss.c.inc --- qemu-10.0.12+ds/target/riscv/insn_trans/trans_rvzicfiss.c.inc 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/riscv/insn_trans/trans_rvzicfiss.c.inc 2026-08-26 22:31:26.000000000 +0300 @@ -32,6 +32,7 @@ TCGLabel *skip = gen_new_label(); uint32_t tmp = (get_xl(ctx) == MXL_RV64) ? 8 : 4; TCGv data = tcg_temp_new(); + gen_update_pc(ctx, 0); tcg_gen_ld_tl(addr, tcg_env, offsetof(CPURISCVState, ssp)); decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); tcg_gen_qemu_ld_tl(data, addr, SS_MMU_INDEX(ctx), @@ -40,7 +41,6 @@ tcg_gen_brcond_tl(TCG_COND_EQ, data, rs1, skip); tcg_gen_st_tl(tcg_constant_tl(RISCV_EXCP_SW_CHECK_BCFI_TVAL), tcg_env, offsetof(CPURISCVState, sw_check_code)); - gen_update_pc(ctx, 0); gen_helper_raise_exception(tcg_env, tcg_constant_i32(RISCV_EXCP_SW_CHECK)); gen_set_label(skip); diff -Nru qemu-10.0.12+ds/target/riscv/op_helper.c qemu-10.0.13+ds/target/riscv/op_helper.c --- qemu-10.0.12+ds/target/riscv/op_helper.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/riscv/op_helper.c 2026-08-26 22:31:26.000000000 +0300 @@ -142,7 +142,16 @@ static void check_zicbo_envcfg(CPURISCVState *env, target_ulong envbits, uintptr_t ra) { -#ifndef CONFIG_USER_ONLY +#if defined(CONFIG_USER_ONLY) + /* + * linux-user: the machine-level envcfg fields are not part of the + * user-mode environment; only the user-mode view of the enabling + * bits (senvcfg, as initialized for the guest) applies. + */ + if (!get_field(env->senvcfg, envbits)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra); + } +#else if ((env->priv < PRV_M) && !get_field(env->menvcfg, envbits)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra); } @@ -288,6 +297,11 @@ const target_ulong src_priv = env->priv; const bool src_virt = env->virt_enabled; + if ((env->virt_enabled && env->priv < PRV_S) || + (env->virt_enabled && get_field(env->hstatus, HSTATUS_VTSR))) { + riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); + } + if (!(env->priv >= PRV_S)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } @@ -303,10 +317,6 @@ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - if (env->virt_enabled && get_field(env->hstatus, HSTATUS_VTSR)) { - riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); - } - mstatus = env->mstatus; prev_priv = get_field(mstatus, MSTATUS_SPP); mstatus = set_field(mstatus, MSTATUS_SIE, diff -Nru qemu-10.0.12+ds/target/s390x/tcg/crypto_helper.c qemu-10.0.13+ds/target/s390x/tcg/crypto_helper.c --- qemu-10.0.12+ds/target/s390x/tcg/crypto_helper.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/s390x/tcg/crypto_helper.c 2026-08-26 22:31:26.000000000 +0300 @@ -16,6 +16,7 @@ #include "qemu/guest-random.h" #include "s390x-internal.h" #include "tcg_s390x.h" +#include "exec/cpu-common.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" @@ -120,8 +121,8 @@ sha512_bda(a, t); } -static void sha512_read_icv(CPUS390XState *env, uint64_t addr, - uint64_t a[8], uintptr_t ra) +static void sha512_read_icv(CPUS390XState *env, const int mmu_idx, + uint64_t addr, uint64_t a[8], uintptr_t ra) { int i; @@ -131,8 +132,8 @@ } } -static void sha512_write_ocv(CPUS390XState *env, uint64_t addr, - uint64_t a[8], uintptr_t ra) +static void sha512_write_ocv(CPUS390XState *env, const int mmu_idx, + uint64_t addr, uint64_t a[8], uintptr_t ra) { int i; @@ -142,8 +143,8 @@ } } -static void sha512_read_block(CPUS390XState *env, uint64_t addr, - uint64_t a[16], uintptr_t ra) +static void sha512_read_block(CPUS390XState *env, const int mmu_idx, + uint64_t addr, uint64_t a[16], uintptr_t ra) { int i; @@ -153,8 +154,8 @@ } } -static void sha512_read_mbl_be64(CPUS390XState *env, uint64_t addr, - uint8_t a[16], uintptr_t ra) +static void sha512_read_mbl_be64(CPUS390XState *env, const int mmu_idx, + uint64_t addr, uint8_t a[16], uintptr_t ra) { int i; @@ -164,8 +165,9 @@ } } -static int cpacf_sha512(CPUS390XState *env, uintptr_t ra, uint64_t param_addr, - uint64_t *message_reg, uint64_t *len_reg, uint32_t type) +static int cpacf_sha512(CPUS390XState *env, const int mmu_idx, uintptr_t ra, + uint64_t param_addr, uint64_t *message_reg, + uint64_t *len_reg, uint32_t type) { enum { MAX_BLOCKS_PER_RUN = 64 }; /* Arbitrary: keep interactivity. */ uint64_t len = *len_reg, a[8], processed = 0; @@ -183,7 +185,7 @@ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } - sha512_read_icv(env, param_addr, a, ra); + sha512_read_icv(env, mmu_idx, param_addr, a, ra); /* Process full blocks first. */ for (; len >= 128; len -= 128, processed += 128) { @@ -193,7 +195,7 @@ break; } - sha512_read_block(env, *message_reg + processed, w, ra); + sha512_read_block(env, mmu_idx, *message_reg + processed, w, ra); sha512_bda(a, w); } @@ -216,13 +218,13 @@ * or use an additional one. */ if (len < 112) { - sha512_read_mbl_be64(env, param_addr + 64, x + 112, ra); + sha512_read_mbl_be64(env, mmu_idx, param_addr + 64, x + 112, ra); } sha512_bda_be64(a, (uint64_t *)x); if (len >= 112) { memset(x, 0, 112); - sha512_read_mbl_be64(env, param_addr + 64, x + 112, ra); + sha512_read_mbl_be64(env, mmu_idx, param_addr + 64, x + 112, ra); sha512_bda_be64(a, (uint64_t *)x); } @@ -237,15 +239,15 @@ * TODO: if writing fails halfway through (e.g., when crossing page * boundaries), we're in trouble. We'd need something like access_prepare(). */ - sha512_write_ocv(env, param_addr, a, ra); + sha512_write_ocv(env, mmu_idx, param_addr, a, ra); *message_reg = deposit64(*message_reg, 0, message_reg_len, *message_reg + processed); *len_reg -= processed; return !len ? 0 : 3; } -static void fill_buf_random(CPUS390XState *env, uintptr_t ra, - uint64_t *buf_reg, uint64_t *len_reg) +static int fill_buf_random(CPUS390XState *env, const int mmu_idx, uintptr_t ra, + uint64_t *buf_reg, uint64_t *len_reg) { uint8_t tmp[256]; uint64_t len = *len_reg; @@ -266,18 +268,26 @@ --*len_reg; } len -= block; + + if (cpu_loop_exit_requested(env_cpu(env))) { + break; + } } + + return len == 0 ? 0 : 3; } uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3, uint32_t type) { + const int mmu_idx = cpu_mmu_index(env_cpu(env), false); const uintptr_t ra = GETPC(); const uint8_t mod = env->regs[0] & 0x80ULL; const uint8_t fc = env->regs[0] & 0x7fULL; uint8_t subfunc[16] = { 0 }; uint64_t param_addr; int i; + int cc; switch (type) { case S390_FEAT_TYPE_KMAC: @@ -304,12 +314,16 @@ } break; case 3: /* CPACF_*_SHA_512 */ - return cpacf_sha512(env, ra, env->regs[1], &env->regs[r2], + return cpacf_sha512(env, mmu_idx, ra, env->regs[1], &env->regs[r2], &env->regs[r2 + 1], type); case 114: /* CPACF_PRNO_TRNG */ - fill_buf_random(env, ra, &env->regs[r1], &env->regs[r1 + 1]); - fill_buf_random(env, ra, &env->regs[r2], &env->regs[r2 + 1]); - break; + cc = fill_buf_random(env, mmu_idx, ra, + &env->regs[r1], &env->regs[r1 + 1]); + if (cc == 0) { + cc = fill_buf_random(env, mmu_idx, ra, + &env->regs[r2], &env->regs[r2 + 1]); + } + return cc; default: /* we don't implement any other subfunction yet */ g_assert_not_reached(); diff -Nru qemu-10.0.12+ds/target/s390x/tcg/insn-data.h.inc qemu-10.0.13+ds/target/s390x/tcg/insn-data.h.inc --- qemu-10.0.12+ds/target/s390x/tcg/insn-data.h.inc 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/s390x/tcg/insn-data.h.inc 2026-08-26 22:31:26.000000000 +0300 @@ -884,8 +884,8 @@ C(0xe32f, STRVG, RXY_a, Z, la2, r1_o, new, m1_64, rev64, 0) /* STORE CLOCK */ - F(0xb205, STCK, S, Z, la2, 0, new, m1_64, stck, 0, IF_IO) - F(0xb27c, STCKF, S, SCF, la2, 0, new, m1_64, stck, 0, IF_IO) + F(0xb205, STCK, S, Z, la2, 0, new, 0, stck, 0, IF_IO) + F(0xb27c, STCKF, S, SCF, la2, 0, new, 0, stck, 0, IF_IO) /* STORE CLOCK EXTENDED */ F(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0, IF_IO) diff -Nru qemu-10.0.12+ds/target/s390x/tcg/int_helper.c qemu-10.0.13+ds/target/s390x/tcg/int_helper.c --- qemu-10.0.12+ds/target/s390x/tcg/int_helper.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/s390x/tcg/int_helper.c 2026-08-26 22:31:26.000000000 +0300 @@ -40,7 +40,8 @@ int32_t b = b64; int64_t q, r; - if (b == 0) { + /* Catch divide by zero, and non-representable quotient (MIN / -1). */ + if (b == 0 || (b == -1 && a == (1ll << 63))) { tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } diff -Nru qemu-10.0.12+ds/target/s390x/tcg/translate.c qemu-10.0.13+ds/target/s390x/tcg/translate.c --- qemu-10.0.12+ds/target/s390x/tcg/translate.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/s390x/tcg/translate.c 2026-08-26 22:31:26.000000000 +0300 @@ -4085,7 +4085,9 @@ static DisasJumpType op_stck(DisasContext *s, DisasOps *o) { gen_helper_stck(o->out, tcg_env); + tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_BEUQ); /* ??? We don't implement clock states. */ + /* Set the CC after the store; a suppressed store must preserve it. */ gen_op_movi_cc(s, 0); return DISAS_NEXT; } diff -Nru qemu-10.0.12+ds/target/sh4/cpu-param.h qemu-10.0.13+ds/target/sh4/cpu-param.h --- qemu-10.0.12+ds/target/sh4/cpu-param.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/sh4/cpu-param.h 2026-08-26 22:31:26.000000000 +0300 @@ -10,10 +10,7 @@ #define TARGET_PAGE_BITS 12 /* 4k */ #define TARGET_PHYS_ADDR_SPACE_BITS 32 -#ifdef CONFIG_USER_ONLY -# define TARGET_VIRT_ADDR_SPACE_BITS 31 -#else -# define TARGET_VIRT_ADDR_SPACE_BITS 32 -#endif +/* qemu-user does not emulate the MMU, so no need to limit to 31 bits. */ +#define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif diff -Nru qemu-10.0.12+ds/target/sparc/machine.c qemu-10.0.13+ds/target/sparc/machine.c --- qemu-10.0.12+ds/target/sparc/machine.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/target/sparc/machine.c 2026-08-26 22:31:26.000000000 +0300 @@ -143,6 +143,37 @@ .get = get_xcc, .put = put_xcc, }; + +static int get_cwp(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + uint32_t val = qemu_get_be32(f); + + /* needed to ensure that the wrapping registers are correctly updated */ + env->cwp = 0; + cpu_set_cwp(env, val); + + return 0; +} + +static int put_cwp(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + uint32_t val = env->cwp; + + qemu_put_be32(f, val); + return 0; +} + +static const VMStateInfo vmstate_cwp = { + .name = "uint32", + .get = get_cwp, + .put = put_cwp, +}; #else static bool fq_needed(void *opaque) { @@ -279,7 +310,14 @@ VMSTATE_CPU_TIMER(env.hstick, SPARCCPU), /* On SPARC32 env.psrpil and env.cwp are migrated as part of the PSR */ VMSTATE_UINT32(env.psrpil, SPARCCPU), - VMSTATE_UINT32(env.cwp, SPARCCPU), + { + .name = "env.cwp", + .version_id = 0, + .size = sizeof(uint32_t), + .info = &vmstate_cwp, + .flags = VMS_SINGLE, + .offset = 0, + }, #endif VMSTATE_END_OF_LIST() }, diff -Nru qemu-10.0.12+ds/tcg/optimize.c qemu-10.0.13+ds/tcg/optimize.c --- qemu-10.0.12+ds/tcg/optimize.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tcg/optimize.c 2026-08-26 22:31:26.000000000 +0300 @@ -2044,7 +2044,7 @@ static bool fold_mul(OptContext *ctx, TCGOp *op) { - if (fold_const2(ctx, op) || + if (fold_const2_commutative(ctx, op) || fold_xi_to_i(ctx, op, 0) || fold_xi_to_x(ctx, op, 1)) { return true; @@ -2602,8 +2602,17 @@ int sh = ti_const_val(t2); z_mask = do_constant_folding(op->opc, ctx->type, z_mask, sh); - s_mask = do_constant_folding(op->opc, ctx->type, s_mask, sh); + if (op->opc == INDEX_op_shr_i32 || op->opc == INDEX_op_shr_i64) { + /* + * Logical right shift will force the sign bit zero. + * Don't bother computing s_mask and let fold_masks + * recompute from z_mask. + */ + return fold_masks_z(ctx, op, z_mask); + } + + s_mask = do_constant_folding(op->opc, ctx->type, s_mask, sh); return fold_masks_zs(ctx, op, z_mask, s_mask); } diff -Nru qemu-10.0.12+ds/tcg/region.c qemu-10.0.13+ds/tcg/region.c --- qemu-10.0.12+ds/tcg/region.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tcg/region.c 2026-08-26 22:31:26.000000000 +0300 @@ -360,30 +360,30 @@ static bool tcg_region_alloc__locked(TCGContext *s) { if (region.current == region.n) { - return true; + return false; } tcg_region_assign(s, region.current); region.current++; - return false; + return true; } /* * Request a new region once the one in use has filled up. - * Returns true on error. + * Returns true on success. */ bool tcg_region_alloc(TCGContext *s) { - bool err; + bool ok; /* read the region size now; alloc__locked will overwrite it on success */ size_t size_full = s->code_gen_buffer_size; qemu_mutex_lock(®ion.lock); - err = tcg_region_alloc__locked(s); - if (!err) { + ok = tcg_region_alloc__locked(s); + if (ok) { region.agg_size_full += size_full - TCG_HIGHWATER; } qemu_mutex_unlock(®ion.lock); - return err; + return ok; } /* @@ -392,15 +392,35 @@ */ static void tcg_region_initial_alloc__locked(TCGContext *s) { - bool err = tcg_region_alloc__locked(s); - g_assert(!err); + bool ok = tcg_region_alloc__locked(s); + g_assert(ok); } -void tcg_region_initial_alloc(TCGContext *s) +void tcg_region_thread_initial_alloc(TCGContext *s) { + bool ok; + qemu_mutex_lock(®ion.lock); - tcg_region_initial_alloc__locked(s); + ok = tcg_region_alloc__locked(s); qemu_mutex_unlock(®ion.lock); + + /* + * A vCPU hotplug may happen at any time. When the new thread is + * started, the region pool may be exhausted. At this point in + * the new thread call stack, we are not in a position to fix this. + * Leave code_gen_ptr NULL, so that this thread's first call to + * tcg_tb_alloc() returns NULL, so that the translator performs + * a tb_flush() and retry. + * + * During the tb_flush(), tcg_region_reset_all() will assign a + * new region to all contexts, including this one. + */ + if (!ok) { + s->code_gen_buffer = NULL; + s->code_gen_ptr = NULL; + s->code_gen_buffer_size = 0; + s->code_gen_highwater = NULL; + } } /* Call from a safe-work context */ diff -Nru qemu-10.0.12+ds/tcg/tcg-internal.h qemu-10.0.13+ds/tcg/tcg-internal.h --- qemu-10.0.12+ds/tcg/tcg-internal.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tcg/tcg-internal.h 2026-08-26 22:31:26.000000000 +0300 @@ -36,7 +36,7 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus); bool tcg_region_alloc(TCGContext *s); -void tcg_region_initial_alloc(TCGContext *s); +void tcg_region_thread_initial_alloc(TCGContext *s); void tcg_region_prologue_set(TCGContext *s); static inline void *tcg_call_func(TCGOp *op) diff -Nru qemu-10.0.12+ds/tcg/tcg-op-gvec.c qemu-10.0.13+ds/tcg/tcg-op-gvec.c --- qemu-10.0.12+ds/tcg/tcg-op-gvec.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tcg/tcg-op-gvec.c 2026-08-26 22:31:26.000000000 +0300 @@ -2304,20 +2304,6 @@ tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); } -static void tcg_gen_ussub_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - TCGv_i32 min = tcg_constant_i32(0); - tcg_gen_sub_i32(d, a, b); - tcg_gen_movcond_i32(TCG_COND_LTU, d, a, b, min, d); -} - -static void tcg_gen_ussub_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - TCGv_i64 min = tcg_constant_i64(0); - tcg_gen_sub_i64(d, a, b); - tcg_gen_movcond_i64(TCG_COND_LTU, d, a, b, min, d); -} - void tcg_gen_gvec_ussub(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz) { diff -Nru qemu-10.0.12+ds/tcg/tcg-op.c qemu-10.0.13+ds/tcg/tcg-op.c --- qemu-10.0.12+ds/tcg/tcg-op.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tcg/tcg-op.c 2026-08-26 22:31:26.000000000 +0300 @@ -1421,6 +1421,16 @@ tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, b, a); } +void tcg_gen_ussub_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_ebb_new_i32(); + TCGv_i32 z = tcg_constant_i32(0); + + tcg_gen_sub_i32(t, a, b); + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, z, t); + tcg_temp_free_i32(t); +} + void tcg_gen_abs_i32(TCGv_i32 ret, TCGv_i32 a) { TCGv_i32 t = tcg_temp_ebb_new_i32(); @@ -3148,6 +3158,16 @@ tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, b, a); } +void tcg_gen_ussub_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_ebb_new_i64(); + TCGv_i64 z = tcg_constant_i64(0); + + tcg_gen_sub_i64(t, a, b); + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, z, t); + tcg_temp_free_i64(t); +} + void tcg_gen_abs_i64(TCGv_i64 ret, TCGv_i64 a) { TCGv_i64 t = tcg_temp_ebb_new_i64(); diff -Nru qemu-10.0.12+ds/tcg/tcg.c qemu-10.0.13+ds/tcg/tcg.c --- qemu-10.0.12+ds/tcg/tcg.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tcg/tcg.c 2026-08-26 22:31:26.000000000 +0300 @@ -1007,7 +1007,7 @@ qatomic_set(&tcg_ctxs[n], s); if (n > 0) { - tcg_region_initial_alloc(s); + tcg_region_thread_initial_alloc(s); } tcg_ctx = s; @@ -1570,18 +1570,24 @@ TranslationBlock *tb; void *next; - retry: - tb = (void *)ROUND_UP((uintptr_t)s->code_gen_ptr, align); - next = (void *)ROUND_UP((uintptr_t)(tb + 1), align); + while (1) { + tb = (void *)ROUND_UP((uintptr_t)s->code_gen_ptr, align); - if (unlikely(next > s->code_gen_highwater)) { - if (tcg_region_alloc(s)) { + /* + * Note that code_gen_ptr can be NULL after vCPU hotplug. + * See tcg_region_thread_initial_alloc. + */ + if (tb) { + next = (void *)ROUND_UP((uintptr_t)(tb + 1), align); + if (next <= s->code_gen_highwater) { + qatomic_set(&s->code_gen_ptr, next); + return tb; + } + } + if (!tcg_region_alloc(s)) { return NULL; } - goto retry; } - qatomic_set(&s->code_gen_ptr, next); - return tb; } void tcg_prologue_init(void) diff -Nru qemu-10.0.12+ds/tests/qemu-iotests/039 qemu-10.0.13+ds/tests/qemu-iotests/039 --- qemu-10.0.12+ds/tests/qemu-iotests/039 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tests/qemu-iotests/039 2026-08-26 22:31:26.000000000 +0300 @@ -85,6 +85,17 @@ _qcow2_dump_header | grep incompatible_features echo +echo "== Read-only open must not crash on close ==" + +# We must not try to write the QCOW2 header to a read-only image. +$QEMU_IMG info --image-opts \ + "driver=$IMGFMT,read-only=on,file.driver=file,file.filename=$TEST_IMG,file.read-only=off" \ + > /dev/null + +# The dirty bit must still be set: this open never wrote any guest data +_qcow2_dump_header | grep incompatible_features + +echo echo "== Repairing the image file must succeed ==" _check_test_img -r all diff -Nru qemu-10.0.12+ds/tests/qemu-iotests/039.out qemu-10.0.13+ds/tests/qemu-iotests/039.out --- qemu-10.0.12+ds/tests/qemu-iotests/039.out 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tests/qemu-iotests/039.out 2026-08-26 22:31:26.000000000 +0300 @@ -24,6 +24,9 @@ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) incompatible_features [0] +== Read-only open must not crash on close == +incompatible_features [0] + == Repairing the image file must succeed == ERROR cluster 5 refcount=0 reference=1 Rebuilding refcount structure diff -Nru qemu-10.0.12+ds/tests/qemu-iotests/testrunner.py qemu-10.0.13+ds/tests/qemu-iotests/testrunner.py --- qemu-10.0.12+ds/tests/qemu-iotests/testrunner.py 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tests/qemu-iotests/testrunner.py 2026-08-26 22:31:26.000000000 +0300 @@ -26,7 +26,7 @@ import json import shutil import sys -from multiprocessing import Pool +from multiprocessing import get_context from typing import List, Optional, Any, Sequence, Dict from testenv import TestEnv @@ -125,7 +125,7 @@ assert TestRunner.shared_self is None TestRunner.shared_self = self - with Pool(jobs) as p: + with get_context('fork').Pool(jobs) as p: results = p.starmap(self.proc_run_test, zip(tests, [test_field_width] * len(tests))) diff -Nru qemu-10.0.12+ds/tests/qtest/ahci-test.c qemu-10.0.13+ds/tests/qtest/ahci-test.c --- qemu-10.0.12+ds/tests/qtest/ahci-test.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tests/qtest/ahci-test.c 2026-08-26 22:31:26.000000000 +0300 @@ -1565,6 +1565,31 @@ return 0; } +static int ahci_cb_cmp_raw(AHCIQState *ahci, AHCICommand *cmd, + const AHCIOpts *opts) +{ + unsigned char *tx = opts->opaque; + unsigned char *rx; + unsigned i, nsectors; + + if (!opts->size) { + return 0; + } + + nsectors = opts->size / ATAPI_RAW_SECTOR_SIZE; + rx = g_malloc0(opts->size); + qtest_bufread(ahci->parent->qts, opts->buffer, rx, opts->size); + /* Each raw sector carries its 2048-byte payload past a 16-byte header. */ + for (i = 0; i < nsectors; i++) { + g_assert_cmphex(memcmp(rx + i * ATAPI_RAW_SECTOR_SIZE + 16, + tx + i * ATAPI_SECTOR_SIZE, + ATAPI_SECTOR_SIZE), ==, 0); + } + g_free(rx); + + return 0; +} + static void ahci_test_cdrom(int nsectors, bool dma, uint8_t cmd, bool override_bcl, uint16_t bcl) { @@ -1625,6 +1650,47 @@ ahci_test_cdrom_read10(3, false); } +static void ahci_test_cdrom_raw(int nsectors, bool dma) +{ + AHCIQState *ahci; + unsigned char *tx; + char *iso; + int fd; + AHCIOpts opts = { + .size = (uint64_t)ATAPI_RAW_SECTOR_SIZE * nsectors, + .atapi = true, + .atapi_dma = dma, + .atapi_raw = true, + .set_bcl = true, + .bcl = ATAPI_RAW_SECTOR_SIZE, /* one raw sector per DRQ burst */ + .post_cb = ahci_cb_cmp_raw, + }; + uint64_t iso_size = (uint64_t)ATAPI_SECTOR_SIZE * (nsectors + 1); + + fd = prepare_iso(iso_size, &tx, &iso); + opts.opaque = tx; + + ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s,format=raw " + "-M q35 " + "-device ide-cd,drive=drive0 ", iso); + + ahci_exec(ahci, ahci_port_select(ahci), CMD_ATAPI_READ_CD, &opts); + + g_free(tx); + ahci_shutdown(ahci); + remove_iso(fd, iso); +} + +static void test_cdrom_dma_raw(void) +{ + ahci_test_cdrom_raw(3, true); +} + +static void test_cdrom_pio_raw(void) +{ + ahci_test_cdrom_raw(3, false); +} + /* * Regression test: a buffered ATAPI read completing after a command * engine restart must not dereference the cleared cur_cmd. Cover both @@ -1688,6 +1754,70 @@ test_atapi_engine_restart_in_flight(true); } +/* + * Regression test: a multi-sector ATAPI read fetches its later sectors from + * inside the first read's completion; a concurrent drain (as a guest reset + * triggers via bdrv_drain_all_begin) must not wedge on that nested read. + * blkdebug keeps the read in flight across x-blockdev-set-iothread. + */ +static void test_atapi_drain_in_flight(bool dma) +{ + AHCIQState *ahci; + AHCICommand *cmd; + unsigned char *tx; + char *iso; + int fd; + uint8_t port; + uint64_t buffer; + uint16_t bcl = ATAPI_SECTOR_SIZE * 2; + uint64_t iso_size = (uint64_t)ATAPI_SECTOR_SIZE * 3; + + fd = prepare_iso(iso_size, &tx, &iso); + + /* 1s read delay: a wide margin so the drain starts before it completes */ + ahci = ahci_boot_and_enable( + "-blockdev driver=file,node-name=file0,filename=%s,read-only=on " + "-blockdev driver=blkdebug,node-name=cd0,image=file0,read-only=on," + "inject-error.0.event=none,inject-error.0.iotype=read," + "inject-error.0.errno=0,inject-error.0.delay-ns=1000000000 " + "-M q35 " + "-device ide-cd,drive=cd0 ", iso); + port = ahci_port_select(ahci); + + buffer = ahci_alloc(ahci, bcl); + qtest_memset(ahci->parent->qts, buffer, 0x00, bcl); + + cmd = ahci_atapi_command_create(CMD_ATAPI_READ_10, bcl, dma); + ahci_command_adjust(cmd, 0, buffer, bcl, 0); + ahci_command_commit(ahci, cmd, port); + ahci_command_issue_async(ahci, cmd); + + /* Drain (all nodes) while the delayed read is still in flight. */ + qtest_qmp_assert_success(ahci->parent->qts, + "{ 'execute': 'x-blockdev-set-iothread'," + " 'arguments': { 'node-name': 'cd0', 'iothread': null," + " 'force': true } }"); + + /* Round-trip through the device to confirm qemu is still alive. */ + ahci_px_rreg(ahci, port, AHCI_PX_TFD); + + ahci_command_free(cmd); + ahci_free(ahci, buffer); + g_free(tx); + ahci_shutdown(ahci); + remove_iso(fd, iso); +} + +static void test_atapi_drain_pio(void) +{ + test_atapi_drain_in_flight(false); +} + +static void test_atapi_drain_dma(void) +{ + test_atapi_drain_in_flight(true); +} + /* Regression test: Test that a READ_CD command with a BCL of 0 but a size of 0 * completes as a NOP instead of erroring out. */ static void test_atapi_bcl(void) @@ -2101,8 +2231,10 @@ qtest_add_func("/ahci/cdrom/dma/single", test_cdrom_dma); qtest_add_func("/ahci/cdrom/dma/multi", test_cdrom_dma_multi); + qtest_add_func("/ahci/cdrom/dma/raw", test_cdrom_dma_raw); qtest_add_func("/ahci/cdrom/pio/single", test_cdrom_pio); qtest_add_func("/ahci/cdrom/pio/multi", test_cdrom_pio_multi); + qtest_add_func("/ahci/cdrom/pio/raw", test_cdrom_pio_raw); qtest_add_func("/ahci/cdrom/pio/bcl", test_atapi_bcl); qtest_add_func("/ahci/cdrom/eject", test_atapi_tray); @@ -2110,6 +2242,8 @@ test_atapi_engine_restart_pio); qtest_add_func("/ahci/cdrom/engine_restart/dma", test_atapi_engine_restart_dma); + qtest_add_func("/ahci/cdrom/drain/pio", test_atapi_drain_pio); + qtest_add_func("/ahci/cdrom/drain/dma", test_atapi_drain_dma); ret = g_test_run(); diff -Nru qemu-10.0.12+ds/tests/qtest/ide-test.c qemu-10.0.13+ds/tests/qtest/ide-test.c --- qemu-10.0.12+ds/tests/qtest/ide-test.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tests/qtest/ide-test.c 2026-08-26 22:31:26.000000000 +0300 @@ -48,6 +48,10 @@ #define ATAPI_BLOCK_SIZE 2048 +/* Raw READ CD sector: 12 sync + 4 header + 2048 data + 288 EDC/ECC. */ +#define ATAPI_RAW_SIZE 2352 +#define ATAPI_RAW_DATA 16 + /* How many bytes to receive via ATAPI PIO at one time. * Must be less than 0xFFFF. */ #define BYTE_COUNT_LIMIT 5120 @@ -982,6 +986,41 @@ } } +typedef struct ReadCDCDB { + uint8_t opcode; + uint8_t sector_type; + uint32_t lba; + uint8_t length[3]; + uint8_t main_channel; + uint8_t sub_channel; + uint8_t control; +} __attribute__((__packed__)) ReadCDCDB; + +static void send_scsi_cdb_read_cd(QPCIDevice *dev, QPCIBar ide_bar, + uint64_t lba, int nblocks) +{ + ReadCDCDB pkt = { }; + int i; + + g_assert_cmpint(lba, <=, UINT32_MAX); + g_assert_cmpint(nblocks, >=, 0); + g_assert_cmpint(nblocks, <=, 0xffffff); + + /* Construct SCSI CDB packet */ + pkt.opcode = 0xbe; + pkt.lba = cpu_to_be32(lba); + pkt.length[0] = (nblocks >> 16) & 0xff; + pkt.length[1] = (nblocks >> 8) & 0xff; + pkt.length[2] = nblocks & 0xff; + pkt.main_channel = 0xf8; /* sync + headers + user data + EDC/ECC: 2352 */ + + /* Send Packet */ + for (i = 0; i < sizeof(ReadCDCDB) / 2; i++) { + qpci_io_writew(dev, ide_bar, reg_data, + le16_to_cpu(((uint16_t *)&pkt)[i])); + } +} + static void nsleep(QTestState *qts, int64_t nsecs) { const struct timespec val = { .tv_nsec = nsecs }; @@ -1034,8 +1073,14 @@ g_assert_not_reached(); } -static void cdrom_pio_impl(int nblocks) +#define CDROM_PIO 0 +#define CDROM_DMA (1 << 0) +#define CDROM_RAW (1 << 1) + +static void cdrom_read_impl(int nblocks, unsigned flags) { + bool dma = flags & CDROM_DMA; + bool raw = flags & CDROM_RAW; QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; @@ -1043,8 +1088,11 @@ int patt_blocks = MAX(16, nblocks); size_t patt_len = ATAPI_BLOCK_SIZE * patt_blocks; char *pattern = g_malloc(patt_len); - size_t rxsize = ATAPI_BLOCK_SIZE * nblocks; + unsigned xfer = raw ? ATAPI_RAW_SIZE : ATAPI_BLOCK_SIZE; + size_t rxsize = xfer * nblocks; uint16_t *rx = g_malloc0(rxsize); + void (*send_cdb)(QPCIDevice *, QPCIBar, uint64_t, int) = + raw ? send_scsi_cdb_read_cd : send_scsi_cdb_read10; int i, j; uint8_t data; uint16_t limit; @@ -1063,57 +1111,83 @@ dev = get_pci_device(qts, &bmdma_bar, &ide_bar); qtest_irq_intercept_in(qts, "ioapic"); - /* PACKET command on device 0 */ - qpci_io_writeb(dev, ide_bar, reg_device, 0); - qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); - qpci_io_writeb(dev, ide_bar, reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF)); - qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET); - /* HP0: Check_Status_A State */ - nsleep(qts, 400); - data = ide_wait_clear(qts, BSY); - /* HP1: Send_Packet State */ - assert_bit_set(data, DRQ | DRDY); - assert_bit_clear(data, ERR | DF | BSY); - - /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */ - send_scsi_cdb_read10(dev, ide_bar, 0, nblocks); - - /* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes. - * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes. - * We allow an odd limit only when the remaining transfer size is - * less than BYTE_COUNT_LIMIT. However, SCSI's read10 command can only - * request n blocks, so our request size is always even. - * For this reason, we assume there is never a hanging byte to fetch. */ - g_assert(!(rxsize & 1)); - limit = BYTE_COUNT_LIMIT & ~1; - for (i = 0; i < DIV_ROUND_UP(rxsize, limit); i++) { - size_t offset = i * (limit / 2); - size_t rem = (rxsize / 2) - offset; + if (dma) { + uintptr_t guest_buf = guest_alloc(&guest_malloc, rxsize); + PrdtEntry prdt[1]; - /* HP3: INTRQ_Wait */ - ide_wait_intr(qts, IDE_PRIMARY_IRQ); + prdt[0].addr = cpu_to_le32(guest_buf); + prdt[0].size = cpu_to_le32(rxsize | PRDT_EOT); + + send_dma_request_dev(qts, dev, bmdma_bar, ide_bar, CMD_PACKET, 0, + nblocks, prdt, ARRAY_SIZE(prdt), send_cdb); - /* HP2: Check_Status_B (and clear IRQ) */ + qtest_memread(qts, guest_buf, rx, rxsize); + } else { + /* PACKET command on device 0 */ + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); + qpci_io_writeb(dev, ide_bar, reg_lba_high, + (BYTE_COUNT_LIMIT >> 8 & 0xFF)); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET); + /* HP0: Check_Status_A State */ + nsleep(qts, 400); data = ide_wait_clear(qts, BSY); + /* HP1: Send_Packet State */ assert_bit_set(data, DRQ | DRDY); assert_bit_clear(data, ERR | DF | BSY); - /* HP4: Transfer_Data */ - for (j = 0; j < MIN((limit / 2), rem); j++) { - rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar, - reg_data)); + send_cdb(dev, ide_bar, 0, nblocks); + + /* + * Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes. + * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes. + * We allow an odd limit only when the remaining transfer size is + * less than BYTE_COUNT_LIMIT. However, SCSI's read10 command can only + * request n blocks, so our request size is always even. + * For this reason, we assume there is never a hanging byte to fetch. + */ + g_assert(!(rxsize & 1)); + limit = BYTE_COUNT_LIMIT & ~1; + for (i = 0; i < DIV_ROUND_UP(rxsize, limit); i++) { + size_t offset = i * (limit / 2); + size_t rem = (rxsize / 2) - offset; + + /* HP3: INTRQ_Wait */ + ide_wait_intr(qts, IDE_PRIMARY_IRQ); + + /* HP2: Check_Status_B (and clear IRQ) */ + data = ide_wait_clear(qts, BSY); + assert_bit_set(data, DRQ | DRDY); + assert_bit_clear(data, ERR | DF | BSY); + + /* HP4: Transfer_Data */ + for (j = 0; j < MIN((limit / 2), rem); j++) { + rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar, + reg_data)); + } } + + /* Check for final completion IRQ */ + ide_wait_intr(qts, IDE_PRIMARY_IRQ); + + /* Sanity check final state */ + data = ide_wait_clear(qts, DRQ); + assert_bit_set(data, DRDY); + assert_bit_clear(data, DRQ | ERR | DF | BSY); } - /* Check for final completion IRQ */ - ide_wait_intr(qts, IDE_PRIMARY_IRQ); + if (raw) { + /* The 2048-byte payload of each raw sector sits past its header. */ + for (i = 0; i < nblocks; i++) { + uint8_t *sec = (uint8_t *)rx + i * ATAPI_RAW_SIZE + ATAPI_RAW_DATA; - /* Sanity check final state */ - data = ide_wait_clear(qts, DRQ); - assert_bit_set(data, DRDY); - assert_bit_clear(data, DRQ | ERR | DF | BSY); + g_assert_cmpint(memcmp(sec, pattern + i * ATAPI_BLOCK_SIZE, + ATAPI_BLOCK_SIZE), ==, 0); + } + } else { + g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0); + } - g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0); g_free(pattern); g_free(rx); test_bmdma_teardown(qts); @@ -1122,51 +1196,33 @@ static void test_cdrom_pio(void) { - cdrom_pio_impl(1); + cdrom_read_impl(1, CDROM_PIO); } static void test_cdrom_pio_large(void) { /* Test a few loops of the PIO DRQ mechanism. */ - cdrom_pio_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE); + cdrom_read_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE, CDROM_PIO); } - static void test_cdrom_dma(void) { - QTestState *qts; - static const size_t len = ATAPI_BLOCK_SIZE; - size_t ret; - char *pattern = g_malloc(ATAPI_BLOCK_SIZE * 16); - char *rx = g_malloc0(len); - uintptr_t guest_buf; - PrdtEntry prdt[1]; - FILE *fh; - - qts = ide_test_start( - "-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " - "-device ide-cd,drive=sr0,bus=ide.0", tmp_path[0]); - qtest_irq_intercept_in(qts, "ioapic"); - - guest_buf = guest_alloc(&guest_malloc, len); - prdt[0].addr = cpu_to_le32(guest_buf); - prdt[0].size = cpu_to_le32(len | PRDT_EOT); - - generate_pattern(pattern, ATAPI_BLOCK_SIZE * 16, ATAPI_BLOCK_SIZE); - fh = fopen(tmp_path[0], "wb+"); - ret = fwrite(pattern, ATAPI_BLOCK_SIZE, 16, fh); - g_assert_cmpint(ret, ==, 16); - fclose(fh); + cdrom_read_impl(1, CDROM_DMA); +} - send_dma_request(qts, CMD_PACKET, 0, 1, prdt, 1, send_scsi_cdb_read10); +static void test_cdrom_dma_large(void) +{ + cdrom_read_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE, CDROM_DMA); +} - /* Read back data from guest memory into local qtest memory */ - qtest_memread(qts, guest_buf, rx, len); - g_assert_cmpint(memcmp(pattern, rx, len), ==, 0); +static void test_cdrom_pio_raw(void) +{ + cdrom_read_impl(4, CDROM_RAW); +} - g_free(pattern); - g_free(rx); - test_bmdma_teardown(qts); +static void test_cdrom_dma_raw(void) +{ + cdrom_read_impl(4, CDROM_DMA | CDROM_RAW); } int main(int argc, char **argv) @@ -1228,6 +1284,9 @@ qtest_add_func("/ide/cdrom/pio", test_cdrom_pio); qtest_add_func("/ide/cdrom/pio_large", test_cdrom_pio_large); qtest_add_func("/ide/cdrom/dma", test_cdrom_dma); + qtest_add_func("/ide/cdrom/dma_large", test_cdrom_dma_large); + qtest_add_func("/ide/cdrom/pio_raw", test_cdrom_pio_raw); + qtest_add_func("/ide/cdrom/dma_raw", test_cdrom_dma_raw); ret = g_test_run(); diff -Nru qemu-10.0.12+ds/tests/qtest/libqos/ahci.c qemu-10.0.13+ds/tests/qtest/libqos/ahci.c --- qemu-10.0.12+ds/tests/qtest/libqos/ahci.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tests/qtest/libqos/ahci.c 2026-08-26 22:31:26.000000000 +0300 @@ -707,6 +707,11 @@ if (opts->atapi) { uint16_t bcl = opts->set_bcl ? opts->bcl : ATAPI_SECTOR_SIZE; cmd = ahci_atapi_command_create(op, bcl, opts->atapi_dma); + if (opts->atapi_raw) { + /* request full 2352-byte raw sectors; sector_size must match */ + cmd->atapi_cmd[9] = 0xf8; + cmd->sector_size = ATAPI_RAW_SECTOR_SIZE; + } } else { cmd = ahci_command_create(op); } diff -Nru qemu-10.0.12+ds/tests/qtest/libqos/ahci.h qemu-10.0.13+ds/tests/qtest/libqos/ahci.h --- qemu-10.0.12+ds/tests/qtest/libqos/ahci.h 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tests/qtest/libqos/ahci.h 2026-08-26 22:31:26.000000000 +0300 @@ -242,6 +242,7 @@ #define AHCI_SECTOR_SIZE (512) #define ATAPI_SECTOR_SIZE (2048) +#define ATAPI_RAW_SECTOR_SIZE (2352) #define AHCI_SIGNATURE_CDROM (0xeb140101) #define AHCI_SIGNATURE_DISK (0x00000101) @@ -485,6 +486,7 @@ uint64_t buffer; /* Pointer to source or destination guest buffer */ bool atapi; /* ATAPI command? */ bool atapi_dma; /* Use DMA for ATAPI? */ + bool atapi_raw; /* READ CD returning 2352-byte raw sectors */ bool error; int (*pre_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *); int (*mid_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *); diff -Nru qemu-10.0.12+ds/tests/tcg/i386/test-i386-opt-shr.c qemu-10.0.13+ds/tests/tcg/i386/test-i386-opt-shr.c --- qemu-10.0.12+ds/tests/tcg/i386/test-i386-opt-shr.c 1970-01-01 03:00:00.000000000 +0300 +++ qemu-10.0.13+ds/tests/tcg/i386/test-i386-opt-shr.c 2026-08-26 22:31:26.000000000 +0300 @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Regression test for tcg optimize vs sign bit repetition counting. */ + +#include <assert.h> + +int main() +{ +#ifndef __x86_64__ + char test; + + asm("movw $0x4000, %%ax\n\t" + "addw %%ax, %%ax\n\t" + "cwtl\n\t" + "shrl %%eax\n\t" + "cmpw $-0x3fff, %%ax\n\t" + "setnl %%al" + : "=a"(test)); + assert(!test); +#endif + return 0; +} diff -Nru qemu-10.0.12+ds/tests/tcg/s390x/Makefile.target qemu-10.0.13+ds/tests/tcg/s390x/Makefile.target --- qemu-10.0.12+ds/tests/tcg/s390x/Makefile.target 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tests/tcg/s390x/Makefile.target 2026-08-26 22:31:26.000000000 +0300 @@ -49,6 +49,7 @@ TESTS+=cvb TESTS+=ts TESTS+=ex-smc +TESTS+=stckf cdsg: CFLAGS+=-pthread cdsg: LDFLAGS+=-pthread @@ -70,6 +71,7 @@ Z13_TESTS+=vstl Z13_TESTS+=vrep Z13_TESTS+=precise-smc-user +Z13_TESTS+=prno-trng $(Z13_TESTS): CFLAGS+=-march=z13 -O2 TESTS+=$(Z13_TESTS) diff -Nru qemu-10.0.12+ds/tests/tcg/s390x/div.c qemu-10.0.13+ds/tests/tcg/s390x/div.c --- qemu-10.0.12+ds/tests/tcg/s390x/div.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tests/tcg/s390x/div.c 2026-08-26 22:31:26.000000000 +0300 @@ -1,6 +1,15 @@ #include <assert.h> +#include <signal.h> #include <stdint.h> +/* Set asynchronously by the signal handler. */ +static volatile int signum; + +static void signal_handler(int n) +{ + signum = n; +} + static void test_dr(void) { register int32_t r0 asm("r0") = -1; @@ -65,11 +74,39 @@ assert(r == 1); } +/* + * The most negative dividend divided by -1 yields a quotient that does not + * fit into 32 bits, so DR must raise a fixed-point-divide exception. + */ +static void test_dr_overflow(void) +{ + struct sigaction act = { .sa_handler = signal_handler }; + register int32_t r0 asm("r0"); + register int32_t r1 asm("r1"); + int32_t b = -1; + int err; + + err = sigaction(SIGFPE, &act, NULL); + assert(err == 0); + signum = -1; + + r0 = 0x80000000; + r1 = 0; + asm volatile("dr %[r0],%[b]" + : [r0] "+r" (r0), [r1] "+r" (r1) + : [b] "r" (b) + : "cc"); + assert(signum == SIGFPE); + + signal(SIGFPE, SIG_DFL); +} + int main(void) { test_dr(); test_dlr(); test_dsgr(); test_dlgr(); + test_dr_overflow(); return 0; } diff -Nru qemu-10.0.12+ds/tests/tcg/s390x/prno-trng.c qemu-10.0.13+ds/tests/tcg/s390x/prno-trng.c --- qemu-10.0.12+ds/tests/tcg/s390x/prno-trng.c 1970-01-01 03:00:00.000000000 +0300 +++ qemu-10.0.13+ds/tests/tcg/s390x/prno-trng.c 2026-08-26 22:31:26.000000000 +0300 @@ -0,0 +1,67 @@ +/* + * Test that PERFORM RANDOM NUMBER OPERATION TRNG is interruptible. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <assert.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <asm/ucontext.h> + +static unsigned char buf1[16 * 1024 * 1024]; +static unsigned char buf2[16 * 1024 * 1024]; + +static volatile sig_atomic_t interrupted; + +static void sigprof_handler(int sig, siginfo_t *info, void *ucontext) +{ + struct ucontext *uc = ucontext; + unsigned long addr = uc->uc_mcontext.regs.psw.addr; + + if (*(unsigned short *)(addr - 4) == 0xb93c) { + interrupted++; + } +} + +static void prno_trng(void *b1, unsigned long l1, void *b2, unsigned long l2) +{ + register unsigned long r0 asm("r0") = 114; /* TRNG */ + register unsigned long r2 asm("r2") = (unsigned long)b1; + register unsigned long r3 asm("r3") = l1; + register unsigned long r4 asm("r4") = (unsigned long)b2; + register unsigned long r5 asm("r5") = l2; + + asm volatile("0: ppno %[r2],%[r4]\n" /* prno alias for old toolchains */ + " jo 0b" + : [r2] "+r" (r2), [r3] "+r" (r3) + , [r4] "+r" (r4), [r5] "+r" (r5) + : "r" (r0) + : "cc", "memory"); +} + +int main(void) +{ + struct itimerval it = { + .it_interval = { .tv_usec = 10000 }, /* 0.01s */ + .it_value = { .tv_usec = 10000 }, + }; + struct sigaction act = { + .sa_sigaction = sigprof_handler, + .sa_flags = SA_SIGINFO, + }; + int err; + + err = sigaction(SIGPROF, &act, NULL); + assert(err == 0); + err = setitimer(ITIMER_PROF, &it, NULL); + assert(err == 0); + + prno_trng(buf1, sizeof(buf1), buf2, sizeof(buf2)); + printf("interrupted %d times\n", interrupted); + assert(interrupted >= 3); + + return EXIT_SUCCESS; +} diff -Nru qemu-10.0.12+ds/tests/tcg/s390x/stckf.c qemu-10.0.13+ds/tests/tcg/s390x/stckf.c --- qemu-10.0.12+ds/tests/tcg/s390x/stckf.c 1970-01-01 03:00:00.000000000 +0300 +++ qemu-10.0.13+ds/tests/tcg/s390x/stckf.c 2026-08-26 22:31:26.000000000 +0300 @@ -0,0 +1,44 @@ +/* + * Test that a faulting STORE CLOCK FAST does not clobber the condition code. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <assert.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> + +static void handle_sigsegv(int sig, siginfo_t *info, void *ucontext) +{ + mcontext_t *mcontext = &((ucontext_t *)ucontext)->uc_mcontext; + + /* The condition code must be the one set by SLGR, not garbage. */ + _exit(((mcontext->psw.mask >> 44) & 3) == 3 ? EXIT_SUCCESS : EXIT_FAILURE); +} + +int main(void) +{ + struct sigaction act = { + .sa_sigaction = handle_sigsegv, + .sa_flags = SA_SIGINFO, + }; + int err; + + err = sigaction(SIGSEGV, &act, NULL); + assert(err == 0); + + asm volatile( + "lghi %%r1,100\n" + "lghi %%r2,0\n" + "clgr %%r1,%%r2\n" /* CC_OP_LTUGTU_64 */ + /* cc_src=100 is not valid for CC_OP_SUBU */ + "ipm %%r0\n" /* force cc_src to env */ + "lghi %%r3,5\n" + "lghi %%r4,3\n" + "slgr %%r3,%%r4\n" /* CC_OP_SUBU, cc=3 */ + "lghi %%r5,0\n" + "stckf 0(%%r5)\n" /* faults; cc must stay 3 */ + : : : "r0", "r1", "r2", "r3", "r4", "r5", "cc", "memory"); + + return EXIT_FAILURE; +} diff -Nru qemu-10.0.12+ds/tests/tcg/x86_64/Makefile.target qemu-10.0.13+ds/tests/tcg/x86_64/Makefile.target --- qemu-10.0.12+ds/tests/tcg/x86_64/Makefile.target 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tests/tcg/x86_64/Makefile.target 2026-08-26 22:31:26.000000000 +0300 @@ -15,6 +15,7 @@ X86_64_TESTS += noexec X86_64_TESTS += cmpxchg X86_64_TESTS += adox +X86_64_TESTS += segment-prefixes X86_64_TESTS += test-1648 X86_64_TESTS += test-2175 X86_64_TESTS += cross-modifying-code diff -Nru qemu-10.0.12+ds/tests/tcg/x86_64/segment-prefixes.c qemu-10.0.13+ds/tests/tcg/x86_64/segment-prefixes.c --- qemu-10.0.12+ds/tests/tcg/x86_64/segment-prefixes.c 1970-01-01 03:00:00.000000000 +0300 +++ qemu-10.0.13+ds/tests/tcg/x86_64/segment-prefixes.c 2026-08-26 22:31:26.000000000 +0300 @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* See https://gitlab.com/qemu-project/qemu/-/work_items/3391 */ + +int main() +{ + int data = 0; + + /* Ensure that ignored segment override prefixes are actually ignored */ + asm volatile ( + "wrgsbase %0\n\t" + ".byte 0x65, 0x26\n\t" /* prefixes: GS + ES */ + "movb $0, 0\n\t" + ".byte 0x65, 0x2E\n\t" /* prefixes: GS + CS */ + "movb $0, 0\n\t" + ".byte 0x65, 0x36\n\t" /* prefixes: GS + SS */ + "movb $0, 0\n\t" + ".byte 0x65, 0x3E\n\t" /* prefixes: GS + DS */ + "movb $0, 0\n\t" + : + : "r" (&data) + : "memory" + ); + + return 0; +} diff -Nru qemu-10.0.12+ds/tests/unit/meson.build qemu-10.0.13+ds/tests/unit/meson.build --- qemu-10.0.12+ds/tests/unit/meson.build 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tests/unit/meson.build 2026-08-26 22:31:26.000000000 +0300 @@ -74,6 +74,7 @@ 'test-blockjob': [testblock], 'test-blockjob-txn': [testblock], 'test-block-backend': [testblock], + 'test-block-accounting': [testblock], 'test-block-iothread': [testblock], 'test-write-threshold': [testblock], 'test-crypto-hash': [crypto], diff -Nru qemu-10.0.12+ds/tests/unit/test-block-accounting.c qemu-10.0.13+ds/tests/unit/test-block-accounting.c --- qemu-10.0.12+ds/tests/unit/test-block-accounting.c 1970-01-01 03:00:00.000000000 +0300 +++ qemu-10.0.13+ds/tests/unit/test-block-accounting.c 2026-08-26 22:31:26.000000000 +0300 @@ -0,0 +1,115 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * BlockAcctStats latency histogram locking regression test + * + * Copyright (c) 2026 Virtuozzo International GmbH. + * + * Regression test for missing stats->lock in + * block_latency_histogram_set()/block_latency_histograms_clear(), + * racing block_account_one_io() reading the same fields from an + * iothread. Aborts reliably before the fix, passes after it. + */ + +#include "qemu/osdep.h" +#include "block/block.h" +#include "block/accounting.h" +#include "system/block-backend.h" +#include "system/block-backend-io.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "qemu/thread.h" + +#define RACE_DURATION_MS 2000 +#define NUM_READER_THREADS 8 + +static bool stop_workers; + +/* + * Different bin counts, so the writer's g_free()/g_new() churn can be + * caught mid-update. Values are small enough (nanoseconds) that plain + * back-to-back start/done calls exercise every bin without sleeping. + */ +static uint64List boundaries_a[] = { + { .next = &boundaries_a[1], .value = 1000 }, + { .next = &boundaries_a[2], .value = 5000 }, + { .next = NULL, .value = 50000 }, +}; + +static uint64List boundaries_b[] = { + { .next = &boundaries_b[1], .value = 800 }, + { .next = &boundaries_b[2], .value = 3000 }, + { .next = &boundaries_b[3], .value = 20000 }, + { .next = NULL, .value = 200000 }, +}; + +static void *writer_thread(void *opaque) +{ + BlockAcctStats *stats = opaque; + + while (!qatomic_read(&stop_workers)) { + block_latency_histogram_set(stats, BLOCK_ACCT_READ, boundaries_a); + block_latency_histogram_set(stats, BLOCK_ACCT_READ, boundaries_b); + block_latency_histograms_clear(stats); + } + + return NULL; +} + +static void *reader_thread(void *opaque) +{ + BlockAcctStats *stats = opaque; + + while (!qatomic_read(&stop_workers)) { + BlockAcctCookie cookie; + + block_acct_start(stats, &cookie, 4096, BLOCK_ACCT_READ); + block_acct_done(stats, &cookie); + } + + return NULL; +} + +static void test_latency_histogram_race(void) +{ + BlockBackend *blk = blk_new(qemu_get_aio_context(), + BLK_PERM_ALL, BLK_PERM_ALL); + BlockAcctStats *stats = blk_get_stats(blk); + QemuThread writer, readers[NUM_READER_THREADS]; + int i; + + /* Histogram has to be enabled (bins != NULL) before racing it. */ + g_assert(block_latency_histogram_set(stats, BLOCK_ACCT_READ, + boundaries_a) == 0); + + stop_workers = false; + qemu_thread_create(&writer, "hist-writer", writer_thread, stats, + QEMU_THREAD_JOINABLE); + for (i = 0; i < NUM_READER_THREADS; i++) { + qemu_thread_create(&readers[i], "hist-reader", reader_thread, stats, + QEMU_THREAD_JOINABLE); + } + + g_usleep(RACE_DURATION_MS * 1000); + qatomic_set(&stop_workers, true); + + qemu_thread_join(&writer); + for (i = 0; i < NUM_READER_THREADS; i++) { + qemu_thread_join(&readers[i]); + } + + blk_unref(blk); +} + +int main(int argc, char **argv) +{ + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/block-accounting/latency_histogram_race", + test_latency_histogram_race); + + return g_test_run(); +} diff -Nru qemu-10.0.12+ds/tests/unit/test-coroutine.c qemu-10.0.13+ds/tests/unit/test-coroutine.c --- qemu-10.0.12+ds/tests/unit/test-coroutine.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/tests/unit/test-coroutine.c 2026-08-26 22:31:26.000000000 +0300 @@ -422,6 +422,57 @@ } /* + * Check that a wake delivered before the sleeper parks is not lost. + * + * qemu_co_sleep_wake() is fire-and-forget: a caller cancelling a + * sleep/work loop may call it in the window after the sleeper has + * decided to sleep but before it has published itself inside + * qemu_co_sleep(). The wake must be sticky and shorten the next sleep + * rather than being dropped (which would block until the full sleep + * duration expired). + * + * No threads, timers or AioContext are needed: coroutines are + * cooperative, so ordering the wake before the sleep deterministically + * reproduces the state the racing waker would otherwise produce. + */ + +typedef struct { + QemuCoSleep w; + bool completed; +} CoSleepWakeData; + +static void coroutine_fn co_sleep_wake_entry(void *opaque) +{ + CoSleepWakeData *d = opaque; + + /* + * The wake was already delivered before we got here. qemu_co_sleep() + * must consume it and return without yielding. + */ + qemu_co_sleep(&d->w); + d->completed = true; +} + +static void test_co_sleep_wake_before_sleep(void) +{ + CoSleepWakeData d = { .w = { 0 }, .completed = false }; + Coroutine *co = qemu_coroutine_create(co_sleep_wake_entry, &d); + + /* Waker runs first, while no sleeper is parked on w. */ + qemu_co_sleep_wake(&d.w); + + /* + * Entering runs qemu_co_sleep(), which consumes the pending wake and + * returns without yielding, so the coroutine runs straight to + * completion in this single enter. With the pre-fix primitive the wake + * is dropped, qemu_co_sleep() parks, and completed stays false. + */ + qemu_coroutine_enter(co); + + g_assert(d.completed); +} + +/* * Check that creation, enter, and return work */ @@ -660,6 +711,8 @@ g_test_add_func("/locking/co-mutex/lockable", test_co_mutex_lockable); g_test_add_func("/locking/co-rwlock/upgrade", test_co_rwlock_upgrade); g_test_add_func("/locking/co-rwlock/downgrade", test_co_rwlock_downgrade); + g_test_add_func("/locking/co-sleep/wake-before-sleep", + test_co_sleep_wake_before_sleep); if (g_test_perf()) { g_test_add_func("/perf/lifecycle", perf_lifecycle); g_test_add_func("/perf/nesting", perf_nesting); diff -Nru qemu-10.0.12+ds/ui/vnc.c qemu-10.0.13+ds/ui/vnc.c --- qemu-10.0.12+ds/ui/vnc.c 2026-07-25 01:10:13.000000000 +0300 +++ qemu-10.0.13+ds/ui/vnc.c 2026-08-26 22:31:26.000000000 +0300 @@ -3005,7 +3005,6 @@ y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT); x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT); - rows = MIN(VNC_STAT_RECT, height - y); rows = MIN(VNC_STAT_RECT, height - y); if (rows <= 0) { diff -Nru qemu-10.0.12+ds/util/qemu-coroutine-sleep.c qemu-10.0.13+ds/util/qemu-coroutine-sleep.c --- qemu-10.0.12+ds/util/qemu-coroutine-sleep.c 2026-07-25 01:10:14.000000000 +0300 +++ qemu-10.0.13+ds/util/qemu-coroutine-sleep.c 2026-08-26 22:31:26.000000000 +0300 @@ -18,20 +18,29 @@ static const char *qemu_co_sleep_ns__scheduled = "qemu_co_sleep_ns"; +/* + * Sentinel stored in QemuCoSleep::to_wake by qemu_co_sleep_wake() when no + * sleeper has parked yet. The next qemu_co_sleep() consumes it and returns + * without yielding, so a wake that races the arming of a sleep is never + * lost. + */ +#define QEMU_CO_SLEEP_PENDING ((Coroutine *)(uintptr_t)1) + void qemu_co_sleep_wake(QemuCoSleep *w) { Coroutine *co; - co = w->to_wake; - w->to_wake = NULL; - if (co) { - /* Write of schedule protected by barrier write in aio_co_schedule */ - const char *scheduled = qatomic_cmpxchg(&co->scheduled, - qemu_co_sleep_ns__scheduled, NULL); - - assert(scheduled == qemu_co_sleep_ns__scheduled); - aio_co_wake(co); + co = qatomic_xchg(&w->to_wake, QEMU_CO_SLEEP_PENDING); + if (co == NULL || co == QEMU_CO_SLEEP_PENDING) { + /* No sleeper, or a wake is already pending. */ + return; } + + /* Write of scheduled protected by barrier write in aio_co_schedule */ + const char *scheduled = qatomic_cmpxchg(&co->scheduled, + qemu_co_sleep_ns__scheduled, NULL); + assert(scheduled == qemu_co_sleep_ns__scheduled); + aio_co_wake(co); } static void co_sleep_cb(void *opaque) @@ -43,6 +52,7 @@ void coroutine_fn qemu_co_sleep(QemuCoSleep *w) { Coroutine *co = qemu_coroutine_self(); + Coroutine *prev; const char *scheduled = qatomic_cmpxchg(&co->scheduled, NULL, qemu_co_sleep_ns__scheduled); @@ -53,11 +63,23 @@ abort(); } - w->to_wake = co; + /* + * Publish ourselves as the sleeper. A wake delivered before we got here, + * or one racing this publish, leaves QEMU_CO_SLEEP_PENDING in to_wake; + * the cmpxchg then fails and we consume the wake without yielding. + */ + prev = qatomic_cmpxchg(&w->to_wake, NULL, co); + if (prev == QEMU_CO_SLEEP_PENDING) { + qatomic_set(&w->to_wake, NULL); + qatomic_set(&co->scheduled, NULL); + return; + } + assert(prev == NULL); + qemu_coroutine_yield(); - /* w->to_wake is cleared before resuming this coroutine. */ - assert(w->to_wake == NULL); + /* The waker left QEMU_CO_SLEEP_PENDING; clear it for the next sleep. */ + qatomic_set(&w->to_wake, NULL); } void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, @@ -70,9 +92,10 @@ timer_mod(&ts, qemu_clock_get_ns(type) + ns); /* - * The timer will fire in the current AiOContext, so the callback - * must happen after qemu_co_sleep yields and there is no race - * between timer_mod and qemu_co_sleep. + * A wake racing with the arming of the sleep -- including the timer + * we just armed firing in another AioContext before qemu_co_sleep() + * publishes itself -- is captured by the sticky PENDING state in + * qemu_co_sleep_wake() and consumed here without yielding. */ qemu_co_sleep(w); timer_del(&ts);