Version in base suite: 10.0.11+ds-0+deb13u1 Version in overlay suite: 10.0.11+ds-0+deb13u1 Base version: qemu_10.0.11+ds-0+deb13u1 Target version: qemu_10.0.13+ds-0+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/q/qemu/qemu_10.0.11+ds-0+deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/q/qemu/qemu_10.0.13+ds-0+deb13u1.dsc /srv/release.debian.org/tmp/Ke1pPJsG5l/qemu-10.0.13+ds/tests/data/acpi/riscv64/virt/SPCR |binary qemu-10.0.13+ds/.gitlab-ci.d/container-template.yml | 2 qemu-10.0.13+ds/VERSION | 2 qemu-10.0.13+ds/accel/tcg/tb-maint.c | 43 qemu-10.0.13+ds/accel/tcg/user-exec.c | 41 qemu-10.0.13+ds/backends/rng.c | 21 qemu-10.0.13+ds/block/accounting.c | 20 qemu-10.0.13+ds/block/cloop.c | 3 qemu-10.0.13+ds/block/dmg.c | 36 qemu-10.0.13+ds/block/qapi.c | 3 qemu-10.0.13+ds/block/qcow2.c | 6 qemu-10.0.13+ds/contrib/vhost-user-gpu/vhost-user-gpu.c | 29 qemu-10.0.13+ds/contrib/vhost-user-gpu/virgl.c | 7 qemu-10.0.13+ds/contrib/vhost-user-gpu/vugbm.c | 11 qemu-10.0.13+ds/contrib/vhost-user-gpu/vugpu.h | 10 qemu-10.0.13+ds/debian/changelog | 493 +++++++++- qemu-10.0.13+ds/debian/control.mk | 2 qemu-10.0.13+ds/disas/riscv-xthead.c | 8 qemu-10.0.13+ds/disas/riscv.c | 68 - qemu-10.0.13+ds/disas/riscv.h | 1 qemu-10.0.13+ds/fsdev/file-op-9p.h | 12 qemu-10.0.13+ds/fsdev/qemu-fsdev-opts.c | 6 qemu-10.0.13+ds/fsdev/qemu-fsdev.c | 2 qemu-10.0.13+ds/hw/9pfs/9p-local.c | 43 qemu-10.0.13+ds/hw/9pfs/9p-synth.c | 57 + qemu-10.0.13+ds/hw/9pfs/9p.c | 127 ++ qemu-10.0.13+ds/hw/9pfs/9p.h | 2 qemu-10.0.13+ds/hw/9pfs/virtio-9p-device.c | 18 qemu-10.0.13+ds/hw/9pfs/xen-9p-backend.c | 48 qemu-10.0.13+ds/hw/acpi/aml-build.c | 4 qemu-10.0.13+ds/hw/arm/Kconfig | 3 qemu-10.0.13+ds/hw/arm/npcm7xx.c | 2 qemu-10.0.13+ds/hw/audio/intel-hda.c | 9 qemu-10.0.13+ds/hw/block/pflash_cfi01.c | 10 qemu-10.0.13+ds/hw/char/sclpconsole-lm.c | 3 qemu-10.0.13+ds/hw/char/serial.c | 1 qemu-10.0.13+ds/hw/char/virtio-serial-bus.c | 34 qemu-10.0.13+ds/hw/core/qdev-clock.c | 9 qemu-10.0.13+ds/hw/display/exynos4210_fimd.c | 54 - qemu-10.0.13+ds/hw/display/qxl-render.c | 32 qemu-10.0.13+ds/hw/display/qxl.c | 18 qemu-10.0.13+ds/hw/display/qxl.h | 1 qemu-10.0.13+ds/hw/display/sm501.c | 64 + qemu-10.0.13+ds/hw/display/vga.c | 7 qemu-10.0.13+ds/hw/display/vhost-user-gpu.c | 38 qemu-10.0.13+ds/hw/display/virtio-gpu-rutabaga.c | 52 - qemu-10.0.13+ds/hw/display/virtio-gpu-udmabuf.c | 1 qemu-10.0.13+ds/hw/display/virtio-gpu-virgl.c | 28 qemu-10.0.13+ds/hw/display/virtio-gpu.c | 174 ++- qemu-10.0.13+ds/hw/display/vmware_vga.c | 6 qemu-10.0.13+ds/hw/dma/i8257.c | 13 qemu-10.0.13+ds/hw/i386/intel_iommu.c | 9 qemu-10.0.13+ds/hw/ide/ahci.c | 3 qemu-10.0.13+ds/hw/ide/atapi.c | 180 +-- qemu-10.0.13+ds/hw/ide/core.c | 17 qemu-10.0.13+ds/hw/intc/loongarch_pch_pic.c | 20 qemu-10.0.13+ds/hw/mem/cxl_type3.c | 3 qemu-10.0.13+ds/hw/misc/applesmc.c | 6 qemu-10.0.13+ds/hw/misc/edu.c | 24 qemu-10.0.13+ds/hw/misc/ivshmem-pci.c | 6 qemu-10.0.13+ds/hw/misc/stm32_rcc.c | 31 qemu-10.0.13+ds/hw/net/cadence_gem.c | 17 qemu-10.0.13+ds/hw/net/e1000e_core.c | 18 qemu-10.0.13+ds/hw/net/fsl_etsec/rings.c | 25 qemu-10.0.13+ds/hw/net/igb_core.c | 2 qemu-10.0.13+ds/hw/net/net_tx_pkt.c | 7 qemu-10.0.13+ds/hw/net/rtl8139.c | 6 qemu-10.0.13+ds/hw/net/virtio-net.c | 54 - qemu-10.0.13+ds/hw/net/vmxnet3.c | 36 qemu-10.0.13+ds/hw/net/xilinx_axienet.c | 36 qemu-10.0.13+ds/hw/nvme/Kconfig | 1 qemu-10.0.13+ds/hw/nvme/ctrl.c | 92 + qemu-10.0.13+ds/hw/nvme/nvme.h | 9 qemu-10.0.13+ds/hw/pci-bridge/cxl_upstream.c | 3 qemu-10.0.13+ds/hw/pci-host/q35.c | 110 +- qemu-10.0.13+ds/hw/pci/pcie_doe.c | 27 qemu-10.0.13+ds/hw/riscv/riscv-iommu-bits.h | 1 qemu-10.0.13+ds/hw/riscv/riscv-iommu-sys.c | 16 qemu-10.0.13+ds/hw/riscv/riscv-iommu.c | 200 +++- qemu-10.0.13+ds/hw/riscv/riscv-iommu.h | 1 qemu-10.0.13+ds/hw/riscv/virt-acpi-build.c | 2 qemu-10.0.13+ds/hw/s390x/css.c | 24 qemu-10.0.13+ds/hw/s390x/event-facility.c | 6 qemu-10.0.13+ds/hw/s390x/ipl.h | 6 qemu-10.0.13+ds/hw/s390x/s390-pci-inst.c | 32 qemu-10.0.13+ds/hw/s390x/sclp.c | 25 qemu-10.0.13+ds/hw/scsi/mptsas.c | 4 qemu-10.0.13+ds/hw/scsi/scsi-bus.c | 7 qemu-10.0.13+ds/hw/scsi/scsi-disk.c | 23 qemu-10.0.13+ds/hw/scsi/virtio-scsi.c | 2 qemu-10.0.13+ds/hw/scsi/vmw_pvscsi.c | 57 + qemu-10.0.13+ds/hw/scsi/vmw_pvscsi.h | 14 qemu-10.0.13+ds/hw/sparc64/niagara.c | 2 qemu-10.0.13+ds/hw/uefi/var-service-auth.c | 7 qemu-10.0.13+ds/hw/uefi/var-service-core.c | 14 qemu-10.0.13+ds/hw/uefi/var-service-json.c | 5 qemu-10.0.13+ds/hw/uefi/var-service-policy.c | 68 - qemu-10.0.13+ds/hw/uefi/var-service-siglist.c | 3 qemu-10.0.13+ds/hw/uefi/var-service-vars.c | 19 qemu-10.0.13+ds/hw/ufs/lu.c | 5 qemu-10.0.13+ds/hw/usb/core.c | 10 qemu-10.0.13+ds/hw/usb/dev-uas.c | 48 qemu-10.0.13+ds/hw/usb/dev-wacom.c | 4 qemu-10.0.13+ds/hw/usb/hcd-ohci.c | 10 qemu-10.0.13+ds/hw/usb/hcd-xhci-pci.c | 12 qemu-10.0.13+ds/hw/usb/hcd-xhci-sysbus.c | 1 qemu-10.0.13+ds/hw/usb/hcd-xhci.c | 38 qemu-10.0.13+ds/hw/usb/redirect.c | 42 qemu-10.0.13+ds/hw/vfio/igd.c | 115 ++ qemu-10.0.13+ds/hw/vfio/pci-quirks.c | 7 qemu-10.0.13+ds/hw/vfio/pci.c | 50 - qemu-10.0.13+ds/hw/vfio/pci.h | 3 qemu-10.0.13+ds/hw/vfio/trace-events | 1 qemu-10.0.13+ds/hw/virtio/vdpa-dev.c | 5 qemu-10.0.13+ds/hw/virtio/vhost-user.c | 3 qemu-10.0.13+ds/hw/virtio/vhost.c | 5 qemu-10.0.13+ds/hw/virtio/virtio-iommu.c | 29 qemu-10.0.13+ds/hw/virtio/virtio-pmem.c | 17 qemu-10.0.13+ds/hw/virtio/virtio-rng.c | 2 qemu-10.0.13+ds/hw/virtio/virtio.c | 19 qemu-10.0.13+ds/hw/watchdog/spapr_watchdog.c | 16 qemu-10.0.13+ds/include/block/accounting.h | 1 qemu-10.0.13+ds/include/exec/cpu-all.h | 10 qemu-10.0.13+ds/include/exec/memory.h | 2 qemu-10.0.13+ds/include/hw/elf_ops.h.inc | 5 qemu-10.0.13+ds/include/hw/i2c/bcm2835_i2c.h | 2 qemu-10.0.13+ds/include/hw/misc/stm32_rcc.h | 6 qemu-10.0.13+ds/include/hw/s390x/css.h | 1 qemu-10.0.13+ds/include/hw/scsi/scsi.h | 1 qemu-10.0.13+ds/include/hw/virtio/virtio-gpu.h | 18 qemu-10.0.13+ds/include/hw/virtio/virtio-iommu.h | 1 qemu-10.0.13+ds/include/hw/virtio/virtio-pmem.h | 1 qemu-10.0.13+ds/include/net/net.h | 23 qemu-10.0.13+ds/include/qemu/coroutine.h | 17 qemu-10.0.13+ds/include/system/rng.h | 14 qemu-10.0.13+ds/include/tcg/tcg-op-common.h | 2 qemu-10.0.13+ds/include/tcg/tcg-op.h | 2 qemu-10.0.13+ds/linux-user/elfload.c | 37 qemu-10.0.13+ds/linux-user/sh4/cpu_loop.c | 7 qemu-10.0.13+ds/linux-user/sh4/signal.c | 6 qemu-10.0.13+ds/linux-user/sh4/target_mman.h | 2 qemu-10.0.13+ds/linux-user/strace.c | 2 qemu-10.0.13+ds/linux-user/strace.list | 5 qemu-10.0.13+ds/linux-user/syscall.c | 120 +- qemu-10.0.13+ds/linux-user/syscall_defs.h | 15 qemu-10.0.13+ds/meson.build | 5 qemu-10.0.13+ds/migration/multifd-qatzip.c | 5 qemu-10.0.13+ds/migration/multifd-qpl.c | 24 qemu-10.0.13+ds/migration/multifd-uadk.c | 24 qemu-10.0.13+ds/migration/multifd-zlib.c | 11 qemu-10.0.13+ds/migration/multifd-zstd.c | 11 qemu-10.0.13+ds/net/filter-rewriter.c | 2 qemu-10.0.13+ds/net/net.c | 2 qemu-10.0.13+ds/pc-bios/s390-ccw/bootmap.c | 6 qemu-10.0.13+ds/pc-bios/s390-ccw/helper.h | 10 qemu-10.0.13+ds/pc-bios/s390-ccw/menu.c | 42 qemu-10.0.13+ds/pc-bios/s390-ccw/s390-ccw.h | 2 qemu-10.0.13+ds/qemu-options.hx | 28 qemu-10.0.13+ds/replay/replay-debugging.c | 23 qemu-10.0.13+ds/subprojects/libvduse/libvduse.c | 5 qemu-10.0.13+ds/subprojects/libvhost-user/libvhost-user.c | 61 + qemu-10.0.13+ds/system/vl.c | 7 qemu-10.0.13+ds/target/arm/tcg/tlb-insns.c | 2 qemu-10.0.13+ds/target/i386/hvf/x86_decode.c | 6 qemu-10.0.13+ds/target/i386/ops_sse.h | 16 qemu-10.0.13+ds/target/i386/sev.c | 4 qemu-10.0.13+ds/target/i386/tcg/decode-new.c.inc | 46 qemu-10.0.13+ds/target/i386/tcg/fpu_helper.c | 92 - qemu-10.0.13+ds/target/i386/tcg/seg_helper.c | 20 qemu-10.0.13+ds/target/loongarch/tcg/insn_trans/trans_branch.c.inc | 2 qemu-10.0.13+ds/target/riscv/cpu.c | 8 qemu-10.0.13+ds/target/riscv/cpu_helper.c | 8 qemu-10.0.13+ds/target/riscv/csr.c | 93 + qemu-10.0.13+ds/target/riscv/insn_trans/trans_rvf.c.inc | 6 qemu-10.0.13+ds/target/riscv/insn_trans/trans_rvzfh.c.inc | 2 qemu-10.0.13+ds/target/riscv/insn_trans/trans_rvzicfiss.c.inc | 2 qemu-10.0.13+ds/target/riscv/op_helper.c | 20 qemu-10.0.13+ds/target/s390x/ioinst.c | 20 qemu-10.0.13+ds/target/s390x/kvm/kvm.c | 9 qemu-10.0.13+ds/target/s390x/tcg/crypto_helper.c | 56 - qemu-10.0.13+ds/target/s390x/tcg/insn-data.h.inc | 4 qemu-10.0.13+ds/target/s390x/tcg/int_helper.c | 3 qemu-10.0.13+ds/target/s390x/tcg/translate.c | 2 qemu-10.0.13+ds/target/sh4/cpu-param.h | 7 qemu-10.0.13+ds/target/sh4/op_helper.c | 2 qemu-10.0.13+ds/target/sh4/translate.c | 15 qemu-10.0.13+ds/target/sparc/machine.c | 40 qemu-10.0.13+ds/tcg/loongarch64/tcg-target.c.inc | 85 + qemu-10.0.13+ds/tcg/optimize.c | 13 qemu-10.0.13+ds/tcg/region.c | 42 qemu-10.0.13+ds/tcg/tcg-internal.h | 2 qemu-10.0.13+ds/tcg/tcg-op-gvec.c | 14 qemu-10.0.13+ds/tcg/tcg-op.c | 20 qemu-10.0.13+ds/tcg/tcg.c | 24 qemu-10.0.13+ds/tests/qemu-iotests/039 | 11 qemu-10.0.13+ds/tests/qemu-iotests/039.out | 3 qemu-10.0.13+ds/tests/qemu-iotests/testrunner.py | 4 qemu-10.0.13+ds/tests/qtest/ahci-test.c | 201 ++++ qemu-10.0.13+ds/tests/qtest/ide-test.c | 209 ++-- qemu-10.0.13+ds/tests/qtest/libqos/ahci.c | 5 qemu-10.0.13+ds/tests/qtest/libqos/ahci.h | 2 qemu-10.0.13+ds/tests/qtest/libqos/virtio-9p-client.c | 124 ++ qemu-10.0.13+ds/tests/qtest/libqos/virtio-9p-client.h | 88 + qemu-10.0.13+ds/tests/qtest/libqos/virtio-9p.c | 6 qemu-10.0.13+ds/tests/qtest/libqos/virtio-9p.h | 6 qemu-10.0.13+ds/tests/qtest/ufs-test.c | 19 qemu-10.0.13+ds/tests/qtest/usb-hcd-xhci-test.c | 67 + qemu-10.0.13+ds/tests/qtest/virtio-9p-test.c | 262 +++++ qemu-10.0.13+ds/tests/tcg/i386/test-i386-opt-shr.c | 21 qemu-10.0.13+ds/tests/tcg/s390x/Makefile.target | 2 qemu-10.0.13+ds/tests/tcg/s390x/div.c | 37 qemu-10.0.13+ds/tests/tcg/s390x/prno-trng.c | 67 + qemu-10.0.13+ds/tests/tcg/s390x/stckf.c | 44 qemu-10.0.13+ds/tests/tcg/x86_64/Makefile.target | 1 qemu-10.0.13+ds/tests/tcg/x86_64/segment-prefixes.c | 25 qemu-10.0.13+ds/tests/unit/meson.build | 1 qemu-10.0.13+ds/tests/unit/test-block-accounting.c | 115 ++ qemu-10.0.13+ds/tests/unit/test-coroutine.c | 53 + qemu-10.0.13+ds/ui/gtk-egl.c | 6 qemu-10.0.13+ds/ui/gtk-gl-area.c | 23 qemu-10.0.13+ds/ui/gtk.c | 4 qemu-10.0.13+ds/ui/input-barrier.c | 2 qemu-10.0.13+ds/ui/vnc.c | 25 qemu-10.0.13+ds/util/qemu-coroutine-sleep.c | 53 - 224 files changed, 5052 insertions(+), 1093 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpmshp7hu8/qemu_10.0.11+ds-0+deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpmshp7hu8/qemu_10.0.13+ds-0+deb13u1.dsc: no acceptable signature found diff -Nru qemu-10.0.11+ds/.gitlab-ci.d/container-template.yml qemu-10.0.13+ds/.gitlab-ci.d/container-template.yml --- qemu-10.0.11+ds/.gitlab-ci.d/container-template.yml 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/.gitlab-ci.d/container-template.yml 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/VERSION qemu-10.0.13+ds/VERSION --- qemu-10.0.11+ds/VERSION 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/VERSION 2026-08-26 19:31:25.000000000 +0000 @@ -1 +1 @@ -10.0.11 +10.0.13 diff -Nru qemu-10.0.11+ds/accel/tcg/tb-maint.c qemu-10.0.13+ds/accel/tcg/tb-maint.c --- qemu-10.0.11+ds/accel/tcg/tb-maint.c 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/accel/tcg/tb-maint.c 2026-08-26 19:31:25.000000000 +0000 @@ -921,6 +921,7 @@ uint32_t orig_cflags = tb_cflags(tb); assert_memory_lock(); + qemu_thread_jit_write(); /* make sure no further incoming jumps will be chained to this TB */ qemu_spin_lock(&tb->jmp_lock); @@ -931,33 +932,27 @@ phys_pc = tb_page_addr0(tb); h = tb_hash_func(phys_pc, (orig_cflags & CF_PCREL ? 0 : tb->pc), tb->flags, tb->cs_base, orig_cflags); - if (!qht_remove(&tb_ctx.htable, tb, h)) { - return; - } + if (qht_remove(&tb_ctx.htable, tb, h)) { - /* remove the TB from the page list */ - if (rm_from_page_list) { - tb_remove(tb); - } + /* remove the TB from the page list */ + if (rm_from_page_list) { + tb_remove(tb); + } - /* remove the TB from the hash list */ - tb_jmp_cache_inval_tb(tb); + /* remove the TB from the hash list */ + tb_jmp_cache_inval_tb(tb); - /* suppress this TB from the two jump lists */ - tb_remove_from_jmp_list(tb, 0); - tb_remove_from_jmp_list(tb, 1); + /* suppress this TB from the two jump lists */ + tb_remove_from_jmp_list(tb, 0); + tb_remove_from_jmp_list(tb, 1); - /* suppress any remaining jumps to this TB */ - tb_jmp_unlink(tb); + /* suppress any remaining jumps to this TB */ + tb_jmp_unlink(tb); - qatomic_set(&tb_ctx.tb_phys_invalidate_count, - tb_ctx.tb_phys_invalidate_count + 1); -} + qatomic_set(&tb_ctx.tb_phys_invalidate_count, + tb_ctx.tb_phys_invalidate_count + 1); + } -static void tb_phys_invalidate__locked(TranslationBlock *tb) -{ - qemu_thread_jit_write(); - do_tb_phys_invalidate(tb, true); qemu_thread_jit_execute(); } @@ -1025,7 +1020,7 @@ assert_memory_lock(); PAGE_FOR_EACH_TB(start, last, unused, tb, n) { - tb_phys_invalidate__locked(tb); + do_tb_phys_invalidate(tb, true); } } @@ -1090,7 +1085,7 @@ current_tb_modified = true; cpu_restore_state_from_tb(current_cpu, current_tb, pc); } - tb_phys_invalidate__locked(tb); + do_tb_phys_invalidate(tb, true); } if (current_tb_modified) { @@ -1153,7 +1148,7 @@ cpu_restore_state_from_tb(current_cpu, current_tb, retaddr); } #endif /* TARGET_HAS_PRECISE_SMC */ - tb_phys_invalidate__locked(tb); + do_tb_phys_invalidate(tb, true); } } diff -Nru qemu-10.0.11+ds/accel/tcg/user-exec.c qemu-10.0.13+ds/accel/tcg/user-exec.c --- qemu-10.0.11+ds/accel/tcg/user-exec.c 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/accel/tcg/user-exec.c 2026-08-26 19:31:25.000000000 +0000 @@ -222,13 +222,16 @@ int page_get_flags(target_ulong address) { - PageFlagsNode *p = pageflags_find(address, address); + PageFlagsNode *p; + + RCU_READ_LOCK_GUARD(); /* * See util/interval-tree.c re lockless lookups: no false positives but * there are false negatives. If we find nothing, retry with the mmap * lock acquired. */ + p = pageflags_find(address, address); if (p) { return p->flags; } @@ -327,15 +330,15 @@ if (prev) { if (next) { - prev->itree.last = next->itree.last; + pageflags_create(prev->itree.start, next->itree.last, flags); g_free_rcu(next, rcu); } else { - prev->itree.last = last; + pageflags_create(prev->itree.start, last, flags); } - interval_tree_insert(&prev->itree, &pageflags_root); + g_free_rcu(prev, rcu); } else if (next) { - next->itree.start = start; - interval_tree_insert(&next->itree, &pageflags_root); + pageflags_create(start, next->itree.last, flags); + g_free_rcu(next, rcu); } else { pageflags_create(start, last, flags); } @@ -405,8 +408,8 @@ if (set_flags != merge_flags) { if (p_start < start) { interval_tree_remove(&p->itree, &pageflags_root); - p->itree.last = start - 1; - interval_tree_insert(&p->itree, &pageflags_root); + pageflags_create(p_start, start - 1, p_flags); + g_free_rcu(p, rcu); if (last < p_last) { if (merge_flags) { @@ -428,11 +431,11 @@ } if (last < p_last) { interval_tree_remove(&p->itree, &pageflags_root); - p->itree.start = last + 1; - interval_tree_insert(&p->itree, &pageflags_root); + pageflags_create(last + 1, p_last, p_flags); if (merge_flags) { pageflags_create(start, last, merge_flags); } + g_free_rcu(p, rcu); } else { if (merge_flags) { p->flags = merge_flags; @@ -453,8 +456,8 @@ if (set_flags == p_flags) { if (start < p_start) { interval_tree_remove(&p->itree, &pageflags_root); - p->itree.start = start; - interval_tree_insert(&p->itree, &pageflags_root); + pageflags_create(start, p_last, p_flags); + g_free_rcu(p, rcu); } if (p_last < last) { start = p_last + 1; @@ -466,8 +469,8 @@ /* Maybe split out head and/or tail ranges with the original flags. */ interval_tree_remove(&p->itree, &pageflags_root); if (p_start < start) { - p->itree.last = start - 1; - interval_tree_insert(&p->itree, &pageflags_root); + pageflags_create(p_start, start - 1, p_flags); + g_free_rcu(p, rcu); if (p_last < last) { goto restart; @@ -476,8 +479,8 @@ pageflags_create(last + 1, p_last, p_flags); } } else if (last < p_last) { - p->itree.start = last + 1; - interval_tree_insert(&p->itree, &pageflags_root); + pageflags_create(last + 1, p_last, p_flags); + g_free_rcu(p, rcu); } else { g_free_rcu(p, rcu); goto restart; @@ -545,6 +548,8 @@ return false; /* wrap around */ } + RCU_READ_LOCK_GUARD(); + locked = have_mmap_lock(); while (true) { PageFlagsNode *p = pageflags_find(start, last); @@ -807,7 +812,7 @@ if (page_flags & acc_flag) { if (access_type != MMU_INST_FETCH && cpu_plugin_mem_cbs_enabled(env_cpu(env))) { - return TLB_MMIO; + return TLB_FORCE_SLOW; } return 0; /* success */ } @@ -842,7 +847,7 @@ g_assert(-(addr | TARGET_PAGE_MASK) >= size); flags = probe_access_internal(env, addr, size, access_type, false, ra); - g_assert((flags & ~TLB_MMIO) == 0); + g_assert((flags & ~TLB_FORCE_SLOW) == 0); return size ? g2h(env_cpu(env), addr) : NULL; } diff -Nru qemu-10.0.11+ds/backends/rng.c qemu-10.0.13+ds/backends/rng.c --- qemu-10.0.11+ds/backends/rng.c 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/backends/rng.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/block/accounting.c qemu-10.0.13+ds/block/accounting.c --- qemu-10.0.11+ds/block/accounting.c 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/block/accounting.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/block/cloop.c qemu-10.0.13+ds/block/cloop.c --- qemu-10.0.11+ds/block/cloop.c 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/block/cloop.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/block/dmg.c qemu-10.0.13+ds/block/dmg.c --- qemu-10.0.11+ds/block/dmg.c 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/block/dmg.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/block/qapi.c qemu-10.0.13+ds/block/qapi.c --- qemu-10.0.11+ds/block/qapi.c 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/block/qapi.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/block/qcow2.c qemu-10.0.13+ds/block/qcow2.c --- qemu-10.0.11+ds/block/qcow2.c 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/block/qcow2.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+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.11+ds/contrib/vhost-user-gpu/vhost-user-gpu.c 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/contrib/vhost-user-gpu/vhost-user-gpu.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/contrib/vhost-user-gpu/virgl.c qemu-10.0.13+ds/contrib/vhost-user-gpu/virgl.c --- qemu-10.0.11+ds/contrib/vhost-user-gpu/virgl.c 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/contrib/vhost-user-gpu/virgl.c 2026-08-26 19:31:25.000000000 +0000 @@ -202,6 +202,13 @@ VUGPU_FILL_CMD(cs); + if (cs.size > VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE) { + g_critical("%s: command buffer too large (%u)", + __func__, cs.size); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + buf = g_malloc(cs.size); s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(cs), buf, cs.size); diff -Nru qemu-10.0.11+ds/contrib/vhost-user-gpu/vugbm.c qemu-10.0.13+ds/contrib/vhost-user-gpu/vugbm.c --- qemu-10.0.11+ds/contrib/vhost-user-gpu/vugbm.c 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/contrib/vhost-user-gpu/vugbm.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/contrib/vhost-user-gpu/vugpu.h qemu-10.0.13+ds/contrib/vhost-user-gpu/vugpu.h --- qemu-10.0.11+ds/contrib/vhost-user-gpu/vugpu.h 2026-06-25 21:39:13.000000000 +0000 +++ qemu-10.0.13+ds/contrib/vhost-user-gpu/vugpu.h 2026-08-26 19:31:25.000000000 +0000 @@ -22,6 +22,7 @@ #include "qemu/queue.h" #include "qemu/iov.h" #include "qemu/bswap.h" +#include "qemu/units.h" #include "vugbm.h" typedef enum VhostUserGpuRequest { @@ -163,6 +164,14 @@ QTAILQ_ENTRY(virtio_gpu_ctrl_command) next; }; +/* + * With 4 KiB pages and QEMU's VIRTQUEUE_MAX_SIZE (1024) mapped-iov + * limit, the largest inline command is ~4 MiB. Cap submit_3d + * allocations to this value to prevent a malicious guest from + * triggering an OOM abort via an inflated cs.size field. + */ +#define VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE (4 * MiB) + #define VUGPU_FILL_CMD(out) do { \ size_t vugpufillcmd_s_ = \ iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \ @@ -170,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.11+ds/debian/changelog qemu-10.0.13+ds/debian/changelog --- qemu-10.0.11+ds/debian/changelog 2026-06-28 05:33:17.000000000 +0000 +++ qemu-10.0.13+ds/debian/changelog 2026-08-29 17:55:40.000000000 +0000 @@ -1,8 +1,490 @@ +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 Sat, 29 Aug 2026 20:55:40 +0300 + +qemu (1:10.0.12+ds-0+deb13u1) trixie; urgency=medium + + * new upstream stable/bugfix release: + - Update version for 10.0.12 release + - hw/audio/intel-hda: restrict all DMA engine paths to memories + (update to CVE-2021-3611) + - hw/net/cadence: Return current Cadence GEM queue pointers + - hw/misc/applesmc: Fix a typo setting MSSD key + - replay: fix use of uninitialized pointer on error + - hw/display/qxl: validate monitors_config heads[] in phys2virt + https://gitlab.com/qemu-project/qemu/-/work_items/4027 + - net: Correct padding check in qemu_receive_packet() + - hw/net/xilinx_axienet: Fix PHY register 17 link status reporting + - hw/usb/hcd-xhci-sysbus: Fix OOB heap access in xhci_sysbus_intr_raise() + https://gitlab.com/qemu-project/qemu/-/work_items/4001 + (Closes: CVE-2026-16043) + - hw/usb/hcd-xhci: Fix guest-triggerable assert() in xhci_find_stream() + https://gitlab.com/qemu-project/qemu/-/work_items/273, 2020-06-16 + - usbredir: fix infinite loop and SIGFPE with zero max_packet_size + https://gitlab.com/qemu-project/qemu/-/work_items/3995 + (Closes: CVE-2026-63319) + - usbredir: fix use-after-free on buffered bulk packet overflow + https://gitlab.com/qemu-project/qemu/-/work_items/3808 + (Closes: CVE-2026-15705) + - tests/qtest: add xhci-pci unplug finalize regression test + - hw/usb/hcd-xhci-pci: break host link cycle so device_finalize() + runs on unplug + - hw/usb/xhci: clamp interval exponent to avoid UB shift + in xhci_init_epctx() + https://gitlab.com/qemu-project/qemu/-/work_items/3703 + - accel/tcg: move jit thread manipulation into do_tb_phys_invalidate + https://gitlab.com/qemu-project/qemu/-/work_items/3444 + - hw/display/virtio-gpu: Check pixman_image_create_bits() results + - hw/display/virtio-gpu: handle migration iov allocation failure + https://gitlab.com/qemu-project/qemu/-/work_items/3753 + - hw/display/virtio-gpu: cap submit_3d command buffer allocation + https://gitlab.com/qemu-project/qemu/-/work_items/3776 + - ui/vnc: validate SetPixelFormat field ranges + https://gitlab.com/qemu-project/qemu/-/issues/3976 + (Closes: CVE-2026-15578) + - ui/vnc: fix out-of-bounds write in lossy refresh dirty marking + https://gitlab.com/qemu-project/qemu/-/work_items/3935 + (Closes: CVE-2026-61475) + - ui/gtk: Narrow DMA-BUF critical section + - ui/input-barrier: fix off-by-one in keycode bounds check + https://gitlab.com/qemu-project/qemu/-/issues/3951 + - ui/vnc: validate color shifts in SetPixelFormat + https://gitlab.com/qemu-project/qemu/-/work_items/3948 + - ui/vnc: fix OOB write in vnc_refresh_lossy_rect + https://gitlab.com/qemu-project/qemu/-/work_items/3950 + (Closes: CVE-2026-48002) + - hw/usb/hcd-xhci: Turn guest-triggerable abort() into qemu_log_mask() + https://gitlab.com/qemu-project/qemu/-/work_items/3784 + - hw/usb/hcd-ohci: Make sure that ohci_service_ed_list() cannot loop forever + https://gitlab.com/qemu-project/qemu/-/work_items/3781 + - hw/display/virtio-gpu: fix dmabuf_fd leak on remap failure + - hw/scsi/vmw_pvscsi: add a comment to explain the endianness + - hw/scsi/vmw_pvscsi: translate data endianness + - hw/sparc64/niagara: use int64_t for vdisk size to avoid truncation + - hw/display/qxl: fix TOCTOU in cursor chunk data_size handling + https://gitlab.com/qemu-project/qemu/-/work_items/3757 + - hw/misc/ivshmem: clear chardev handlers before freeing peers + https://gitlab.com/qemu-project/qemu/-/work_items/3594 + - linux-user/alpha: populate AT_HWCAP from env->amask + - linux-user/alpha: add coredump support + - s390x/css: firm up handling of chained TIC CCWs + - s390x/sclp: prevent re-reading the sclp header + - hw/s390x/sclp: Do not ignore address_space_read/write() errors + - hw/s390x/sclp: Replace [cpu_physical_memory -> address_space]_r/w() + - hw/misc/stm32_rcc: Correct offset-to-irq calculation + - hw/display/sm501: Don't allow guest to set ram size larger than it is + https://gitlab.com/qemu-project/qemu/-/work_items/3811 + - hw/display/sm501: Avoid overflow problems in bounds check calculations + https://gitlab.com/qemu-project/qemu/-/work_items/3584 + - hw/display/sm501: Catch bad coordinates for RTL operations + https://gitlab.com/qemu-project/qemu/-/work_items/3920 + - hw/usb/dev-wacom: Don't write off end of buffer + https://gitlab.com/qemu-project/qemu/-/work_items/3672 + - hw/net/vmxnet3: Correct bounds check on tx queue index + https://gitlab.com/qemu-project/qemu/-/work_items/3780 + - target/arm: Be more defensive for invalid tlbi_aa64_get_range + - linux-user: Validate guest-passed dm_ioctl data_size + https://gitlab.com/qemu-project/qemu/-/work_items/3736 + - tests: update SPCR loongarch64 and riscv64 test data + - hw/acpi: correct field sequence in SPCR table + https://bugs.launchpad.net/ubuntu/+source/qemu/+bug/2146419 + - tests: allow differences in SPCR + - hw/pci/pcie_doe: Check mailbox length for overflows + https://gitlab.com/qemu-project/qemu/-/work_items/3679 + - hw/riscv/riscv-iommu: Fix MemoryRegion owner + - hw/riscv/riscv-iommu.c: set ftype and iova in riscv_iommu_ctx() + https://gitlab.com/qemu-project/qemu/-/work_items/3564 + - hw/riscv: riscv-iommu: Don't look up DDT cache in Off and Bare modes + - hw/riscv/riscv-iommu: Avoid caching PCI device IDs + - hw/riscv/riscv-iommu: forbid GATE/SADE if caps.AMO_HWADD is zero + https://gitlab.com/qemu-project/qemu/-/work_items/3549 + - hw/riscv/riscv-iommu.c: update ioval2 when faulting in spa_fetch() + https://gitlab.com/qemu-project/qemu/-/work_items/3559 + - hw/riscv/riscv-iommu.c: check for misaligned IOHGATP_PPN + https://gitlab.com/qemu-project/qemu/-/work_items/3550 + - hw/riscv/riscv-iommu-sys.c: record fault on IOMMU-generated MSI write + https://gitlab.com/qemu-project/qemu/-/work_items/3572 + - hw/riscv/riscv-iommu.c: check reserved MSI PTE basic bits + https://gitlab.com/qemu-project/qemu/-/work_items/3563 + - hw/riscv/riscv-iommu.c: fault for non-user PTE in G_STAGE + https://gitlab.com/qemu-project/qemu/-/work_items/3555 + - hw/riscv/riscv-iommu.c: fault when !PTE_U and no priv access + https://gitlab.com/qemu-project/qemu/-/work_items/3553 + - hw/riscv/riscv-iommu.c: check for reserved PTE bits + https://gitlab.com/qemu-project/qemu/-/work_items/3554 + - hw/riscv/riscv-iommu.c: fix fault type for spa_fetch() faults + https://gitlab.com/qemu-project/qemu/-/work_items/3557 + https://gitlab.com/qemu-project/qemu/-/work_items/3577 + - hw/ufs: avoid double unref of wrapped scsi-hd + - target/sh4: fixup tcg for sh4 fipr/ftrv instructions + - tcg/loongarch64: Fix cmp_vec with TCG_COND_NE + https://gitlab.com/qemu-project/qemu/-/work_items/3589 + - tcg/loongarch64: Improve constraints for TCG_CT_CONST_VCMP + - tcg/loongarch64: Fix vec_val computation in tcg_target_const_match + - accel/tcg: Make PageFlagsNodes' start and last immutable + - accel/tcg: Use TLB_FORCE_SLOW not TLB_MMIO for user-only plugins + - hw/net: fix e1000e/igb ip_len inflation by Ethernet minimum-frame padding + - hw/core/qdev-clock: Fix potential null pointer dereference + https://gitlab.com/qemu-project/qemu/-/work_items/2342 + - vdpa: fix use-after-free of vqs in vhost_vdpa_device_unrealize + - hw/display/qxl: Fix mono cursor validation that can read + past a cursor chunk + https://gitlab.com/qemu-project/qemu/-/work_items/3646 + - hw/scsi/mptsas: Reset doorbell state on reset + https://gitlab.com/qemu-project/qemu/-/work_items/304, 2020-12-17 + - hw/dma/i8257: Return zeroes for read_memory in verify mode + https://gitlab.com/qemu-project/qemu/-/work_items/3487 + - tests/qtest/ahci: test ATAPI read completing after engine restart + - hw/ide/ahci: cancel in-flight buffered reads on command engine restart + - hw/misc/edu: restrict dma access to dma buffer + https://gitlab.com/qemu-project/qemu/-/work_items/3852 + - s390x/css: limit number of CHPIDs in description + - s390x/ioinst: Require strict length and format for SEI CHSC handler + - s390x/pci: Shrink RPCIT ranges to registered window + - s390x/pci: Tighten region detection for BAR read/write + - s390x/sclp: reject invalid write event data headers + - hw/nvme: ensure sgl forward progress + - hw/nvme: fix FDP set FDP events + https://gitlab.com/qemu-project/qemu/-/work_items/3631 + - hw/arm: use cortex-a9 mpcore base for CBAR on npcm7xx machines + - hw/net/fsl_etsec: validate FCB offsets in process_tx_fcb() + https://gitlab.com/qemu-project/qemu/-/work_items/3517 + - virtio-net: validate RSS indirections_len in post_load + - hw/char/virtio-serial-bus: fix guest-triggerable OOM in control_out() + https://gitlab.com/qemu-project/qemu/-/issues/3585 + - hw/riscv/riscv-iommu.c: always fault with SADE=0 and A=0 + https://gitlab.com/qemu-project/qemu/-/work_items/3551 + - target/riscv: Apply UXL WARL handling to vsstatus + - target/riscv: avoid abort when reading vtype before env->xl is set + https://gitlab.com/qemu-project/qemu/-/issues/3545 + - hw/riscv/virt-acpi-build: Fix RINTC PLIC context ID for KVM + - target/riscv: Check PMP before updating PTE + - hw/9pfs/local: harden local_fid_fd() on FID types + - hw/9pfs: fix invalid union access by v9fs_co_fstat() + - hw/9pfs: fix invalid union access by v9fs_co_fsync() + - tests/9p: add 3 xattr FID limit test cases (local fs driver) + - tests/9p: add 3 xattr FID limit test cases (synth fs driver) + - tests/9p: add virtio_9p_add_synth_driver_args() test client function + - tests/9p: increase P9_MAX_SIZE for test client + - hw/9pfs: add xattr count query interface to fs synth driver + - hw/9pfs: enable xattr (mockup) support for synth fs driver + - tests/9p: add Txattrcreate / Rxattrcreate test client functions + - tests/9p: add Tclunk / Rclunk test client functions + - tests/9p: add Tread / Rread test client functions + - qemu-options: document 9pfs max_xattr option + - hw/9pfs: add max_xattr option + - hw/9pfs: add xattr FID limit to prevent memory exhaustion + (Closes: CVE-2026-8348) + - hw/9pfs: cap Treaddir allocation + (Closes: CVE-2026-9238) + - 9pfs/xen: implement response_buffer_size callback + - 9pfs/virtio: implement response_buffer_size callback + - hw/9pfs: add response_buffer_size transport callback + - hw/9pfs: cap negotiated msize to transport limit + - 9pfs/xen: implement msize_limit callback + - 9pfs/virtio: implement msize_limit callback + - hw/9pfs: add msize_limit transport callback + - 9pfs: local : Introduce local_fid_fd() helper + - 9pfs: Don't use file descriptors in core code + - s390x/kvm: clamp stsi 3.2.2 size + - ui/gtk: fix bad widget realize on non-GFX VC + + -- Michael Tokarev Sat, 25 Jul 2026 09:35:02 +0300 + qemu (1:10.0.11+ds-0+deb13u1) trixie; urgency=medium * new upstream stable/bugfix release: - Update version for 10.0.11 release - - linux-user: Fix AT_PHDR when program headers are relocated into their own segment + - linux-user: Fix AT_PHDR when program headers are relocated + into their own segment - hw/pci: Replace assert with bounds check and return - ppc/pnv_phb3: Error out on invalid config access - linux-user/xtensa: fix unlock of uninitialized frame pointer on sigreturn @@ -45,7 +527,8 @@ - qemu-io: Add 'aio_discard' command - virtio-blk: add missing VIRTIO_BLK_T_SCSI_CMD size check (Closes: #1139923, CVE-2026-48914) - - block/io: fallback to bounce buffer if BLKZEROOUT is not supported because of alignment + - block/io: fallback to bounce buffer if BLKZEROOUT is not supported + because of alignment - s390x/pci: Fix interrupt forwarding disable for interpreted devices - target/s390x: Make container ids in SysIB_15x 1-based - tests/unit: add test-envlist covering setenv/unsetenv name matching @@ -54,9 +537,11 @@ (Closes: CVE-2026-48004) - tests/9pfs: add deep absolute path test - tests/qtest/libqos: add qvirtqueue_reset_pool() for descriptor pool reset - - hw/9pfs: let callers of v9fs_path_sprintf() and v9fs_fix_path() handle errors + - hw/9pfs: let callers of v9fs_path_sprintf() and v9fs_fix_path() + handle errors - hw/9pfs: add error handling to v9fs_fix_path() - - hw/9pfs: change V9fsPath.size to size_t and v9fs_path_sprintf() return type + - hw/9pfs: change V9fsPath.size to size_t and v9fs_path_sprintf() + return type - hw/9pfs: add NULL check in v9fs_path_is_ancestor() - hw/9pfs: move G_GNUC_PRINTF to header - linux-user/s390x: restore fpu_status rounding mode from FPC on sigreturn diff -Nru qemu-10.0.11+ds/debian/control.mk qemu-10.0.13+ds/debian/control.mk --- qemu-10.0.11+ds/debian/control.mk 2026-06-28 05:22:14.000000000 +0000 +++ qemu-10.0.13+ds/debian/control.mk 2026-08-29 17:55:40.000000000 +0000 @@ -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.11+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.11+ds/disas/riscv-xthead.c qemu-10.0.13+ds/disas/riscv-xthead.c --- qemu-10.0.11+ds/disas/riscv-xthead.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/disas/riscv-xthead.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/disas/riscv.c qemu-10.0.13+ds/disas/riscv.c --- qemu-10.0.11+ds/disas/riscv.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/disas/riscv.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/disas/riscv.h qemu-10.0.13+ds/disas/riscv.h --- qemu-10.0.11+ds/disas/riscv.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/disas/riscv.h 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/fsdev/file-op-9p.h qemu-10.0.13+ds/fsdev/file-op-9p.h --- qemu-10.0.11+ds/fsdev/file-op-9p.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/fsdev/file-op-9p.h 2026-08-26 19:31:25.000000000 +0000 @@ -79,6 +79,11 @@ #define V9FS_SEC_MASK 0x0000003C +/* + * Limits the maximum amount of simultaneously open xattr FIDs to prevent + * host memory exhaustion (as each xattr FID contains a xattr value buffer). + */ +#define V9FS_MAX_XATTR_DEFAULT 1024 typedef struct FileOperations FileOperations; typedef struct XattrOperations XattrOperations; @@ -94,6 +99,8 @@ FsThrottle fst; mode_t fmode; mode_t dmode; + /* temporary storage for parse_opts only */ + uint32_t max_xattr; } FsDriverEntry; struct FsContext { @@ -107,6 +114,10 @@ void *private; mode_t fmode; mode_t dmode; + /* max. amount of simultaneously open xattr FIDs */ + uint32_t xattr_fid_limit; + /* current amount of open xattr FIDs */ + uint32_t xattr_fid_count; }; struct V9fsPath { @@ -164,6 +175,7 @@ int (*renameat)(FsContext *ctx, V9fsPath *olddir, const char *old_name, V9fsPath *newdir, const char *new_name); int (*unlinkat)(FsContext *ctx, V9fsPath *dir, const char *name, int flags); + bool (*has_valid_file_handle)(int fid_type, V9fsFidOpenState *fs); }; #endif diff -Nru qemu-10.0.11+ds/fsdev/qemu-fsdev-opts.c qemu-10.0.13+ds/fsdev/qemu-fsdev-opts.c --- qemu-10.0.11+ds/fsdev/qemu-fsdev-opts.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/fsdev/qemu-fsdev-opts.c 2026-08-26 19:31:25.000000000 +0000 @@ -46,6 +46,9 @@ }, { .name = "dmode", .type = QEMU_OPT_NUMBER, + }, { + .name = "max_xattr", + .type = QEMU_OPT_NUMBER, }, THROTTLE_OPTS, @@ -92,6 +95,9 @@ }, { .name = "dmode", .type = QEMU_OPT_NUMBER, + }, { + .name = "max_xattr", + .type = QEMU_OPT_NUMBER, }, { /*End of list */ } diff -Nru qemu-10.0.11+ds/fsdev/qemu-fsdev.c qemu-10.0.13+ds/fsdev/qemu-fsdev.c --- qemu-10.0.11+ds/fsdev/qemu-fsdev.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/fsdev/qemu-fsdev.c 2026-08-26 19:31:25.000000000 +0000 @@ -45,7 +45,7 @@ static QTAILQ_HEAD(, FsDriverListEntry) fsdriver_entries = QTAILQ_HEAD_INITIALIZER(fsdriver_entries); -#define COMMON_FS_DRIVER_OPTIONS "id", "fsdriver", "readonly" +#define COMMON_FS_DRIVER_OPTIONS "id", "fsdriver", "readonly", "max_xattr" static FsDriverTable FsDrivers[] = { { diff -Nru qemu-10.0.11+ds/hw/9pfs/9p-local.c qemu-10.0.13+ds/hw/9pfs/9p-local.c --- qemu-10.0.11+ds/hw/9pfs/9p-local.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/9pfs/9p-local.c 2026-08-26 19:31:25.000000000 +0000 @@ -766,16 +766,22 @@ return err; } -static int local_fstat(FsContext *fs_ctx, int fid_type, - V9fsFidOpenState *fs, struct stat *stbuf) +static int local_fid_fd(int fid_type, V9fsFidOpenState *fs) { - int err, fd; - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir.stream); + return dirfd(fs->dir.stream); + } else if (fid_type == P9_FID_FILE) { + return fs->fd; } else { - fd = fs->fd; + errno = EBADF; + return -1; } +} + +static int local_fstat(FsContext *fs_ctx, int fid_type, + V9fsFidOpenState *fs, struct stat *stbuf) +{ + int err, fd = local_fid_fd(fid_type, fs); err = fstat(fd, stbuf); if (err) { @@ -1167,13 +1173,7 @@ static int local_fsync(FsContext *ctx, int fid_type, V9fsFidOpenState *fs, int datasync) { - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir.stream); - } else { - fd = fs->fd; - } + int fd = local_fid_fd(fid_type, fs); if (datasync) { return qemu_fdatasync(fd); @@ -1509,6 +1509,15 @@ const char *path = qemu_opt_get(opts, "path"); const char *multidevs = qemu_opt_get(opts, "multidevs"); + uint64_t val = qemu_opt_get_number(opts, "max_xattr", + V9FS_MAX_XATTR_DEFAULT); + if (val > UINT32_MAX) { + error_setg(errp, "max_xattr value '%s' too large", + qemu_opt_get(opts, "max_xattr")); + return -1; + } + fse->max_xattr = val; + if (!sec_model) { error_setg(errp, "security_model property not set"); error_append_security_model_hint(errp); @@ -1584,6 +1593,13 @@ return 0; } +static bool local_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs) +{ + return + (fid_type == P9_FID_FILE && fs->fd != -1) || + (fid_type == P9_FID_DIR && fs->dir.stream != NULL); +} + FileOperations local_ops = { .parse_opts = local_parse_opts, .init = local_init, @@ -1621,4 +1637,5 @@ .name_to_path = local_name_to_path, .renameat = local_renameat, .unlinkat = local_unlinkat, + .has_valid_file_handle = local_has_valid_file_handle, }; diff -Nru qemu-10.0.11+ds/hw/9pfs/9p-synth.c qemu-10.0.13+ds/hw/9pfs/9p-synth.c --- qemu-10.0.11+ds/hw/9pfs/9p-synth.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/9pfs/9p-synth.c 2026-08-26 19:31:25.000000000 +0000 @@ -25,6 +25,8 @@ #include "qemu/rcu_queue.h" #include "qemu/cutils.h" #include "system/qtest.h" +#include "qapi/error.h" +#include "qemu/option.h" /* Root node for synth file system */ static V9fsSynthNode synth_root = { @@ -461,15 +463,15 @@ const char *name, void *value, size_t size, int flags) { - errno = ENOTSUP; - return -1; + /* pretend it worked */ + return 0; } static int synth_lremovexattr(FsContext *ctx, V9fsPath *path, const char *name) { - errno = ENOTSUP; - return -1; + /* pretend it worked */ + return 0; } static int synth_name_to_path(FsContext *ctx, V9fsPath *dir_path, @@ -549,6 +551,19 @@ return 1; } +/* transmits internal xattr counter to client */ +static ssize_t v9fs_synth_read_xattr_count(void *buf, int len, off_t offset, + void *arg) +{ + FsContext *ctx = arg; + size_t local_count = ctx->xattr_fid_count; + if (len < (int)sizeof(size_t)) { + return -ENOSPC; + } + memcpy(buf, &local_count, sizeof(size_t)); + return sizeof(size_t); +} + static int synth_init(FsContext *ctx, Error **errp) { QLIST_INIT(&synth_root.child); @@ -610,12 +625,45 @@ g_free(name); } } + + /* Directory for internal statistic queries */ + { + V9fsSynthNode *stat_dir = NULL; + ret = qemu_v9fs_synth_mkdir(NULL, 0755, "stat", &stat_dir); + assert(!ret); + + /* File for internal xattr count query */ + ret = qemu_v9fs_synth_add_file(stat_dir, 0444, "xattr_count", + v9fs_synth_read_xattr_count, + NULL, ctx); + assert(!ret); + } + } + + return 0; +} + +static int synth_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) +{ + uint64_t val = qemu_opt_get_number(opts, "max_xattr", + V9FS_MAX_XATTR_DEFAULT); + if (val > UINT32_MAX) { + error_setg(errp, "max_xattr value '%s' too large", + qemu_opt_get(opts, "max_xattr")); + return -1; } + fse->max_xattr = val; return 0; } +static bool synth_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs) +{ + return false; +} + FileOperations synth_ops = { + .parse_opts = synth_parse_opts, .init = synth_init, .lstat = synth_lstat, .readlink = synth_readlink, @@ -650,4 +698,5 @@ .name_to_path = synth_name_to_path, .renameat = synth_renameat, .unlinkat = synth_unlinkat, + .has_valid_file_handle = synth_has_valid_file_handle, }; diff -Nru qemu-10.0.11+ds/hw/9pfs/9p.c qemu-10.0.13+ds/hw/9pfs/9p.c --- qemu-10.0.11+ds/hw/9pfs/9p.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/9pfs/9p.c 2026-08-26 19:31:25.000000000 +0000 @@ -263,6 +263,31 @@ return str->size; } +static int xattr_fid_count_inc(V9fsPDU *pdu) +{ + V9fsState *s = pdu->s; + + if (s->ctx.xattr_fid_limit > 0 && + s->ctx.xattr_fid_count >= s->ctx.xattr_fid_limit) { + error_report_once("9pfs: xattr_fid_count limit exceeded " + "(configurable by option 'max_xattr')."); + return -ENOSPC; + } + s->ctx.xattr_fid_count++; + return 0; +} + +static void xattr_fid_count_decr(V9fsPDU *pdu) +{ + V9fsState *s = pdu->s; + + if (s->ctx.xattr_fid_count > 0) { + s->ctx.xattr_fid_count--; + } else { + error_report_once("9pfs: xattr_fid_count underflow detected"); + } +} + /* * returns 0 if fid got re-opened, 1 if not, < 0 on error */ @@ -395,6 +420,7 @@ } } else if (fidp->fid_type == P9_FID_XATTR) { retval = v9fs_xattr_fid_clunk(pdu, fidp); + xattr_fid_count_decr(pdu); } v9fs_path_free(&fidp->path); g_free(fidp); @@ -624,6 +650,14 @@ fidp->clunked = true; put_fid(pdu, fidp); } + + /* + * Explicitly reset the xattr FID counter. + * + * free_fid() already decrements the counter for each P9_FID_XATTR, so the + * counter should already be zero, hence this is just a defensive measure. + */ + s->ctx.xattr_fid_count = 0; } #define P9_QID_TYPE_DIR 0x80 @@ -1459,6 +1493,16 @@ goto out; } + /* cap msize to transport's theoretical limit */ + if (s->transport->msize_limit) { + size_t limit = s->transport->msize_limit(s); + if (s->msize > limit) { + s->msize = limit; + warn_report_once("9p: client msize capped to %zu (transport limit)", + limit); + } + } + /* 8192 is the default msize of Linux clients */ if (s->msize <= 8192 && !(s->ctx.export_flags & V9FS_NO_PERF_WARN)) { warn_report_once( @@ -1607,6 +1651,11 @@ pdu_complete(pdu, err); } +static bool fid_has_valid_file_handle(V9fsState *s, V9fsFidState *fidp) +{ + return s->ops->has_valid_file_handle(fidp->fid_type, &fidp->fs); +} + static void coroutine_fn v9fs_getattr(void *opaque) { int32_t fid; @@ -1629,9 +1678,7 @@ retval = -ENOENT; goto out_nofid; } - if ((fidp->fid_type == P9_FID_FILE && fidp->fs.fd != -1) || - (fidp->fid_type == P9_FID_DIR && fidp->fs.dir.stream)) - { + if (fid_has_valid_file_handle(pdu->s, fidp)) { retval = v9fs_co_fstat(pdu, fidp, &stbuf); } else { retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf); @@ -2097,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; } @@ -2226,10 +2273,15 @@ err = -ENOENT; goto out_nofid; } + if (!fid_has_valid_file_handle(pdu->s, fidp)) { + err = -EBADF; + goto out; + } err = v9fs_co_fsync(pdu, fidp, datasync); if (!err) { err = offset; } +out: put_fid(pdu, fidp); out_nofid: pdu_complete(pdu, err); @@ -2634,6 +2686,7 @@ uint32_t max_count; V9fsPDU *pdu = opaque; V9fsState *s = pdu->s; + size_t max_resp_sz; retval = pdu_unmarshal(pdu, offset, "dqd", &fid, &initial_offset, &max_count); @@ -2642,9 +2695,28 @@ } trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count); + max_resp_sz = s->msize; + + /* + * Constrain max_count to transport's current, actual response buffer size. + * A bad client might provide a response buffer < msize. + */ + if (s->transport->response_buffer_size) { + size_t buf_size = s->transport->response_buffer_size(pdu); + if (max_resp_sz > buf_size) { + max_resp_sz = buf_size; + } + } + /* Enough space for a R_readdir header: size[4] Rreaddir tag[2] count[4] */ - if (max_count > s->msize - 11) { - max_count = s->msize - 11; + if (max_resp_sz > 11) { + max_resp_sz -= 11; + } else { + max_resp_sz = 0; + } + + if (max_count > max_resp_sz) { + max_count = max_resp_sz; warn_report_once( "9p: bad client: T_readdir with count > msize - 11" ); @@ -3563,6 +3635,10 @@ } /* do we need to sync the file? */ if (donttouch_stat(&v9stat)) { + if (!fid_has_valid_file_handle(s, fidp)) { + err = -EBADF; + goto out; + } err = v9fs_co_fsync(pdu, fidp, 0); goto out; } @@ -3831,6 +3907,10 @@ err = -ENOENT; goto out_nofid; } + if (!fid_has_valid_file_handle(pdu->s, fidp)) { + err = -EBADF; + goto out; + } err = v9fs_co_fstat(pdu, fidp, &stbuf); if (err < 0) { goto out; @@ -3876,6 +3956,10 @@ err = -ENOENT; goto out_nofid; } + if (!fid_has_valid_file_handle(pdu->s, fidp)) { + err = -EBADF; + goto out; + } err = v9fs_co_fstat(pdu, fidp, &stbuf); if (err < 0) { goto out; @@ -3994,6 +4078,14 @@ clunk_fid(s, xattr_fidp->fid); goto out; } + + /* Check xattr FID limit */ + err = xattr_fid_count_inc(pdu); + if (err < 0) { + clunk_fid(s, xattr_fidp->fid); + goto out; + } + /* * Read the xattr value */ @@ -4001,6 +4093,7 @@ xattr_fidp->fid_type = P9_FID_XATTR; xattr_fidp->fs.xattr.xattrwalk_fid = true; xattr_fidp->fs.xattr.value = g_malloc0(size); + if (size) { err = v9fs_co_llistxattr(pdu, &xattr_fidp->path, xattr_fidp->fs.xattr.value, @@ -4027,6 +4120,14 @@ clunk_fid(s, xattr_fidp->fid); goto out; } + + /* Check xattr FID limit */ + err = xattr_fid_count_inc(pdu); + if (err < 0) { + clunk_fid(s, xattr_fidp->fid); + goto out; + } + /* * Read the xattr value */ @@ -4034,6 +4135,7 @@ xattr_fidp->fid_type = P9_FID_XATTR; xattr_fidp->fs.xattr.xattrwalk_fid = true; xattr_fidp->fs.xattr.value = g_malloc0(size); + if (size) { err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, &name, xattr_fidp->fs.xattr.value, @@ -4125,6 +4227,12 @@ goto out_put_fid; } + /* Check xattr FID limit */ + err = xattr_fid_count_inc(pdu); + if (err < 0) { + goto out_put_fid; + } + /* Make the file fid point to xattr */ xattr_fidp = file_fidp; xattr_fidp->fid_type = P9_FID_XATTR; @@ -4387,6 +4495,10 @@ s->reclaiming = false; + /* init xattr FID limit from fsdev config */ + s->ctx.xattr_fid_limit = fse->max_xattr; + s->ctx.xattr_fid_count = 0; + rc = 0; out: if (rc) { @@ -4413,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.11+ds/hw/9pfs/9p.h qemu-10.0.13+ds/hw/9pfs/9p.h --- qemu-10.0.11+ds/hw/9pfs/9p.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/9pfs/9p.h 2026-08-26 19:31:25.000000000 +0000 @@ -481,6 +481,8 @@ void (*init_out_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, unsigned int *pniov, size_t size); void (*push_and_notify)(V9fsPDU *pdu); + size_t (*msize_limit)(V9fsState *s); + size_t (*response_buffer_size)(V9fsPDU *pdu); }; #endif diff -Nru qemu-10.0.11+ds/hw/9pfs/virtio-9p-device.c qemu-10.0.13+ds/hw/9pfs/virtio-9p-device.c --- qemu-10.0.11+ds/hw/9pfs/virtio-9p-device.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/9pfs/virtio-9p-device.c 2026-08-26 19:31:25.000000000 +0000 @@ -192,12 +192,29 @@ *pniov = elem->out_num; } +static size_t virtio_9p_msize_limit(V9fsState *s) +{ + const size_t guestPageSize = 4096; + return (VIRTQUEUE_MAX_SIZE - 2) * guestPageSize; +} + +static size_t virtio_9p_response_buffer_size(V9fsPDU *pdu) +{ + V9fsState *s = pdu->s; + V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); + VirtQueueElement *elem = v->elems[pdu->idx]; + + return iov_size(elem->in_sg, elem->in_num); +} + static const V9fsTransport virtio_9p_transport = { .pdu_vmarshal = virtio_pdu_vmarshal, .pdu_vunmarshal = virtio_pdu_vunmarshal, .init_in_iov_from_pdu = virtio_init_in_iov_from_pdu, .init_out_iov_from_pdu = virtio_init_out_iov_from_pdu, .push_and_notify = virtio_9p_push_and_notify, + .msize_limit = virtio_9p_msize_limit, + .response_buffer_size = virtio_9p_response_buffer_size, }; static void virtio_9p_device_realize(DeviceState *dev, Error **errp) @@ -226,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.11+ds/hw/9pfs/xen-9p-backend.c qemu-10.0.13+ds/hw/9pfs/xen-9p-backend.c --- qemu-10.0.11+ds/hw/9pfs/xen-9p-backend.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/9pfs/xen-9p-backend.c 2026-08-26 19:31:25.000000000 +0000 @@ -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; } @@ -250,12 +257,43 @@ qemu_bh_schedule(ring->bh); } +static size_t xen_9p_msize_limit(V9fsState *s) +{ + Xen9pfsDev *xen_9pfs = container_of(s, Xen9pfsDev, state); + size_t limit; + int i; + + if (!xen_9pfs->num_rings) { + return 0; + } + + limit = XEN_FLEX_RING_SIZE(xen_9pfs->rings[0].ring_order); + for (i = 1; i < xen_9pfs->num_rings; i++) { + limit = MIN(limit, XEN_FLEX_RING_SIZE(xen_9pfs->rings[i].ring_order)); + } + + return limit; +} + +static size_t xen_9pfs_response_buffer_size(V9fsPDU *pdu) +{ + Xen9pfsDev *priv = container_of(pdu->s, Xen9pfsDev, state); + Xen9pfsRing *ring = &priv->rings[pdu->tag % priv->num_rings]; + struct iovec in_sg[2]; + int num; + + xen_9pfs_in_sg(ring, in_sg, &num, pdu->idx, 0); + return iov_size(in_sg, num); +} + static const V9fsTransport xen_9p_transport = { .pdu_vmarshal = xen_9pfs_pdu_vmarshal, .pdu_vunmarshal = xen_9pfs_pdu_vunmarshal, .init_in_iov_from_pdu = xen_9pfs_init_in_iov_from_pdu, .init_out_iov_from_pdu = xen_9pfs_init_out_iov_from_pdu, .push_and_notify = xen_9pfs_push_and_notify, + .msize_limit = xen_9p_msize_limit, + .response_buffer_size = xen_9pfs_response_buffer_size, }; static int xen_9pfs_init(struct XenLegacyDevice *xendev) @@ -337,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.11+ds/hw/acpi/aml-build.c qemu-10.0.13+ds/hw/acpi/aml-build.c --- qemu-10.0.11+ds/hw/acpi/aml-build.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/acpi/aml-build.c 2026-08-26 19:31:25.000000000 +0000 @@ -2106,10 +2106,10 @@ build_append_int_noprefix(table_data, f->stop_bits, 1); /* Flow Control */ build_append_int_noprefix(table_data, f->flow_control, 1); - /* Language */ - build_append_int_noprefix(table_data, f->language, 1); /* Terminal Type */ build_append_int_noprefix(table_data, f->terminal_type, 1); + /* Language */ + build_append_int_noprefix(table_data, f->language, 1); /* PCI Device ID */ build_append_int_noprefix(table_data, f->pci_device_id, 2); /* PCI Vendor ID */ diff -Nru qemu-10.0.11+ds/hw/arm/Kconfig qemu-10.0.13+ds/hw/arm/Kconfig --- qemu-10.0.11+ds/hw/arm/Kconfig 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/arm/Kconfig 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/arm/npcm7xx.c qemu-10.0.13+ds/hw/arm/npcm7xx.c --- qemu-10.0.11+ds/hw/arm/npcm7xx.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/arm/npcm7xx.c 2026-08-26 19:31:25.000000000 +0000 @@ -492,7 +492,7 @@ /* CPUs */ for (i = 0; i < nc->num_cpus; i++) { object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar", - NPCM7XX_GIC_CPU_IF_ADDR, &error_abort); + NPCM7XX_CPUP_BA, &error_abort); object_property_set_bool(OBJECT(&s->cpu[i]), "reset-hivecs", true, &error_abort); diff -Nru qemu-10.0.11+ds/hw/audio/intel-hda.c qemu-10.0.13+ds/hw/audio/intel-hda.c --- qemu-10.0.11+ds/hw/audio/intel-hda.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/audio/intel-hda.c 2026-08-26 19:31:25.000000000 +0000 @@ -305,6 +305,7 @@ static void intel_hda_corb_run(IntelHDAState *d) { + const MemTxAttrs attrs = { .memory = true }; hwaddr addr; uint32_t rp, verb; @@ -330,7 +331,7 @@ rp = (d->corb_rp + 1) & 0xff; addr = intel_hda_addr(d->corb_lbase, d->corb_ubase); - ldl_le_pci_dma(&d->pci, addr + 4 * rp, &verb, MEMTXATTRS_UNSPECIFIED); + ldl_le_pci_dma(&d->pci, addr + 4 * rp, &verb, attrs); d->corb_rp = rp; dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __func__, rp, verb); @@ -395,7 +396,7 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, uint8_t *buf, uint32_t len) { - const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; + const MemTxAttrs attrs = { .memory = true }; HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus); IntelHDAState *d = container_of(bus, IntelHDAState, codecs); hwaddr addr; @@ -466,6 +467,7 @@ static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st) { + const MemTxAttrs attrs = { .memory = true }; hwaddr addr; uint8_t buf[16]; uint32_t i; @@ -475,7 +477,8 @@ g_free(st->bpl); st->bpl = g_new(bpl, st->bentries); for (i = 0; i < st->bentries; i++, addr += 16) { - pci_dma_read(&d->pci, addr, buf, 16); + pci_dma_rw(&d->pci, addr, buf, 16, + DMA_DIRECTION_TO_DEVICE, attrs); st->bpl[i].addr = le64_to_cpu(*(uint64_t *)buf); st->bpl[i].len = le32_to_cpu(*(uint32_t *)(buf + 8)); st->bpl[i].flags = le32_to_cpu(*(uint32_t *)(buf + 12)); diff -Nru qemu-10.0.11+ds/hw/block/pflash_cfi01.c qemu-10.0.13+ds/hw/block/pflash_cfi01.c --- qemu-10.0.11+ds/hw/block/pflash_cfi01.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/block/pflash_cfi01.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/char/sclpconsole-lm.c qemu-10.0.13+ds/hw/char/sclpconsole-lm.c --- qemu-10.0.11+ds/hw/char/sclpconsole-lm.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/char/sclpconsole-lm.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/char/serial.c qemu-10.0.13+ds/hw/char/serial.c --- qemu-10.0.11+ds/hw/char/serial.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/char/serial.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/char/virtio-serial-bus.c qemu-10.0.13+ds/hw/char/virtio-serial-bus.c --- qemu-10.0.11+ds/hw/char/virtio-serial-bus.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/char/virtio-serial-bus.c 2026-08-26 19:31:25.000000000 +0000 @@ -344,22 +344,16 @@ } /* Guest wants to notify us of some event */ -static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) +static void handle_control_message(VirtIOSerial *vser, + struct virtio_console_control *gcpkt) { VirtIODevice *vdev = VIRTIO_DEVICE(vser); struct VirtIOSerialPort *port; VirtIOSerialPortClass *vsc; - struct virtio_console_control cpkt, *gcpkt; + struct virtio_console_control cpkt; uint8_t *buffer; size_t buffer_len; - gcpkt = buf; - - if (len < sizeof(cpkt)) { - /* The guest sent an invalid control packet */ - return; - } - cpkt.event = virtio_lduw_p(vdev, &gcpkt->event); cpkt.value = virtio_lduw_p(vdev, &gcpkt->value); @@ -457,41 +451,27 @@ static void control_out(VirtIODevice *vdev, VirtQueue *vq) { + struct virtio_console_control cpkt; VirtQueueElement *elem; VirtIOSerial *vser; - uint8_t *buf; size_t len; vser = VIRTIO_SERIAL(vdev); - len = 0; - buf = NULL; for (;;) { - size_t cur_len; - elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { break; } - cur_len = iov_size(elem->out_sg, elem->out_num); - /* - * Allocate a new buf only if we didn't have one previously or - * if the size of the buf differs - */ - if (cur_len > len) { - g_free(buf); - - buf = g_malloc(cur_len); - len = cur_len; + len = iov_to_buf(elem->out_sg, elem->out_num, 0, &cpkt, sizeof(cpkt)); + if (len == sizeof(cpkt)) { + handle_control_message(vser, &cpkt); } - iov_to_buf(elem->out_sg, elem->out_num, 0, buf, cur_len); - handle_control_message(vser, buf, cur_len); virtqueue_push(vq, elem, 0); g_free(elem); } - g_free(buf); virtio_notify(vdev, vq); } diff -Nru qemu-10.0.11+ds/hw/core/qdev-clock.c qemu-10.0.13+ds/hw/core/qdev-clock.c --- qemu-10.0.11+ds/hw/core/qdev-clock.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/core/qdev-clock.c 2026-08-26 19:31:25.000000000 +0000 @@ -157,7 +157,14 @@ DeviceState *alias_dev, const char *alias_name) { NamedClockList *ncl = qdev_get_clocklist(dev, name); - Clock *clk = ncl->clock; + Clock *clk; + + if (!ncl) { + error_report("Can not find clock '%s' for device type '%s'", + name, object_get_typename(OBJECT(dev))); + abort(); + } + clk = ncl->clock; ncl = qdev_init_clocklist(alias_dev, alias_name, true, ncl->output, clk); diff -Nru qemu-10.0.11+ds/hw/display/exynos4210_fimd.c qemu-10.0.13+ds/hw/display/exynos4210_fimd.c --- qemu-10.0.11+ds/hw/display/exynos4210_fimd.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/display/exynos4210_fimd.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/display/qxl-render.c qemu-10.0.13+ds/hw/display/qxl-render.c --- qemu-10.0.11+ds/hw/display/qxl-render.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/display/qxl-render.c 2026-08-26 19:31:25.000000000 +0000 @@ -217,7 +217,8 @@ } static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, - QXLDataChunk *chunk, uint32_t group_id) + QXLDataChunk *chunk, uint32_t group_id, + uint32_t chunk_data_size) { uint32_t max_chunks = 32; size_t offset = 0; @@ -225,22 +226,21 @@ QXLPHYSICAL next_chunk_phys = 0; for (;;) { - bytes = MIN(size - offset, chunk->data_size); + bytes = MIN(size - offset, chunk_data_size); memcpy(dest + offset, chunk->data, bytes); offset += bytes; if (offset == size) { return; } next_chunk_phys = chunk->next_chunk; - /* fist time, only get the next chunk's data size */ chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, sizeof(QXLDataChunk)); if (!chunk) { return; } - /* second time, check data size and get data */ + chunk_data_size = chunk->data_size; chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, - sizeof(QXLDataChunk) + chunk->data_size); + sizeof(QXLDataChunk) + chunk_data_size); if (!chunk) { return; } @@ -252,7 +252,7 @@ } static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, - uint32_t group_id) + uint32_t group_id, uint32_t chunk_data_size) { QEMUCursor *c; uint8_t *and_mask, *xor_mask; @@ -272,9 +272,11 @@ case SPICE_CURSOR_TYPE_MONO: /* Assume that the full cursor is available in a single chunk. */ size = 2 * cursor_get_mono_bpl(c) * c->height; - if (size != cursor->data_size) { - fprintf(stderr, "%s: bad monochrome cursor %ux%u with size %u\n", - __func__, c->width, c->height, cursor->data_size); + if (size != cursor->data_size || chunk_data_size < size) { + qxl_set_guest_bug(qxl, "%s: bad monochrome cursor %ux%u" + " data_size %u chunk_size %u", + __func__, c->width, c->height, + cursor->data_size, chunk_data_size); goto fail; } and_mask = cursor->chunk.data; @@ -286,7 +288,8 @@ break; case SPICE_CURSOR_TYPE_ALPHA: size = sizeof(uint32_t) * c->width * c->height; - qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id); + qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id, + chunk_data_size); if (qxl->debug > 2) { cursor_print_ascii_art(c, "qxl/alpha"); } @@ -323,19 +326,23 @@ } switch (cmd->type) { case QXL_CURSOR_SET: + { + uint32_t chunk_data_size; + /* First read the QXLCursor to get QXLDataChunk::data_size ... */ cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id, sizeof(QXLCursor)); if (!cursor) { return 1; } + chunk_data_size = cursor->chunk.data_size; /* Then read including the chunked data following QXLCursor. */ cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id, - sizeof(QXLCursor) + cursor->chunk.data_size); + sizeof(QXLCursor) + chunk_data_size); if (!cursor) { return 1; } - c = qxl_cursor(qxl, cursor, ext->group_id); + c = qxl_cursor(qxl, cursor, ext->group_id, chunk_data_size); if (c == NULL) { c = cursor_builtin_left_ptr(); } @@ -349,6 +356,7 @@ qemu_mutex_unlock(&qxl->ssd.lock); qemu_bh_schedule(qxl->ssd.cursor_bh); break; + } case QXL_CURSOR_MOVE: qemu_mutex_lock(&qxl->ssd.lock); qxl->ssd.mouse_x = cmd->u.position.x; diff -Nru qemu-10.0.11+ds/hw/display/qxl.c qemu-10.0.13+ds/hw/display/qxl.c --- qemu-10.0.11+ds/hw/display/qxl.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/display/qxl.c 2026-08-26 19:31:25.000000000 +0000 @@ -275,7 +275,7 @@ } cfg = qxl_phys2virt(qxl, qxl->guest_monitors_config, MEMSLOT_GROUP_GUEST, - sizeof(QXLMonitorsConfig)); + sizeof(QXLMonitorsConfig) + sizeof(QXLHead)); if (cfg != NULL && cfg->count == 1) { qxl->guest_primary.resized = 1; qxl->guest_head0_width = cfg->heads[0].width; @@ -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.11+ds/hw/display/qxl.h qemu-10.0.13+ds/hw/display/qxl.h --- qemu-10.0.11+ds/hw/display/qxl.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/display/qxl.h 2026-08-26 19:31:25.000000000 +0000 @@ -83,6 +83,7 @@ /* thread signaling */ QEMUBH *update_irq; + VMChangeStateEntry *vmstate_handler; /* ram pci bar */ QXLRam *ram; diff -Nru qemu-10.0.11+ds/hw/display/sm501.c qemu-10.0.13+ds/hw/display/sm501.c --- qemu-10.0.11+ds/hw/display/sm501.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/display/sm501.c 2026-08-26 19:31:25.000000000 +0000 @@ -570,6 +570,25 @@ return index; } +static void set_new_local_mem_size_index(SM501State *s, uint32_t idx) +{ + /* + * Update local_mem_size_index on guest write. We don't allow this + * to be set to larger than the actual RAM size. (The guest will + * still read back the SYSTEM_CONTROL.Size bits that it wrote.) + */ + if (idx < ARRAY_SIZE(sm501_mem_local_size) && + sm501_mem_local_size[idx] <= memory_region_size(&s->local_mem_region)) { + s->local_mem_size_index = idx; + return; + } + qemu_log_mask(LOG_GUEST_ERROR, + "sm501: Guest set DRAM_CONTROL.Size to 0x%x but " + "local memory is not that large\n", + idx); + /* Don't change the effective size, leave it as whatever it was */ +} + static ram_addr_t get_fb_addr(SM501State *s, int crt) { return (crt ? s->dc_crt_fb_addr : s->dc_panel_fb_addr) & 0x3FFFFF0; @@ -681,6 +700,28 @@ get_fb_addr(s, crt) + start, end - start); } +static bool sm501_rect_outside_vram(SM501State *s, uint32_t base, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height, + uint32_t pitch, uint32_t bypp) +{ + /* + * Return true if the 2D area specified by the arguments is + * partially or completely outside the VRAM (a guest error) + * + * Limits on the input sizes mean we can't overflow as long as + * we do all the arithmetic at 64 bits. + */ + uint64_t rect_size, last_addr; + + assert(x <= UINT16_MAX && y <= UINT16_MAX && height <= UINT16_MAX && + pitch <= UINT16_MAX && bypp <= 8); + rect_size = (((uint64_t)y + height) * pitch + x + width) * bypp; + last_addr = base + rect_size; + + return last_addr >= get_local_mem_size(s); +} + static void sm501_2d_operation(SM501State *s) { int cmd = (s->twoD_control >> 16) & 0x1F; @@ -722,13 +763,16 @@ } if (rtl) { + if (dst_x < (width - 1) || dst_y < (height - 1)) { + qemu_log_mask(LOG_GUEST_ERROR, "sm501: RTL op out of bounds\n"); + return; + } dst_x -= width - 1; dst_y -= height - 1; } - if (dst_base >= get_local_mem_size(s) || - dst_base + (dst_x + width + (dst_y + height) * dst_pitch) * bypp >= - get_local_mem_size(s)) { + if (sm501_rect_outside_vram(s, dst_base, dst_x, dst_y, width, height, + dst_pitch, bypp)) { qemu_log_mask(LOG_GUEST_ERROR, "sm501: 2D op dest is outside vram.\n"); return; } @@ -747,13 +791,16 @@ } if (rtl) { + if (src_x < (width - 1) || src_y < (height - 1)) { + qemu_log_mask(LOG_GUEST_ERROR, "sm501: RTL op out of bounds\n"); + return; + } src_x -= width - 1; src_y -= height - 1; } - if (src_base >= get_local_mem_size(s) || - src_base + (src_x + width + (src_y + height) * src_pitch) * bypp >= - get_local_mem_size(s)) { + if (sm501_rect_outside_vram(s, src_base, src_x, src_y, width, height, + src_pitch, bypp)) { qemu_log_mask(LOG_GUEST_ERROR, "sm501: 2D op src is outside vram.\n"); return; @@ -961,7 +1008,7 @@ ret = 0x050100A0; break; case SM501_DRAM_CONTROL: - ret = (s->dram_control & 0x07F107C0) | s->local_mem_size_index << 13; + ret = (s->dram_control & 0x07F1E7C0); break; case SM501_ARBTRTN_CONTROL: ret = s->arbitration_control; @@ -1020,8 +1067,7 @@ s->gpio_63_32_control = value & 0xFF80FFFF; break; case SM501_DRAM_CONTROL: - s->local_mem_size_index = (value >> 13) & 0x7; - /* TODO : check validity of size change */ + set_new_local_mem_size_index(s, (value >> 13) & 0x7); s->dram_control &= 0x80000000; s->dram_control |= value & 0x7FFFFFC3; break; diff -Nru qemu-10.0.11+ds/hw/display/vga.c qemu-10.0.13+ds/hw/display/vga.c --- qemu-10.0.11+ds/hw/display/vga.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/display/vga.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/display/vhost-user-gpu.c qemu-10.0.13+ds/hw/display/vhost-user-gpu.c --- qemu-10.0.11+ds/hw/display/vhost-user-gpu.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/display/vhost-user-gpu.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/display/virtio-gpu-rutabaga.c qemu-10.0.13+ds/hw/display/virtio-gpu-rutabaga.c --- qemu-10.0.11+ds/hw/display/virtio-gpu-rutabaga.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/display/virtio-gpu-rutabaga.c 2026-08-26 19:31:25.000000000 +0000 @@ -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 #include @@ -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); @@ -351,6 +358,14 @@ VIRTIO_GPU_FILL_CMD(cs); trace_virtio_gpu_cmd_ctx_submit(cs.hdr.ctx_id, cs.size); + if (cs.size > VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: command buffer too large (%u)\n", + __func__, cs.size); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + buf = g_new0(uint8_t, cs.size); s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(cs), buf, cs.size); @@ -1032,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) @@ -1070,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); @@ -1079,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); @@ -1094,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.11+ds/hw/display/virtio-gpu-udmabuf.c qemu-10.0.13+ds/hw/display/virtio-gpu-udmabuf.c --- qemu-10.0.11+ds/hw/display/virtio-gpu-udmabuf.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/display/virtio-gpu-udmabuf.c 2026-08-26 19:31:25.000000000 +0000 @@ -142,6 +142,7 @@ } virtio_gpu_remap_udmabuf(res); if (!res->remapped) { + virtio_gpu_destroy_udmabuf(res); return; } pdata = res->remapped; diff -Nru qemu-10.0.11+ds/hw/display/virtio-gpu-virgl.c qemu-10.0.13+ds/hw/display/virtio-gpu-virgl.c --- qemu-10.0.11+ds/hw/display/virtio-gpu-virgl.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/display/virtio-gpu-virgl.c 2026-08-26 19:31:25.000000000 +0000 @@ -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(); @@ -486,6 +491,14 @@ VIRTIO_GPU_FILL_CMD(cs); trace_virtio_gpu_cmd_ctx_submit(cs.hdr.ctx_id, cs.size); + if (cs.size > VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: command buffer too large (%u)\n", + __func__, cs.size); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + buf = g_malloc(cs.size); s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(cs), buf, cs.size); @@ -848,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.11+ds/hw/display/virtio-gpu.c qemu-10.0.13+ds/hw/display/virtio-gpu.c --- qemu-10.0.11+ds/hw/display/virtio-gpu.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/display/virtio-gpu.c 2026-08-26 19:31:25.000000000 +0000 @@ -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; } @@ -670,6 +711,10 @@ void *ptr = data + fb->offset; rect = pixman_image_create_bits(fb->format, r->width, r->height, ptr, fb->stride); + if (!rect) { + *error = VIRTIO_GPU_RESP_ERR_UNSPEC; + return false; + } if (res->image) { pixman_image_ref(res->image); @@ -696,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)); @@ -721,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); @@ -735,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) { @@ -745,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 (fbend > blob_size) { + 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; + } + + 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; } @@ -945,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 @@ -1199,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, @@ -1239,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); @@ -1336,8 +1412,15 @@ return -EINVAL; } - res->addrs = g_new(uint64_t, res->iov_cnt); - res->iov = g_new(struct iovec, res->iov_cnt); + res->addrs = g_try_new(uint64_t, res->iov_cnt); + res->iov = g_try_new(struct iovec, res->iov_cnt); + if (res->iov_cnt && (!res->addrs || !res->iov)) { + pixman_image_unref(res->image); + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } /* read data */ for (i = 0; i < res->iov_cnt; i++) { @@ -1373,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); @@ -1409,8 +1492,15 @@ res->resource_id = resource_id; res->blob_size = qemu_get_be32(f); res->iov_cnt = qemu_get_be32(f); - res->addrs = g_new(uint64_t, res->iov_cnt); - res->iov = g_new(struct iovec, res->iov_cnt); + + res->addrs = g_try_new(uint64_t, res->iov_cnt); + res->iov = g_try_new(struct iovec, res->iov_cnt); + if (res->iov_cnt && (!res->addrs || !res->iov)) { + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } /* read data */ for (i = 0; i < res->iov_cnt; i++) { @@ -1418,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; @@ -1597,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.11+ds/hw/display/vmware_vga.c qemu-10.0.13+ds/hw/display/vmware_vga.c --- qemu-10.0.11+ds/hw/display/vmware_vga.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/display/vmware_vga.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/dma/i8257.c qemu-10.0.13+ds/hw/dma/i8257.c --- qemu-10.0.11+ds/hw/dma/i8257.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/dma/i8257.c 2026-08-26 19:31:25.000000000 +0000 @@ -406,6 +406,19 @@ hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; if (i8257_is_verify_transfer(r)) { + /* + * If the device is expecting this verify operation then + * it won't care about the nonexistent data. But if it + * is expecting a real read (i.e. the guest has misprogrammed + * the DMA controller and the device) it's going to try to do + * something with the buffer contents. Give it zeroes. + * (It's not clear whether this is exactly what happens if + * you do this on real hardware. In practice no device QEMU + * emulates has a use for verify on a memory-read transfer, + * so we don't care beyond avoiding the guest being able to + * trigger the caller reading uninitialized data.) + */ + memset(buf, 0, len); return len; } diff -Nru qemu-10.0.11+ds/hw/i386/intel_iommu.c qemu-10.0.13+ds/hw/i386/intel_iommu.c --- qemu-10.0.11+ds/hw/i386/intel_iommu.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/i386/intel_iommu.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/ide/ahci.c qemu-10.0.13+ds/hw/ide/ahci.c --- qemu-10.0.11+ds/hw/ide/ahci.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/ide/ahci.c 2026-08-26 19:31:25.000000000 +0000 @@ -740,6 +740,9 @@ static void ahci_unmap_clb_address(AHCIDevice *ad) { + /* Cancel in-flight reads that would complete against a cleared cur_cmd. */ + ide_cancel_dma_sync(ide_bus_active_if(&ad->port)); + if (ad->lst == NULL) { trace_ahci_unmap_clb_address_null(ad->hba, ad->port_no); return; diff -Nru qemu-10.0.11+ds/hw/ide/atapi.c qemu-10.0.13+ds/hw/ide/atapi.c --- qemu-10.0.11+ds/hw/ide/atapi.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/ide/atapi.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/ide/core.c qemu-10.0.13+ds/hw/ide/core.c --- qemu-10.0.11+ds/hw/ide/core.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/ide/core.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/intc/loongarch_pch_pic.c qemu-10.0.13+ds/hw/intc/loongarch_pch_pic.c --- qemu-10.0.11+ds/hw/intc/loongarch_pch_pic.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/intc/loongarch_pch_pic.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/mem/cxl_type3.c qemu-10.0.13+ds/hw/mem/cxl_type3.c --- qemu-10.0.11+ds/hw/mem/cxl_type3.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/mem/cxl_type3.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/misc/applesmc.c qemu-10.0.13+ds/hw/misc/applesmc.c --- qemu-10.0.11+ds/hw/misc/applesmc.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/misc/applesmc.c 2026-08-26 19:31:25.000000000 +0000 @@ -333,9 +333,9 @@ applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); applesmc_add_key(s, "OSK0", 32, s->osk); applesmc_add_key(s, "OSK1", 32, s->osk + 32); - applesmc_add_key(s, "NATJ", 1, "\0"); - applesmc_add_key(s, "MSSP", 1, "\0"); - applesmc_add_key(s, "MSSD", 1, "\0x3"); + applesmc_add_key(s, "NATJ", 1, "\x00"); + applesmc_add_key(s, "MSSP", 1, "\x00"); + applesmc_add_key(s, "MSSD", 1, "\x03"); } static void applesmc_unrealize(DeviceState *dev) diff -Nru qemu-10.0.11+ds/hw/misc/edu.c qemu-10.0.13+ds/hw/misc/edu.c --- qemu-10.0.11+ds/hw/misc/edu.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/misc/edu.c 2026-08-26 19:31:25.000000000 +0000 @@ -103,7 +103,7 @@ } } -static void edu_check_range(uint64_t xfer_start, uint64_t xfer_size, +static bool edu_check_range(uint64_t xfer_start, uint64_t xfer_size, uint64_t dma_start, uint64_t dma_size) { uint64_t xfer_end = xfer_start + xfer_size; @@ -115,13 +115,15 @@ */ if (dma_end >= dma_start && xfer_end >= xfer_start && xfer_start >= dma_start && xfer_end <= dma_end) { - return; + return true; } qemu_log_mask(LOG_GUEST_ERROR, "EDU: DMA range 0x%016"PRIx64"-0x%016"PRIx64 " out of bounds (0x%016"PRIx64"-0x%016"PRIx64")!", xfer_start, xfer_end - 1, dma_start, dma_end - 1); + + return false; } static dma_addr_t edu_clamp_addr(const EduState *edu, dma_addr_t addr) @@ -148,16 +150,18 @@ if (EDU_DMA_DIR(edu->dma.cmd) == EDU_DMA_FROM_PCI) { uint64_t dst = edu->dma.dst; - edu_check_range(dst, edu->dma.cnt, DMA_START, DMA_SIZE); - dst -= DMA_START; - pci_dma_read(&edu->pdev, edu_clamp_addr(edu, edu->dma.src), - edu->dma_buf + dst, edu->dma.cnt); + if (edu_check_range(dst, edu->dma.cnt, DMA_START, DMA_SIZE)) { + dst -= DMA_START; + pci_dma_read(&edu->pdev, edu_clamp_addr(edu, edu->dma.src), + edu->dma_buf + dst, edu->dma.cnt); + } } else { uint64_t src = edu->dma.src; - edu_check_range(src, edu->dma.cnt, DMA_START, DMA_SIZE); - src -= DMA_START; - pci_dma_write(&edu->pdev, edu_clamp_addr(edu, edu->dma.dst), - edu->dma_buf + src, edu->dma.cnt); + if (edu_check_range(src, edu->dma.cnt, DMA_START, DMA_SIZE)) { + src -= DMA_START; + pci_dma_write(&edu->pdev, edu_clamp_addr(edu, edu->dma.dst), + edu->dma_buf + src, edu->dma.cnt); + } } edu->dma.cmd &= ~EDU_DMA_RUN; diff -Nru qemu-10.0.11+ds/hw/misc/ivshmem-pci.c qemu-10.0.13+ds/hw/misc/ivshmem-pci.c --- qemu-10.0.11+ds/hw/misc/ivshmem-pci.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/misc/ivshmem-pci.c 2026-08-26 19:31:25.000000000 +0000 @@ -920,6 +920,9 @@ IVShmemState *s = IVSHMEM_COMMON(dev); int i; + qemu_chr_fe_set_handlers(&s->server_chr, + NULL, NULL, NULL, NULL, NULL, NULL, true); + migrate_del_blocker(&s->migration_blocker); if (memory_region_is_mapped(s->ivshmem_bar2)) { @@ -948,6 +951,8 @@ close_peer_eventfds(s, i); } g_free(s->peers); + s->peers = NULL; + s->nb_peers = 0; } if (ivshmem_has_feature(s, IVSHMEM_MSI)) { @@ -955,6 +960,7 @@ } g_free(s->msi_vectors); + s->msi_vectors = NULL; } static int ivshmem_pre_load(void *opaque) diff -Nru qemu-10.0.11+ds/hw/misc/stm32_rcc.c qemu-10.0.13+ds/hw/misc/stm32_rcc.c --- qemu-10.0.11+ds/hw/misc/stm32_rcc.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/misc/stm32_rcc.c 2026-08-26 19:31:25.000000000 +0000 @@ -53,6 +53,27 @@ return value; } +static int reg_offset_to_irq_offset(hwaddr addr) +{ + /* + * The reset and enable registers aren't all consecutive. In getting the + * irq index from the register offset, we need to account for the gap + * between the AHB regs and the APB regs. + */ + switch (addr) { + case STM32_RCC_AHB1_RSTR ... STM32_RCC_AHB3_RSTR: + return ((addr - STM32_RCC_AHB1_RSTR) / 4) * 32; + case STM32_RCC_APB1_RSTR ... STM32_RCC_APB2_RSTR: + return ((addr - STM32_RCC_APB1_RSTR) / 4) * 32 + STM32_RCC_N_AHB_IRQS; + case STM32_RCC_AHB1_ENR ... STM32_RCC_AHB3_ENR: + return ((addr - STM32_RCC_AHB1_ENR) / 4) * 32; + case STM32_RCC_APB1_ENR ... STM32_RCC_APB2_ENR: + return ((addr - STM32_RCC_APB1_ENR) / 4) * 32 + STM32_RCC_N_AHB_IRQS; + default: + g_assert_not_reached(); + } +} + static void stm32_rcc_write(void *opaque, hwaddr addr, uint64_t val64, unsigned int size) { @@ -69,11 +90,12 @@ } switch (addr) { - case STM32_RCC_AHB1_RSTR...STM32_RCC_APB2_RSTR: + case STM32_RCC_AHB1_RSTR ... STM32_RCC_AHB3_RSTR: + case STM32_RCC_APB1_RSTR ... STM32_RCC_APB2_RSTR: prev_value = s->regs[addr / 4]; s->regs[addr / 4] = value; - irq_offset = ((addr - STM32_RCC_AHB1_RSTR) / 4) * 32; + irq_offset = reg_offset_to_irq_offset(addr); for (int i = 0; i < 32; i++) { new_value = extract32(value, i, 1); if (extract32(prev_value, i, 1) && !new_value) { @@ -82,11 +104,12 @@ } } return; - case STM32_RCC_AHB1_ENR...STM32_RCC_APB2_ENR: + case STM32_RCC_AHB1_ENR ... STM32_RCC_AHB3_ENR: + case STM32_RCC_APB1_ENR ... STM32_RCC_APB2_ENR: prev_value = s->regs[addr / 4]; s->regs[addr / 4] = value; - irq_offset = ((addr - STM32_RCC_AHB1_ENR) / 4) * 32; + irq_offset = reg_offset_to_irq_offset(addr); for (int i = 0; i < 32; i++) { new_value = extract32(value, i, 1); if (!extract32(prev_value, i, 1) && new_value) { diff -Nru qemu-10.0.11+ds/hw/net/cadence_gem.c qemu-10.0.13+ds/hw/net/cadence_gem.c --- qemu-10.0.11+ds/hw/net/cadence_gem.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/net/cadence_gem.c 2026-08-26 19:31:25.000000000 +0000 @@ -1469,6 +1469,8 @@ /* Set post reset register values */ memset(&s->regs[0], 0, sizeof(s->regs)); + memset(&s->rx_desc_addr[0], 0, sizeof(s->rx_desc_addr)); + memset(&s->tx_desc_addr[0], 0, sizeof(s->tx_desc_addr)); s->regs[R_NWCFG] = 0x00080000; s->regs[R_NWSTATUS] = 0x00000006; s->regs[R_DMACFG] = 0x00020784; @@ -1582,9 +1584,22 @@ offset >>= 2; retval = s->regs[offset]; - DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset*4, retval); + DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset * 4, + retval); switch (offset) { + case R_RXQBASE: + retval = s->rx_desc_addr[0]; + break; + case R_TXQBASE: + retval = s->tx_desc_addr[0]; + break; + case R_TRANSMIT_Q1_PTR ... R_TRANSMIT_Q7_PTR: + retval = s->tx_desc_addr[offset - R_TRANSMIT_Q1_PTR + 1]; + break; + case R_RECEIVE_Q1_PTR ... R_RECEIVE_Q7_PTR: + retval = s->rx_desc_addr[offset - R_RECEIVE_Q1_PTR + 1]; + break; case R_ISR: DB_PRINT("lowering irqs on ISR read\n"); /* The interrupts get updated at the end of the function. */ diff -Nru qemu-10.0.11+ds/hw/net/e1000e_core.c qemu-10.0.13+ds/hw/net/e1000e_core.c --- qemu-10.0.11+ds/hw/net/e1000e_core.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/net/e1000e_core.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/net/fsl_etsec/rings.c qemu-10.0.13+ds/hw/net/fsl_etsec/rings.c --- qemu-10.0.11+ds/hw/net/fsl_etsec/rings.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/net/fsl_etsec/rings.c 2026-08-26 19:31:25.000000000 +0000 @@ -175,15 +175,30 @@ static void process_tx_fcb(eTSEC *etsec) { uint8_t flags = (uint8_t)(*etsec->tx_buffer); - /* L3 header offset from start of frame */ + /* L3 header offset from start of frame (FCB byte 3) */ uint8_t l3_header_offset = (uint8_t)*(etsec->tx_buffer + 3); - /* L4 header offset from start of L3 header */ + /* L4 header offset from start of L3 header (FCB byte 2) */ uint8_t l4_header_offset = (uint8_t)*(etsec->tx_buffer + 2); + uint8_t *l3_header; + uint8_t *l4_header; + int csum = 0; + + /* + * Validate FCB header offsets before pointer arithmetic. The highest + * byte accessed is l4_header[7], at offset + * 8 (FCB size) + l3_header_offset + l4_header_offset + 7 + * from tx_buffer. Drop the frame if this exceeds the buffer length. + */ + if (etsec->tx_buffer_len < 8u + l3_header_offset + l4_header_offset + 8u) { + qemu_log_mask(LOG_GUEST_ERROR, + "eTSEC: FCB offsets exceed frame length, dropping\n"); + return; + } + /* L3 header */ - uint8_t *l3_header = etsec->tx_buffer + 8 + l3_header_offset; + l3_header = etsec->tx_buffer + 8 + l3_header_offset; /* L4 header */ - uint8_t *l4_header = l3_header + l4_header_offset; - int csum = 0; + l4_header = l3_header + l4_header_offset; /* if packet is IP4 and IP checksum is requested */ if (flags & FCB_TX_IP && flags & FCB_TX_CIP) { diff -Nru qemu-10.0.11+ds/hw/net/igb_core.c qemu-10.0.13+ds/hw/net/igb_core.c --- qemu-10.0.11+ds/hw/net/igb_core.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/net/igb_core.c 2026-08-26 19:31:25.000000000 +0000 @@ -4547,5 +4547,7 @@ igb_intrmgr_resume(core); igb_autoneg_resume(core); + igb_calc_rxdesclen(core); + return 0; } diff -Nru qemu-10.0.11+ds/hw/net/net_tx_pkt.c qemu-10.0.13+ds/hw/net/net_tx_pkt.c --- qemu-10.0.11+ds/hw/net/net_tx_pkt.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/net/net_tx_pkt.c 2026-08-26 19:31:25.000000000 +0000 @@ -93,9 +93,6 @@ uint16_t csum; assert(pkt); - pkt->l3_hdr.ip.ip_len = cpu_to_be16(pkt->payload_len + - pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); - pkt->l3_hdr.ip.ip_sum = 0; csum = net_raw_checksum(pkt->l3_hdr.octets, pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); @@ -117,7 +114,9 @@ if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || gso_type == VIRTIO_NET_HDR_GSO_UDP) { - /* Calculate IP header checksum */ + /* Set ip_len and calculate IP header checksum */ + pkt->l3_hdr.ip.ip_len = cpu_to_be16(pkt->payload_len + + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); net_tx_pkt_update_ip_hdr_checksum(pkt); /* Calculate IP pseudo header checksum */ diff -Nru qemu-10.0.11+ds/hw/net/rtl8139.c qemu-10.0.13+ds/hw/net/rtl8139.c --- qemu-10.0.11+ds/hw/net/rtl8139.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/net/rtl8139.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/net/virtio-net.c qemu-10.0.13+ds/hw/net/virtio-net.c --- qemu-10.0.11+ds/hw/net/virtio-net.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/net/virtio-net.c 2026-08-26 19:31:25.000000000 +0000 @@ -1380,6 +1380,11 @@ ebpf_rss_unload(&n->ebpf_rss); } +static bool virtio_net_rss_indirections_len_valid(uint16_t len) +{ + return is_power_of_2(len) && len <= VIRTIO_NET_RSS_MAX_TABLE_LEN; +} + static uint16_t virtio_net_handle_rss(VirtIONet *n, struct iovec *iov, unsigned int iov_cnt, @@ -1417,14 +1422,9 @@ if (!do_rss) { n->rss_data.indirections_len = 0; } - if (n->rss_data.indirections_len >= VIRTIO_NET_RSS_MAX_TABLE_LEN) { - err_msg = "Too large indirection table"; - err_value = n->rss_data.indirections_len; - goto error; - } n->rss_data.indirections_len++; - if (!is_power_of_2(n->rss_data.indirections_len)) { - err_msg = "Invalid size of indirection table"; + if (!virtio_net_rss_indirections_len_valid(n->rss_data.indirections_len)) { + err_msg = "Invalid indirection table length"; err_value = n->rss_data.indirections_len; goto error; } @@ -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; @@ -3311,6 +3330,20 @@ }, }; +static int virtio_net_rss_post_load(void *opaque, int version_id) +{ + VirtIONet *n = VIRTIO_NET(opaque); + + if (!virtio_net_rss_indirections_len_valid(n->rss_data.indirections_len)) { + error_report("virtio-net: saved image has invalid RSS " + "indirections_len: %u", + n->rss_data.indirections_len); + return -EINVAL; + } + + return 0; +} + static bool virtio_net_rss_needed(void *opaque) { return VIRTIO_NET(opaque)->rss_data.enabled; @@ -3320,6 +3353,7 @@ .name = "virtio-net-device/rss", .version_id = 1, .minimum_version_id = 1, + .post_load = virtio_net_rss_post_load, .needed = virtio_net_rss_needed, .fields = (const VMStateField[]) { VMSTATE_BOOL(rss_data.enabled, VirtIONet), diff -Nru qemu-10.0.11+ds/hw/net/vmxnet3.c qemu-10.0.13+ds/hw/net/vmxnet3.c --- qemu-10.0.11+ds/hw/net/vmxnet3.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/net/vmxnet3.c 2026-08-26 19:31:25.000000000 +0000 @@ -1092,7 +1092,7 @@ int tx_queue_idx = VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_TXPROD, VMXNET3_REG_ALIGN); - if (tx_queue_idx <= s->txq_num) { + if (tx_queue_idx < s->txq_num) { vmxnet3_process_tx_queue(s, tx_queue_idx); } else { qemu_log_mask(LOG_GUEST_ERROR, "vmxnet3: Illegal TX queue %d/%d\n", @@ -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.11+ds/hw/net/xilinx_axienet.c qemu-10.0.13+ds/hw/net/xilinx_axienet.c --- qemu-10.0.11+ds/hw/net/xilinx_axienet.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/net/xilinx_axienet.c 2026-08-26 19:31:25.000000000 +0000 @@ -103,6 +103,9 @@ case 17: /* Marvell PHY on many xilinx boards. */ r = 0x8000; /* 1000Mb */ + if (phy->link) { + r |= 0x0400; /* Link is up */ + } break; case 18: { @@ -915,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; - - /* Fold the 32bit partial checksum. */ - csum = net_checksum_finish(tmp_csum); - - /* Writeback. */ - buf[write_off] = csum >> 8; - buf[write_off + 1] = csum & 0xff; + 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; + + tmp_csum = net_checksum_add(s->txpos - start_off, + buf + start_off); + /* Accumulate the seed. */ + tmp_csum += s->hdr[2] & 0xffff; + + /* 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.11+ds/hw/nvme/Kconfig qemu-10.0.13+ds/hw/nvme/Kconfig --- qemu-10.0.11+ds/hw/nvme/Kconfig 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/nvme/Kconfig 2026-08-26 19:31:25.000000000 +0000 @@ -2,3 +2,4 @@ bool default y if PCI_DEVICES || PCIE_DEVICES depends on PCI + select SPDM_SOCKET diff -Nru qemu-10.0.11+ds/hw/nvme/ctrl.c qemu-10.0.13+ds/hw/nvme/ctrl.c --- qemu-10.0.11+ds/hw/nvme/ctrl.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/nvme/ctrl.c 2026-08-26 19:31:25.000000000 +0000 @@ -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" @@ -1085,6 +1086,8 @@ } for (;;) { + size_t prev_len = len; + switch (NVME_SGL_TYPE(sgld->type)) { case NVME_SGL_DESCR_TYPE_SEGMENT: case NVME_SGL_DESCR_TYPE_LAST_SEGMENT: @@ -1165,6 +1168,17 @@ if (status) { goto unmap; } + + /* + * Reject if this segment made no forward progress. The host should + * have skipped linking an empty segment. While not strictly spec + * compliant, allowing this makes it easy for a pathological host to + * create an infinite loop. + */ + if (len == prev_len) { + status = NVME_INVALID_SGL_SEG_DESCR | NVME_DNR; + goto unmap; + } } out: @@ -2777,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); @@ -3177,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; @@ -3298,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, @@ -4788,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; @@ -4822,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]; @@ -6240,10 +6271,6 @@ for (uint8_t event_type = 0; event_type < FDP_EVT_MAX; event_type++) { uint8_t shift = nvme_fdp_evf_shifts[event_type]; if (!shift && event_type) { - /* - * only first entry (event_type == 0) has a shift value of 0 - * other entries are simply unpopulated. - */ continue; } @@ -6488,9 +6515,9 @@ uint8_t noet = (cdw11 >> 16) & 0xff; uint16_t ret, ruhid; uint8_t enable = le32_to_cpu(cmd->cdw12) & 0x1; - uint8_t event_mask = 0; + uint64_t event_mask = 0; unsigned int i; - g_autofree uint8_t *events = g_malloc0(noet); + g_autofree uint8_t *events = NULL; NvmeRuHandle *ruh = NULL; assert(ns); @@ -6503,16 +6530,31 @@ return NVME_INVALID_FIELD | NVME_DNR; } + if (unlikely(noet == 0)) { + return NVME_SUCCESS; + } + ruhid = ns->fdp.phs[ph]; ruh = &n->subsys->endgrp.fdp.ruhs[ruhid]; + events = g_malloc0(noet); + ret = nvme_h2c(n, events, noet, req); if (ret) { return ret; } for (i = 0; i < noet; i++) { - event_mask |= (1 << nvme_fdp_evf_shifts[events[i]]); + /* + * We ignore requests to enable tracking of unsupported FDP event types + */ + uint8_t event_type = events[i]; + uint8_t shift = nvme_fdp_evf_shifts[event_type]; + if (!shift && event_type) { + continue; + } + event_mask = + deposit64(event_mask, nvme_fdp_evf_shifts[events[i]], 1, 1); } if (enable) { @@ -7616,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); @@ -9055,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.11+ds/hw/nvme/nvme.h qemu-10.0.13+ds/hw/nvme/nvme.h --- qemu-10.0.11+ds/hw/nvme/nvme.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/nvme/nvme.h 2026-08-26 19:31:25.000000000 +0000 @@ -160,7 +160,14 @@ #define NVME_FDP_MAX_NS_RUHS 32u #define FDPVSS 0 -static const uint8_t nvme_fdp_evf_shifts[FDP_EVT_MAX] = { +/* + * NOTE: Apart from event type 0, any event type with a shift value of 0 is + * considered unsupported and thus skipped in get/set features calls. + * + * NOTE: NvmeRuHandle uses a 64bit event mask - refactor to support event types + * of 63 or greater. + */ +static const uint8_t nvme_fdp_evf_shifts[FDP_EVT_MAX + 1] = { /* Host events */ [FDP_EVT_RU_NOT_FULLY_WRITTEN] = 0, [FDP_EVT_RU_ATL_EXCEEDED] = 1, diff -Nru qemu-10.0.11+ds/hw/pci/pcie_doe.c qemu-10.0.13+ds/hw/pci/pcie_doe.c --- qemu-10.0.11+ds/hw/pci/pcie_doe.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/pci/pcie_doe.c 2026-08-26 19:31:25.000000000 +0000 @@ -78,14 +78,21 @@ return true; } +static void pcie_doe_reset_write_mbox(DOECap *st) +{ + st->write_mbox_len = 0; + + memset(st->write_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE); +} + static void pcie_doe_reset_mbox(DOECap *st) { st->read_mbox_idx = 0; st->read_mbox_len = 0; - st->write_mbox_len = 0; memset(st->read_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE); - memset(st->write_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE); + + pcie_doe_reset_write_mbox(st); } void pcie_doe_init(PCIDevice *dev, DOECap *doe_cap, uint16_t offset, @@ -356,8 +363,20 @@ if (size != DWORD_BYTE) { return; } - doe_cap->write_mbox[doe_cap->write_mbox_len] = val; - doe_cap->write_mbox_len++; + if (doe_cap->write_mbox_len < PCI_DOE_DW_SIZE_MAX) { + doe_cap->write_mbox[doe_cap->write_mbox_len] = val; + doe_cap->write_mbox_len++; + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "Mailbox write length (%d) overflow\n", + doe_cap->write_mbox_len); + /* + * Too much data has been written, it can't + * "match the Length indicated in DOE Data Object Header 2" + * so we drop the entire object. + */ + pcie_doe_reset_write_mbox(doe_cap); + } break; case PCI_EXP_DOE_CAP: /* fallthrough */ diff -Nru qemu-10.0.11+ds/hw/pci-bridge/cxl_upstream.c qemu-10.0.13+ds/hw/pci-bridge/cxl_upstream.c --- qemu-10.0.11+ds/hw/pci-bridge/cxl_upstream.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/pci-bridge/cxl_upstream.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/pci-host/q35.c qemu-10.0.13+ds/hw/pci-host/q35.c --- qemu-10.0.11+ds/hw/pci-host/q35.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/pci-host/q35.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/riscv/riscv-iommu-bits.h qemu-10.0.13+ds/hw/riscv/riscv-iommu-bits.h --- qemu-10.0.11+ds/hw/riscv/riscv-iommu-bits.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/riscv/riscv-iommu-bits.h 2026-08-26 19:31:25.000000000 +0000 @@ -85,6 +85,7 @@ #define RISCV_IOMMU_CAP_SV57X4 BIT_ULL(19) #define RISCV_IOMMU_CAP_MSI_FLAT BIT_ULL(22) #define RISCV_IOMMU_CAP_MSI_MRIF BIT_ULL(23) +#define RISCV_IOMMU_CAP_AMO_HWAD BIT_ULL(24) #define RISCV_IOMMU_CAP_ATS BIT_ULL(25) #define RISCV_IOMMU_CAP_T2GPA BIT_ULL(26) #define RISCV_IOMMU_CAP_IGS GENMASK_ULL(29, 28) diff -Nru qemu-10.0.11+ds/hw/riscv/riscv-iommu-sys.c qemu-10.0.13+ds/hw/riscv/riscv-iommu-sys.c --- qemu-10.0.11+ds/hw/riscv/riscv-iommu-sys.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/riscv/riscv-iommu-sys.c 2026-08-26 19:31:25.000000000 +0000 @@ -27,6 +27,7 @@ #include "qemu/module.h" #include "qom/object.h" #include "exec/exec-all.h" +#include "target/riscv/cpu_bits.h" #include "trace.h" #include "riscv-iommu.h" @@ -150,7 +151,20 @@ address_space_stl_le(&address_space_memory, msi_addr, msi_data, MEMTXATTRS_UNSPECIFIED, &result); - trace_riscv_iommu_sys_msi_sent(vector, msi_addr, msi_data, result); + + if (result == MEMTX_OK) { + trace_riscv_iommu_sys_msi_sent(vector, msi_addr, msi_data, result); + } else { + /* Record an access fault error in the fault queue */ + struct riscv_iommu_fq_record ev = { 0 }; + RISCVIOMMUState *iommu = &s->iommu; + + ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_CAUSE, + RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT); + ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_TTYPE, + RISCV_IOMMU_FQ_TTYPE_UADDR_WR); + riscv_iommu_fault(iommu, &ev); + } } static void riscv_iommu_sysdev_notify(RISCVIOMMUState *iommu, diff -Nru qemu-10.0.11+ds/hw/riscv/riscv-iommu.c qemu-10.0.13+ds/hw/riscv/riscv-iommu.c --- qemu-10.0.11+ds/hw/riscv/riscv-iommu.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/riscv/riscv-iommu.c 2026-08-26 19:31:25.000000000 +0000 @@ -46,7 +46,8 @@ IOMMUMemoryRegion iova_mr; /* IOVA memory region for attached device */ AddressSpace iova_as; /* IOVA address space for attached device */ RISCVIOMMUState *iommu; /* Managing IOMMU device state */ - uint32_t devid; /* Requester identifier, AKA device_id */ + PCIBus *bus; /* PCI bus of the requester */ + uint8_t devfn; /* Requester identifier, AKA device_id */ bool notifier; /* IOMMU unmap notifier enabled */ QLIST_ENTRY(RISCVIOMMUSpace) list; }; @@ -71,6 +72,15 @@ /* IOMMU index for transactions without process_id specified. */ #define RISCV_IOMMU_NOPROCID 0 +static uint32_t riscv_iommu_space_devid(RISCVIOMMUSpace *as) +{ + uint32_t devid = PCI_BUILD_BDF(pci_bus_num(as->bus), as->devfn); + + /* FIXME: PCIe bus remapping for attached endpoints. */ + devid |= as->iommu->bus << 8; + return devid; +} + static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type) { switch (vec_type) { @@ -105,8 +115,7 @@ } } -static void riscv_iommu_fault(RISCVIOMMUState *s, - struct riscv_iommu_fq_record *ev) +void riscv_iommu_fault(RISCVIOMMUState *s, struct riscv_iommu_fq_record *ev) { uint32_t ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR); uint32_t head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQH) & s->fq_mask; @@ -269,6 +278,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, IOMMUTLBEntry *iotlb) { + IOMMUAccessFlags trans_perm = IOMMU_NONE; dma_addr_t addr, base; uint64_t satp, gatp, pte; bool en_s, en_g; @@ -284,6 +294,15 @@ G_STAGE = 1, } 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); @@ -302,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; @@ -403,6 +422,11 @@ const bool ade = ctx->tc & (pass ? RISCV_IOMMU_DC_TC_GADE : RISCV_IOMMU_DC_TC_SADE); + if (ade && !(s->cap & RISCV_IOMMU_CAP_AMO_HWAD)) { + /* GADE/SADE are reserved bits if AMO_HWAD is cleared. */ + return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; + } + /* Address range check before first level lookup */ if (!sc[pass].step) { const uint64_t va_len = va_skip + va_bits; @@ -415,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; } @@ -446,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++; @@ -455,6 +479,8 @@ if (!(pte & PTE_V)) { break; /* Invalid PTE */ + } else if (pte & PTE_RESERVED) { + break; /* Reserved PTE bits set */ } 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) { @@ -463,14 +489,31 @@ 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 ((iotlb->perm & IOMMU_RO) && !ade && !(pte & PTE_A)) { + } 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)) { + /* + * riscv-iommu spec 1.0: "When checking the U bit in a + * second-stage PTE, the transaction is treated as + * not requesting supervisor privilege." + * + * I.e. we need to fault if this is a non-user PTE since + * we are always in user mode at this point. + */ + break; } else { /* Leaf PTE, translation completed. */ sc[pass].step = sc[pass].levels; @@ -494,13 +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; - iotlb->perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO) - : IOMMU_RO; /* 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; @@ -517,6 +567,7 @@ continue; } + iotlb->perm = trans_perm; return 0; } @@ -533,7 +584,15 @@ } } while (1); - return (iotlb->perm & IOMMU_WO) ? + /* + * riscv_iommu_translate() will receive a fault and then call + * riscv_iommu_report_fault() using iotlb->translated_addr + * as iotval2. Update translated_addr it with the latest + * translated addr we have. + */ + iotlb->translated_addr = addr; + + 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 : @@ -635,6 +694,27 @@ switch (get_field(pte[0], RISCV_IOMMU_MSI_PTE_M)) { case RISCV_IOMMU_MSI_PTE_M_BASIC: + /* + * riscv-iommu spec MSI PTE basic translate mode: + * "When an MSI PTE has fields V = 1, C = 0, and M = 3 + * (basic translate mode), the PTE's complete format is: + * First doubleword: bit 63 C, = 0 + * bits 53:10 PPN + * bits 2:1 M, = 3 + * bit 0 V, = 1 + * All other bits of the first doubleword are reserved + * and must be set to zeros by software. The second + * doubleword is ignored by an IOMMU so is free for + * software to use." + * + * In other words, bits 62:54 and 9:3 of pte[0] are reserved. + */ + if (pte[0] & (GENMASK_ULL(62, 54) | GENMASK_ULL(9, 3))) { + res = MEMTX_DECODE_ERROR; + cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED; + goto err; + } + /* MSI Pass-through mode */ addr = PPN_PHYS(get_field(pte[0], RISCV_IOMMU_MSI_PTE_PPN)); @@ -777,6 +857,21 @@ return false; } + if (gatp != RISCV_IOMMU_DC_IOHGATP_MODE_BARE) { + uint64_t iohgatp_ppn = get_field(ctx->gatp, + RISCV_IOMMU_DC_IOHGATP_PPN); + + /* + * One of the conditions for a misconfigured DDT entry + * according to the riscv-spec: "DC.iohgatp.MODE is not + * Bare and the root page table (address) determined by + * DC.iohgatp.PPN is not aligned to a 16-KiB boundary." + */ + if (PPN_PHYS(iohgatp_ppn) & ((1ULL << 14) - 1)) { + return false; + } + } + fsc_mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE); if (ctx->tc & RISCV_IOMMU_DC_TC_PDTV) { @@ -1317,6 +1412,7 @@ /* Find or allocate translation context for a given {device_id, process_id} */ static RISCVIOMMUContext *riscv_iommu_ctx(RISCVIOMMUState *s, unsigned devid, unsigned process_id, + IOMMUAccessFlags perm, uint64_t iova, void **ref) { GHashTable *ctx_cache; @@ -1325,13 +1421,19 @@ .devid = devid, .process_id = process_id, }; + unsigned mode = get_field(s->ddtp, RISCV_IOMMU_DDTP_MODE); + uint32_t fault_type; ctx_cache = g_hash_table_ref(s->ctx_cache); - ctx = g_hash_table_lookup(ctx_cache, &key); - if (ctx && (ctx->tc & RISCV_IOMMU_DC_TC_V)) { - *ref = ctx_cache; - return ctx; + if (mode != RISCV_IOMMU_DDTP_MODE_OFF && + mode != RISCV_IOMMU_DDTP_MODE_BARE) { + ctx = g_hash_table_lookup(ctx_cache, &key); + + if (ctx && (ctx->tc & RISCV_IOMMU_DC_TC_V)) { + *ref = ctx_cache; + return ctx; + } } ctx = g_new0(RISCVIOMMUContext, 1); @@ -1356,8 +1458,21 @@ g_hash_table_unref(ctx_cache); *ref = NULL; - riscv_iommu_report_fault(s, ctx, RISCV_IOMMU_FQ_TTYPE_UADDR_RD, - fault, !!process_id, 0, 0); + /* + * TODO: (1) do we need to distinguish other fault types + * for ctx fetching and (2) evaluate putting the 'fault_type' + * logic inside riscv_iommu_report_fault() - there's at + * least one other place (end of riscv_iommu_translate()) + * that does something similar. + */ + if (perm & IOMMU_RO) { + fault_type = RISCV_IOMMU_FQ_TTYPE_UADDR_RD; + } else { + fault_type = RISCV_IOMMU_FQ_TTYPE_UADDR_WR; + } + + riscv_iommu_report_fault(s, ctx, fault_type, fault, + !!process_id, iova, 0); g_free(ctx); return NULL; @@ -1371,15 +1486,13 @@ } /* Find or allocate address space for a given device */ -static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid) +static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, PCIBus *bus, + int devfn) { RISCVIOMMUSpace *as; - /* FIXME: PCIe bus remapping for attached endpoints. */ - devid |= s->bus << 8; - QLIST_FOREACH(as, &s->spaces, list) { - if (as->devid == devid) { + if (as->bus == bus && as->devfn == devfn) { break; } } @@ -1389,21 +1502,22 @@ as = g_new0(RISCVIOMMUSpace, 1); as->iommu = s; - as->devid = devid; + as->bus = bus; + as->devfn = devfn; snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova", - PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid)); + pci_bus_num(bus), PCI_SLOT(devfn), PCI_FUNC(devfn)); /* IOVA address space, untranslated addresses */ memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr), TYPE_RISCV_IOMMU_MEMORY_REGION, - OBJECT(as), "riscv_iommu", UINT64_MAX); + OBJECT(s), "riscv_iommu", UINT64_MAX); address_space_init(&as->iova_as, MEMORY_REGION(&as->iova_mr), name); QLIST_INSERT_HEAD(&s->spaces, as, list); - trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid), - PCI_SLOT(as->devid), PCI_FUNC(as->devid)); + trace_riscv_iommu_new(s->parent_obj.id, pci_bus_num(bus), + PCI_SLOT(devfn), PCI_FUNC(devfn)); } return &as->iova_as; } @@ -1674,7 +1788,8 @@ if (fault) { unsigned ttype = RISCV_IOMMU_FQ_TTYPE_PCIE_ATS_REQ; - if (iotlb->perm & IOMMU_RW) { + if ((iotlb->perm & IOMMU_RW) == IOMMU_RW + || iotlb->perm & IOMMU_WO) { ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_WR; } else if (iotlb->perm & IOMMU_RO) { ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_RD; @@ -1727,7 +1842,7 @@ pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID); QLIST_FOREACH(as, &s->spaces, list) { - if (as->devid == devid) { + if (riscv_iommu_space_devid(as) == devid) { break; } } @@ -2088,6 +2203,8 @@ uint64_t ctrl = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_CTL); unsigned devid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_DID); unsigned pid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_PID); + IOMMUAccessFlags perm = ctrl & RISCV_IOMMU_TR_REQ_CTL_NW + ? IOMMU_RO : IOMMU_RW; RISCVIOMMUContext *ctx; void *ref; @@ -2095,7 +2212,7 @@ return; } - ctx = riscv_iommu_ctx(s, devid, pid, &ref); + ctx = riscv_iommu_ctx(s, devid, pid, perm, iova, &ref); if (ctx == NULL) { riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, RISCV_IOMMU_TR_RESPONSE_FAULT | @@ -2103,7 +2220,7 @@ } else { IOMMUTLBEntry iotlb = { .iova = iova, - .perm = ctrl & RISCV_IOMMU_TR_REQ_CTL_NW ? IOMMU_RO : IOMMU_RW, + .perm = perm, .addr_mask = ~0, .target_as = NULL, }; @@ -2442,7 +2559,7 @@ /* FIXME: PCIe bus remapping for attached endpoints. */ devid |= s->bus << 8; - ctx = riscv_iommu_ctx(s, devid, 0, &ref); + ctx = riscv_iommu_ctx(s, devid, 0, IOMMU_RW, addr, &ref); if (ctx == NULL) { res = MEMTX_ACCESS_ERROR; } else { @@ -2730,8 +2847,9 @@ .addr_mask = ~0ULL, .perm = flag, }; + uint32_t devid = riscv_iommu_space_devid(as); - ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref); + ctx = riscv_iommu_ctx(as->iommu, devid, iommu_idx, flag, addr, &ref); if (ctx == NULL) { /* Translation disabled or invalid. */ iotlb.addr_mask = 0; @@ -2743,8 +2861,8 @@ } /* Trace all dma translations with original access flags. */ - trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid), - PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx, + trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(devid), + PCI_SLOT(devid), PCI_FUNC(devid), iommu_idx, IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova, iotlb.translated_addr); @@ -2792,7 +2910,7 @@ /* Find first matching IOMMU */ while (s != NULL && as == NULL) { - as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn)); + as = riscv_iommu_space(s, bus, devfn); s = s->iommus.le_next; } diff -Nru qemu-10.0.11+ds/hw/riscv/riscv-iommu.h qemu-10.0.13+ds/hw/riscv/riscv-iommu.h --- qemu-10.0.11+ds/hw/riscv/riscv-iommu.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/riscv/riscv-iommu.h 2026-08-26 19:31:25.000000000 +0000 @@ -98,6 +98,7 @@ void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode); void riscv_iommu_reset(RISCVIOMMUState *s); void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type); +void riscv_iommu_fault(RISCVIOMMUState *s, struct riscv_iommu_fq_record *ev); typedef struct RISCVIOMMUContext RISCVIOMMUContext; /* Device translation context state. */ diff -Nru qemu-10.0.11+ds/hw/riscv/virt-acpi-build.c qemu-10.0.13+ds/hw/riscv/virt-acpi-build.c --- qemu-10.0.11+ds/hw/riscv/virt-acpi-build.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/riscv/virt-acpi-build.c 2026-08-26 19:31:25.000000000 +0000 @@ -100,6 +100,8 @@ build_append_int_noprefix(entry, ACPI_BUILD_INTC_ID( arch_ids->cpus[uid].props.node_id, + kvm_enabled() ? + local_cpu_id : 2 * local_cpu_id + 1), 4); } else { diff -Nru qemu-10.0.11+ds/hw/s390x/css.c qemu-10.0.13+ds/hw/s390x/css.c --- qemu-10.0.11+ds/hw/s390x/css.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/s390x/css.c 2026-08-26 19:31:25.000000000 +0000 @@ -1104,6 +1104,12 @@ ret = -EINVAL; break; } + /* Limit the number of TICs in a given channel program */ + if (sch->ccw_tic_cnt == 255) { + ret = -EINVAL; + break; + } + sch->ccw_tic_cnt++; sch->channel_prog = ccw.cda; ret = -EAGAIN; break; @@ -1155,6 +1161,7 @@ sch->ccw_fmt_1 = !!(orb->ctrl0 & ORB_CTRL0_MASK_FMT); schib->scsw.flags |= (sch->ccw_fmt_1) ? SCSW_FLAGS_MASK_FMT : 0; sch->ccw_no_data_cnt = 0; + sch->ccw_tic_cnt = 0; suspend_allowed = !!(orb->ctrl0 & ORB_CTRL0_MASK_SPND); } else { /* Start Function resumed via rsch */ @@ -1900,6 +1907,7 @@ int i, desc_size; uint32_t words[8]; uint32_t chpid_type_word; + uint32_t max_chpids, chpid_count = 0; CssImage *css; if (!m && !cssid) { @@ -1910,9 +1918,25 @@ if (!css) { return 0; } + + if (rfmt == 0) { + max_chpids = 256; + } else if (rfmt == 1) { + max_chpids = 127; + } else { + /* Should be rejected by caller */ + return 0; + } + desc_size = 0; for (i = f_chpid; i <= l_chpid; i++) { if (css->chpids[i].in_use) { + /* Limit number of CHPIDs sent back */ + if (chpid_count == max_chpids) { + break; + } + + chpid_count++; chpid_type_word = 0x80000000 | (css->chpids[i].type << 8) | i; if (rfmt == 0) { words[0] = cpu_to_be32(chpid_type_word); diff -Nru qemu-10.0.11+ds/hw/s390x/event-facility.c qemu-10.0.13+ds/hw/s390x/event-facility.c --- qemu-10.0.11+ds/hw/s390x/event-facility.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/s390x/event-facility.c 2026-08-26 19:31:25.000000000 +0000 @@ -291,6 +291,7 @@ static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb) { WriteEventMask *we_mask = (WriteEventMask *) sccb; + uint16_t sccb_length = be16_to_cpu(sccb->h.length); uint16_t mask_length = be16_to_cpu(we_mask->mask_length); sccb_mask_t tmp_mask; @@ -300,6 +301,11 @@ return; } + if (sccb_length < sizeof(WriteEventMask) + 4 * mask_length) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH); + return; + } + /* * Note: We currently only support masks up to 8 byte length; * the remainder is filled up with zeroes. Older Linux diff -Nru qemu-10.0.11+ds/hw/s390x/ipl.h qemu-10.0.13+ds/hw/s390x/ipl.h --- qemu-10.0.11+ds/hw/s390x/ipl.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/s390x/ipl.h 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/s390x/s390-pci-inst.c qemu-10.0.13+ds/hw/s390x/s390-pci-inst.c --- qemu-10.0.11+ds/hw/s390x/s390-pci-inst.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/s390x/s390-pci-inst.c 2026-08-26 19:31:25.000000000 +0000 @@ -390,13 +390,22 @@ static MemoryRegion *s390_get_subregion(MemoryRegion *mr, uint64_t offset, uint8_t len) { + uint64_t last = offset + len; MemoryRegion *subregion; uint64_t subregion_size; + /* + * Ensure the region is valid, the calculated address cannot wrap and that + * it falls within this region. + */ + if (!mr || offset > last || last > memory_region_size(mr)) { + return NULL; + } + QTAILQ_FOREACH(subregion, &mr->subregions, subregions_link) { subregion_size = int128_get64(subregion->size); if ((offset >= subregion->addr) && - (offset + len) <= (subregion->addr + subregion_size)) { + (last) <= (subregion->addr + subregion_size)) { mr = subregion; break; } @@ -411,6 +420,10 @@ mr = pbdev->pdev->io_regions[pcias].memory; mr = s390_get_subregion(mr, offset, len); + if (!mr) { + return MEMTX_ERROR; + } + offset -= mr->addr; return memory_region_dispatch_read(mr, offset, data, size_memop(len) | MO_BE, @@ -511,6 +524,10 @@ mr = pbdev->pdev->io_regions[pcias].memory; mr = s390_get_subregion(mr, offset, len); + if (!mr) { + return MEMTX_ERROR; + } + offset -= mr->addr; return memory_region_dispatch_write(mr, offset, data, size_memop(len) | MO_BE, @@ -751,10 +768,16 @@ goto err; } - if (end < iommu->pba || start > iommu->pal) { + if (end < start || end < iommu->pba || start > iommu->pal) { error = ERR_EVENT_OORANGE; goto err; } + /* + * If the specified range at least partially overlaps the registered + * aperture, clamp the request to the aperture and ignore the rest. + */ + sstart = MAX(start, iommu->pba); + end = MIN(end, iommu->pal + 1); retry: start = sstart; @@ -898,6 +921,11 @@ mr = pbdev->pdev->io_regions[pcias].memory; mr = s390_get_subregion(mr, offset, len); + if (!mr) { + s390_program_interrupt(env, PGM_OPERAND, ra); + return 0; + } + offset -= mr->addr; for (i = 0; i < len; i += 8) { diff -Nru qemu-10.0.11+ds/hw/s390x/sclp.c qemu-10.0.13+ds/hw/s390x/sclp.c --- qemu-10.0.11+ds/hw/s390x/sclp.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/s390x/sclp.c 2026-08-26 19:31:25.000000000 +0000 @@ -303,6 +303,9 @@ SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); SCCBHeader header; g_autofree SCCB *work_sccb = NULL; + AddressSpace *as = CPU(cpu)->as; + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; + MemTxResult ret; /* first some basic checks on program checks */ if (env->psw.mask & PSW_MASK_PSTATE) { @@ -317,7 +320,10 @@ } /* the header contains the actual length of the sccb */ - cpu_physical_memory_read(sccb, &header, sizeof(SCCBHeader)); + ret = address_space_read(as, sccb, attrs, &header, sizeof(SCCBHeader)); + if (ret != MEMTX_OK) { + return -PGM_ADDRESSING; + } /* Valid sccb sizes */ if (be16_to_cpu(header.length) < sizeof(SCCBHeader)) { @@ -327,10 +333,16 @@ /* * we want to work on a private copy of the sccb, to prevent guests * from playing dirty tricks by modifying the memory content after - * the host has checked the values + * the host has checked the values. + * Reuse the previously fetched header */ work_sccb = g_malloc0(be16_to_cpu(header.length)); - cpu_physical_memory_read(sccb, work_sccb, be16_to_cpu(header.length)); + ret = address_space_read(as, sccb, attrs, + work_sccb, be16_to_cpu(header.length)); + if (ret != MEMTX_OK) { + return -PGM_ADDRESSING; + } + work_sccb->h = header; if (!sclp_command_code_valid(code)) { work_sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); @@ -344,8 +356,11 @@ sclp_c->execute(sclp, work_sccb, code); out_write: - cpu_physical_memory_write(sccb, work_sccb, - be16_to_cpu(work_sccb->h.length)); + ret = address_space_write(as, sccb, attrs, + work_sccb, be16_to_cpu(header.length)); + if (ret != MEMTX_OK) { + return -PGM_PROTECTION; + } sclp_c->service_interrupt(sclp, sccb); diff -Nru qemu-10.0.11+ds/hw/scsi/mptsas.c qemu-10.0.13+ds/hw/scsi/mptsas.c --- qemu-10.0.11+ds/hw/scsi/mptsas.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/scsi/mptsas.c 2026-08-26 19:31:25.000000000 +0000 @@ -811,6 +811,10 @@ s->intr_status = 0; s->intr_mask = save_mask; + s->doorbell_state = DOORBELL_NONE; + s->doorbell_reply_idx = 0; + s->doorbell_reply_size = 0; + s->reply_free_tail = 0; s->reply_free_head = 0; s->reply_post_tail = 0; diff -Nru qemu-10.0.11+ds/hw/scsi/scsi-bus.c qemu-10.0.13+ds/hw/scsi/scsi-bus.c --- qemu-10.0.11+ds/hw/scsi/scsi-bus.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/scsi/scsi-bus.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/scsi/scsi-disk.c qemu-10.0.13+ds/hw/scsi/scsi-disk.c --- qemu-10.0.11+ds/hw/scsi/scsi-disk.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/scsi/scsi-disk.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/scsi/virtio-scsi.c qemu-10.0.13+ds/hw/scsi/virtio-scsi.c --- qemu-10.0.11+ds/hw/scsi/virtio-scsi.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/scsi/virtio-scsi.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/scsi/vmw_pvscsi.c qemu-10.0.13+ds/hw/scsi/vmw_pvscsi.c --- qemu-10.0.11+ds/hw/scsi/vmw_pvscsi.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/scsi/vmw_pvscsi.c 2026-08-26 19:31:25.000000000 +0000 @@ -404,9 +404,18 @@ pvscsi_cmp_ring_put(PVSCSIState *s, struct PVSCSIRingCmpDesc *cmp_desc) { hwaddr cmp_descr_pa; + PVSCSIRingCmpDesc cmp_desc_conv; cmp_descr_pa = pvscsi_ring_pop_cmp_descr(&s->rings); trace_pvscsi_cmp_ring_put(cmp_descr_pa); + cmp_desc_conv = (struct PVSCSIRingCmpDesc) { + .context = cpu_to_le64(cmp_desc->context), + .dataLen = cpu_to_le64(cmp_desc->dataLen), + .senseLen = cpu_to_le32(cmp_desc->senseLen), + .hostStatus = cpu_to_le16(cmp_desc->hostStatus), + .scsiStatus = cpu_to_le16(cmp_desc->scsiStatus), + }; + cmp_desc = &cmp_desc_conv; cpu_physical_memory_write(cmp_descr_pa, cmp_desc, sizeof(*cmp_desc)); } @@ -414,9 +423,18 @@ pvscsi_msg_ring_put(PVSCSIState *s, struct PVSCSIRingMsgDesc *msg_desc) { hwaddr msg_descr_pa; + PVSCSIRingMsgDesc msg_desc_conv; + int i; msg_descr_pa = pvscsi_ring_pop_msg_descr(&s->rings); trace_pvscsi_msg_ring_put(msg_descr_pa); + msg_desc_conv = (PVSCSIRingMsgDesc) { + .type = cpu_to_le32(msg_desc->type), + }; + for (i = 0; i < ARRAY_SIZE(msg_desc->args); i++) { + msg_desc_conv.args[i] = cpu_to_le32(msg_desc->args[i]); + } + msg_desc = &msg_desc_conv; cpu_physical_memory_write(msg_descr_pa, msg_desc, sizeof(*msg_desc)); } @@ -493,6 +511,9 @@ struct PVSCSISGElement elem; cpu_physical_memory_read(sg->elemAddr, &elem, sizeof(elem)); + elem.addr = le64_to_cpu(elem.addr); + elem.length = le32_to_cpu(elem.length); + elem.flags = le32_to_cpu(elem.flags); if ((elem.flags & ~PVSCSI_KNOWN_FLAGS) != 0) { /* * There is PVSCSI_SGE_FLAG_CHAIN_ELEMENT flag described in @@ -771,6 +792,12 @@ trace_pvscsi_process_io(next_descr_pa); cpu_physical_memory_read(next_descr_pa, &descr, sizeof(descr)); + descr.context = le64_to_cpu(descr.context); + descr.dataAddr = le64_to_cpu(descr.dataAddr); + descr.dataLen = le64_to_cpu(descr.dataLen); + descr.senseAddr = le64_to_cpu(descr.senseAddr); + descr.senseLen = le32_to_cpu(descr.senseLen); + descr.flags = le32_to_cpu(descr.flags); pvscsi_process_request_descriptor(s, &descr); } @@ -820,6 +847,17 @@ { PVSCSICmdDescSetupRings *rc = (PVSCSICmdDescSetupRings *) s->curr_cmd_data; + PVSCSICmdDescSetupRings translated; + int i; + + translated.reqRingNumPages = le32_to_cpu(rc->reqRingNumPages); + translated.cmpRingNumPages = le32_to_cpu(rc->cmpRingNumPages); + translated.ringsStatePPN = le64_to_cpu(rc->ringsStatePPN); + for (i = 0; i < PVSCSI_SETUP_RINGS_MAX_NUM_PAGES; i++) { + translated.reqRingPPNs[i] = le64_to_cpu(rc->reqRingPPNs[i]); + translated.cmpRingPPNs[i] = le64_to_cpu(rc->cmpRingPPNs[i]); + } + rc = &translated; trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_RINGS"); @@ -843,6 +881,11 @@ PVSCSICmdDescAbortCmd *cmd = (PVSCSICmdDescAbortCmd *) s->curr_cmd_data; PVSCSIRequest *r, *next; + PVSCSICmdDescAbortCmd translated = *cmd; + translated.context = le32_to_cpu(cmd->context); + translated.target = le32_to_cpu(cmd->target); + cmd = &translated; + trace_pvscsi_on_cmd_abort(cmd->context, cmd->target); QTAILQ_FOREACH_SAFE(r, &s->pending_queue, next, next) { @@ -874,6 +917,10 @@ (struct PVSCSICmdDescResetDevice *) s->curr_cmd_data; SCSIDevice *sdev; + PVSCSICmdDescResetDevice translated = *cmd; + translated.target = le32_to_cpu(cmd->target); + cmd = &translated; + sdev = pvscsi_device_find(s, 0, cmd->target, cmd->lun, &target_lun); trace_pvscsi_on_cmd_reset_dev(cmd->target, (int) target_lun, sdev); @@ -904,6 +951,14 @@ { PVSCSICmdDescSetupMsgRing *rc = (PVSCSICmdDescSetupMsgRing *) s->curr_cmd_data; + PVSCSICmdDescSetupMsgRing translated = *rc; + int i; + + translated.numPages = le32_to_cpu(rc->numPages); + for (i = 0; i < PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES; i++) { + translated.ringPPNs[i] = le64_to_cpu(rc->ringPPNs[i]); + } + rc = &translated; trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_MSG_RING"); @@ -1006,7 +1061,7 @@ size_t bytes_arrived = s->curr_cmd_data_cntr * sizeof(uint32_t); assert(bytes_arrived < sizeof(s->curr_cmd_data)); - s->curr_cmd_data[s->curr_cmd_data_cntr++] = value; + s->curr_cmd_data[s->curr_cmd_data_cntr++] = cpu_to_le32(value); pvscsi_do_command_processing(s); } diff -Nru qemu-10.0.11+ds/hw/scsi/vmw_pvscsi.h qemu-10.0.13+ds/hw/scsi/vmw_pvscsi.h --- qemu-10.0.11+ds/hw/scsi/vmw_pvscsi.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/scsi/vmw_pvscsi.h 2026-08-26 19:31:25.000000000 +0000 @@ -109,6 +109,20 @@ #define PVSCSI_COMMAND_NOT_ENOUGH_DATA (-2) /* + * About endianess for the below structs: + * + * These structs are used to describe the data that is exchanged between the + * guest and the PVSCSI device. The endianess of the fields in these structs + * is not defined by any standard. The current implemented drivers are designed + * to only work on x86 architecture, so there is no endianess awareness in the + * drivers and thus we have no idea whether the fields should be in little- + * endian or target native endian format. + * + * Considering the above, we assume that PVSCSI is implicitly little-endian and + * expect the fields in these structs to be in little-endian format. + */ + +/* * Command descriptor for PVSCSI_CMD_RESET_DEVICE -- */ diff -Nru qemu-10.0.11+ds/hw/sparc64/niagara.c qemu-10.0.13+ds/hw/sparc64/niagara.c --- qemu-10.0.11+ds/hw/sparc64/niagara.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/sparc64/niagara.c 2026-08-26 19:31:25.000000000 +0000 @@ -137,7 +137,7 @@ outside of the partition RAM */ if (dinfo) { BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - int size = blk_getlength(blk); + int64_t size = blk_getlength(blk); if (size > 0) { memory_region_init_ram(&s->vdisk_ram, NULL, "sun4v_vdisk.ram", size, &error_fatal); diff -Nru qemu-10.0.11+ds/hw/uefi/var-service-auth.c qemu-10.0.13+ds/hw/uefi/var-service-auth.c --- qemu-10.0.11+ds/hw/uefi/var-service-auth.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/uefi/var-service-auth.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/uefi/var-service-core.c qemu-10.0.13+ds/hw/uefi/var-service-core.c --- qemu-10.0.11+ds/hw/uefi/var-service-core.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/uefi/var-service-core.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/uefi/var-service-json.c qemu-10.0.13+ds/hw/uefi/var-service-json.c --- qemu-10.0.11+ds/hw/uefi/var-service-json.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/uefi/var-service-json.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/uefi/var-service-policy.c qemu-10.0.13+ds/hw/uefi/var-service-policy.c --- qemu-10.0.11+ds/hw/uefi/var-service-policy.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/uefi/var-service-policy.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/uefi/var-service-siglist.c qemu-10.0.13+ds/hw/uefi/var-service-siglist.c --- qemu-10.0.11+ds/hw/uefi/var-service-siglist.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/uefi/var-service-siglist.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/uefi/var-service-vars.c qemu-10.0.13+ds/hw/uefi/var-service-vars.c --- qemu-10.0.11+ds/hw/uefi/var-service-vars.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/uefi/var-service-vars.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/ufs/lu.c qemu-10.0.13+ds/hw/ufs/lu.c --- qemu-10.0.11+ds/hw/ufs/lu.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/ufs/lu.c 2026-08-26 19:31:25.000000000 +0000 @@ -412,10 +412,7 @@ { UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev); - if (lu->scsi_dev) { - object_unref(OBJECT(lu->scsi_dev)); - lu->scsi_dev = NULL; - } + lu->scsi_dev = NULL; } static void ufs_lu_class_init(ObjectClass *oc, void *data) diff -Nru qemu-10.0.11+ds/hw/usb/core.c qemu-10.0.13+ds/hw/usb/core.c --- qemu-10.0.11+ds/hw/usb/core.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/usb/core.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/usb/dev-uas.c qemu-10.0.13+ds/hw/usb/dev-uas.c --- qemu-10.0.11+ds/hw/usb/dev-uas.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/usb/dev-uas.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/usb/dev-wacom.c qemu-10.0.13+ds/hw/usb/dev-wacom.c --- qemu-10.0.11+ds/hw/usb/dev-wacom.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/usb/dev-wacom.c 2026-08-26 19:31:25.000000000 +0000 @@ -252,6 +252,10 @@ if (s->buttons_state & MOUSE_EVENT_MBUTTON) b |= 0x04; + if (len < 3) { + return 0; + } + buf[0] = b; buf[1] = dx; buf[2] = dy; diff -Nru qemu-10.0.11+ds/hw/usb/hcd-ohci.c qemu-10.0.13+ds/hw/usb/hcd-ohci.c --- qemu-10.0.11+ds/hw/usb/hcd-ohci.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/usb/hcd-ohci.c 2026-08-26 19:31:25.000000000 +0000 @@ -28,6 +28,7 @@ #include "qemu/osdep.h" #include "hw/irq.h" #include "qapi/error.h" +#include "qemu/log.h" #include "qemu/module.h" #include "qemu/timer.h" #include "hw/usb.h" @@ -1130,6 +1131,8 @@ return 0; } for (cur = head; cur && link_cnt++ < ED_LINK_LIMIT; cur = next_ed) { + unsigned int ed_cnt = 0; + if (ohci_read_ed(ohci, cur, &ed)) { trace_usb_ohci_ed_read_error(cur); ohci_die(ohci); @@ -1173,6 +1176,13 @@ break; } } + + if (ed_cnt++ > ED_LINK_LIMIT) { + qemu_log_mask(LOG_GUEST_ERROR, + "ohci: Too many endpoint descriptors in loop\n"); + ohci_die(ohci); + return 0; + } } if (ohci_put_ed(ohci, cur, &ed)) { diff -Nru qemu-10.0.11+ds/hw/usb/hcd-xhci-pci.c qemu-10.0.13+ds/hw/usb/hcd-xhci-pci.c --- qemu-10.0.11+ds/hw/usb/hcd-xhci-pci.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/usb/hcd-xhci-pci.c 2026-08-26 19:31:25.000000000 +0000 @@ -190,6 +190,18 @@ && dev->msix_entry_used) { msix_uninit(dev, &s->xhci.mem, &s->xhci.mem); } + /* + * The embedded xhci-core child holds a strong "host" link back to this + * PCI device (set in usb_xhci_pci_realize()), forming a refcount cycle: + * the PCI device owns the child, and the child's strong link pins the PCI + * device. On unplug, object_unparent() only drops the parent/bus refs, so + * the link ref keeps this device at refcount 1 forever and + * device_finalize() never runs. Unrealize the child first (so the + * realized-check in set_link passes), then clear the link to break the + * cycle. + */ + qdev_unrealize(DEVICE(&s->xhci)); + object_property_set_link(OBJECT(&s->xhci), "host", NULL, &error_abort); } static const VMStateDescription vmstate_xhci_pci = { diff -Nru qemu-10.0.11+ds/hw/usb/hcd-xhci-sysbus.c qemu-10.0.13+ds/hw/usb/hcd-xhci-sysbus.c --- qemu-10.0.11+ds/hw/usb/hcd-xhci-sysbus.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/usb/hcd-xhci-sysbus.c 2026-08-26 19:31:25.000000000 +0000 @@ -20,6 +20,7 @@ { XHCISysbusState *s = container_of(xhci, XHCISysbusState, xhci); + assert(n < xhci->numintrs); qemu_set_irq(s->irq[n], level); return false; diff -Nru qemu-10.0.11+ds/hw/usb/hcd-xhci.c qemu-10.0.13+ds/hw/usb/hcd-xhci.c --- qemu-10.0.11+ds/hw/usb/hcd-xhci.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/usb/hcd-xhci.c 2026-08-26 19:31:25.000000000 +0000 @@ -965,11 +965,13 @@ * together and make an usb_device_alloc_streams call per group. */ if (epctxs[i]->nr_pstreams != req_nr_streams) { - FIXME("guest streams config not identical for all eps"); + qemu_log_mask(LOG_UNIMP, + "guest streams config not identical for all eps\n"); return CC_RESOURCE_ERROR; } if (eps[i]->max_streams != dev_max_streams) { - FIXME("device streams config not identical for all eps"); + qemu_log_mask(LOG_UNIMP, + "device streams config not identical for all eps\n"); return CC_RESOURCE_ERROR; } } @@ -1009,7 +1011,12 @@ dma_addr_t base; uint32_t ctx[2], sct; - assert(streamid != 0); + if (!streamid) { + qemu_log_mask(LOG_GUEST_ERROR, "xhci: stream ID is zero\n"); + *cc_error = CC_INVALID_STREAM_ID_ERROR; + return NULL; + } + if (epctx->lsa) { if (streamid >= epctx->nr_pstreams) { *cc_error = CC_INVALID_STREAM_ID_ERROR; @@ -1120,7 +1127,7 @@ epctx->ring.ccs = ctx[2] & 1; } - epctx->interval = 1 << ((ctx[0] >> 16) & 0xff); + epctx->interval = 1u << MIN((ctx[0] >> 16) & 0xffu, 18u); } static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, @@ -1451,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 */ @@ -1461,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); @@ -1610,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)) { @@ -3039,6 +3050,12 @@ } } else { int v = (reg - 0x20) / 0x20; + + if (v >= xhci->numintrs) { + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: read from nonexistent interrupter %i\n", v); + goto out_trace; + } XHCIInterrupter *intr = &xhci->intr[v]; switch (reg & 0x1f) { case 0x00: /* IMAN */ @@ -3065,6 +3082,7 @@ } } +out_trace: trace_usb_xhci_runtime_read(reg, ret); return ret; } @@ -3082,7 +3100,13 @@ trace_usb_xhci_unimplemented("runtime write", reg); return; } + v = (reg - 0x20) / 0x20; + if (v >= xhci->numintrs) { + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: write to nonexistent interrupter %i\n", v); + return; + } intr = &xhci->intr[v]; switch (reg & 0x1f) { diff -Nru qemu-10.0.11+ds/hw/usb/redirect.c qemu-10.0.13+ds/hw/usb/redirect.c --- qemu-10.0.11+ds/hw/usb/redirect.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/usb/redirect.c 2026-08-26 19:31:25.000000000 +0000 @@ -690,6 +690,7 @@ struct buf_packet *bulkp; int count; + assert(maxp != 0); while ((bulkp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq)) && p->actual_length < p->iov.size && p->status == USB_RET_SUCCESS) { if (bulkp->len < 2) { @@ -739,6 +740,7 @@ .stream_id = 0, .no_transfers = 5, }; + assert(dev->endpoint[EP2I(ep)].max_packet_size != 0); /* Round bytes_per_transfer up to a multiple of max_packet_size */ bpt = 512 + dev->endpoint[EP2I(ep)].max_packet_size - 1; bpt /= dev->endpoint[EP2I(ep)].max_packet_size; @@ -793,6 +795,7 @@ } if (dev->endpoint[EP2I(ep)].bulk_receiving_enabled) { + assert(maxp != 0); if (size != 0 && (size % maxp) == 0) { usbredir_handle_buffered_bulk_in_data(dev, p, ep); return; @@ -1801,6 +1804,17 @@ if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_ep_info_max_packet_size)) { dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i]; + if (ep_info->max_packet_size[i] == 0 && + dev->endpoint[i].bulk_receiving_enabled) { + USBPacket *p = dev->endpoint[i].pending_async_packet; + usbredir_stop_bulk_receiving(dev, I2EP(i)); + dev->endpoint[i].bulk_receiving_enabled = 0; + if (p != NULL) { + dev->endpoint[i].pending_async_packet = NULL; + p->status = USB_RET_IOERROR; + usb_packet_complete(&dev->dev, p); + } + } } #if USBREDIR_VERSION >= 0x000700 if (usbredirparser_peer_has_cap(dev->parser, @@ -2143,7 +2157,7 @@ USBRedirDevice *dev = priv; uint8_t status, ep = buffered_bulk_packet->endpoint; void *free_on_destroy; - int i, len; + int i, len, queued = 0; DPRINTF("buffered-bulk-in status %d ep %02X len %d id %"PRIu64"\n", buffered_bulk_packet->status, ep, data_len, id); @@ -2161,6 +2175,7 @@ } /* Data must be in maxp chunks for buffered_bulk_add_*_data_to_packet */ + assert(dev->endpoint[EP2I(ep)].max_packet_size != 0); len = dev->endpoint[EP2I(ep)].max_packet_size; status = usb_redir_success; free_on_destroy = NULL; @@ -2174,8 +2189,24 @@ /* bufp_alloc also adds the packet to the ep queue */ r = bufp_alloc(dev, data + i, len, status, ep, free_on_destroy); if (r) { + /* + * Earlier fragments from this packet are in the queue + * with interior pointers into data. If the dropped + * fragment was the final one, bufp_alloc already freed + * data so those pointers are dangling. Remove them. + */ + while (queued > 0) { + struct buf_packet *bufp; + bufp = QTAILQ_LAST(&dev->endpoint[EP2I(ep)].bufpq); + bufp_free(dev, bufp, ep); + queued--; + } + if (!free_on_destroy) { + free(data); + } break; } + queued++; } if (dev->endpoint[EP2I(ep)].pending_async_packet) { @@ -2228,6 +2259,15 @@ usbredir_setup_usb_eps(dev); usbredir_check_bulk_receiving(dev); + for (int i = 0; i < MAX_ENDPOINTS; i++) { + if (dev->endpoint[i].bulk_receiving_started && + dev->endpoint[i].max_packet_size == 0) { + error_report("usbredir: endpoint %d has bulk receiving started " + "with zero max_packet_size", i); + return -EINVAL; + } + } + return 0; } diff -Nru qemu-10.0.11+ds/hw/vfio/igd.c qemu-10.0.13+ds/hw/vfio/igd.c --- qemu-10.0.11+ds/hw/vfio/igd.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/vfio/igd.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/vfio/pci-quirks.c qemu-10.0.13+ds/hw/vfio/pci-quirks.c --- qemu-10.0.11+ds/hw/vfio/pci-quirks.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/vfio/pci-quirks.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/vfio/pci.c qemu-10.0.13+ds/hw/vfio/pci.c --- qemu-10.0.11+ds/hw/vfio/pci.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/vfio/pci.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/vfio/pci.h qemu-10.0.13+ds/hw/vfio/pci.h --- qemu-10.0.11+ds/hw/vfio/pci.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/vfio/pci.h 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/vfio/trace-events qemu-10.0.13+ds/hw/vfio/trace-events --- qemu-10.0.11+ds/hw/vfio/trace-events 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/vfio/trace-events 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/virtio/vdpa-dev.c qemu-10.0.13+ds/hw/virtio/vdpa-dev.c --- qemu-10.0.11+ds/hw/virtio/vdpa-dev.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/virtio/vdpa-dev.c 2026-08-26 19:31:25.000000000 +0000 @@ -173,6 +173,7 @@ { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); + struct vhost_virtqueue *vqs = s->dev.vqs; int i; virtio_set_status(vdev, 0); @@ -184,8 +185,8 @@ virtio_cleanup(vdev); g_free(s->config); - g_free(s->dev.vqs); vhost_dev_cleanup(&s->dev); + g_free(vqs); g_free(s->vdpa.shared); qemu_close(s->vhostfd); s->vhostfd = -1; @@ -212,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.11+ds/hw/virtio/vhost-user.c qemu-10.0.13+ds/hw/virtio/vhost-user.c --- qemu-10.0.11+ds/hw/virtio/vhost-user.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/virtio/vhost-user.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/virtio/vhost.c qemu-10.0.13+ds/hw/virtio/vhost.c --- qemu-10.0.11+ds/hw/virtio/vhost.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/virtio/vhost.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/virtio/virtio-iommu.c qemu-10.0.13+ds/hw/virtio/virtio-iommu.c --- qemu-10.0.11+ds/hw/virtio/virtio-iommu.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/virtio/virtio-iommu.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/virtio/virtio-pmem.c qemu-10.0.13+ds/hw/virtio/virtio-pmem.c --- qemu-10.0.11+ds/hw/virtio/virtio-pmem.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/virtio/virtio-pmem.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/virtio/virtio-rng.c qemu-10.0.13+ds/hw/virtio/virtio-rng.c --- qemu-10.0.11+ds/hw/virtio/virtio-rng.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/virtio/virtio-rng.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/virtio/virtio.c qemu-10.0.13+ds/hw/virtio/virtio.c --- qemu-10.0.11+ds/hw/virtio/virtio.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/virtio/virtio.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/hw/watchdog/spapr_watchdog.c qemu-10.0.13+ds/hw/watchdog/spapr_watchdog.c --- qemu-10.0.11+ds/hw/watchdog/spapr_watchdog.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/hw/watchdog/spapr_watchdog.c 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/include/block/accounting.h qemu-10.0.13+ds/include/block/accounting.h --- qemu-10.0.11+ds/include/block/accounting.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/block/accounting.h 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/include/exec/cpu-all.h qemu-10.0.13+ds/include/exec/cpu-all.h --- qemu-10.0.11+ds/include/exec/cpu-all.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/exec/cpu-all.h 2026-08-26 19:31:25.000000000 +0000 @@ -105,12 +105,14 @@ static inline int cpu_mmu_index(CPUState *cs, bool ifetch); /* - * Allow some level of source compatibility with softmmu. We do not - * support any of the more exotic features, so only invalid pages may - * be signaled by probe_access_flags(). + * Allow some level of source compatibility with softmmu. + * Invalid is set when the page does not have requested permissions. + * MMIO is set when we want the target helper to use the functional + * interface for load/store so that plugins see the access. */ #define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1)) -#define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 2)) +#define TLB_FORCE_SLOW (1 << (TARGET_PAGE_BITS_MIN - 2)) +#define TLB_MMIO 0 #define TLB_WATCHPOINT 0 static inline int cpu_mmu_index(CPUState *cs, bool ifetch) diff -Nru qemu-10.0.11+ds/include/exec/memory.h qemu-10.0.13+ds/include/exec/memory.h --- qemu-10.0.11+ds/include/exec/memory.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/exec/memory.h 2026-08-26 19:31:25.000000000 +0000 @@ -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.11+ds/include/hw/elf_ops.h.inc qemu-10.0.13+ds/include/hw/elf_ops.h.inc --- qemu-10.0.11+ds/include/hw/elf_ops.h.inc 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/hw/elf_ops.h.inc 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/include/hw/i2c/bcm2835_i2c.h qemu-10.0.13+ds/include/hw/i2c/bcm2835_i2c.h --- qemu-10.0.11+ds/include/hw/i2c/bcm2835_i2c.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/hw/i2c/bcm2835_i2c.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/include/hw/misc/stm32_rcc.h qemu-10.0.13+ds/include/hw/misc/stm32_rcc.h --- qemu-10.0.11+ds/include/hw/misc/stm32_rcc.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/hw/misc/stm32_rcc.h 2026-08-26 19:31:26.000000000 +0000 @@ -65,7 +65,11 @@ #define STM32_RCC_NREGS ((STM32_RCC_DCKCFGR2 >> 2) + 1) #define STM32_RCC_PERIPHERAL_SIZE 0x400 -#define STM32_RCC_NIRQS (32 * 5) /* 32 bits per reg, 5 en/rst regs */ + +/* 32 bits per reg, 3 AHB regs and 2 APB regs */ +#define STM32_RCC_N_AHB_IRQS (32 * 3) +#define STM32_RCC_N_APB_IRQS (32 * 2) +#define STM32_RCC_NIRQS (STM32_RCC_N_AHB_IRQS + STM32_RCC_N_APB_IRQS) #define STM32_RCC_GPIO_IRQ_OFFSET 0 diff -Nru qemu-10.0.11+ds/include/hw/s390x/css.h qemu-10.0.13+ds/include/hw/s390x/css.h --- qemu-10.0.11+ds/include/hw/s390x/css.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/hw/s390x/css.h 2026-08-26 19:31:26.000000000 +0000 @@ -132,6 +132,7 @@ bool ccw_fmt_1; bool thinint_active; uint8_t ccw_no_data_cnt; + uint8_t ccw_tic_cnt; uint16_t migrated_schid; /* used for mismatch detection */ CcwDataStream cds; /* transport-provided data: */ diff -Nru qemu-10.0.11+ds/include/hw/scsi/scsi.h qemu-10.0.13+ds/include/hw/scsi/scsi.h --- qemu-10.0.11+ds/include/hw/scsi/scsi.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/hw/scsi/scsi.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/include/hw/virtio/virtio-gpu.h qemu-10.0.13+ds/include/hw/virtio/virtio-gpu.h --- qemu-10.0.11+ds/include/hw/virtio/virtio-gpu.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/hw/virtio/virtio-gpu.h 2026-08-26 19:31:26.000000000 +0000 @@ -15,6 +15,7 @@ #define HW_VIRTIO_GPU_H #include "qemu/queue.h" +#include "qemu/units.h" #include "ui/qemu-pixman.h" #include "ui/console.h" #include "hw/virtio/virtio.h" @@ -64,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; @@ -280,6 +280,14 @@ struct rutabaga *rutabaga; }; +/* + * With 4 KiB pages and QEMU's VIRTQUEUE_MAX_SIZE (1024) mapped-iov + * limit, the largest inline command is ~4 MiB. Cap submit_3d + * allocations to this value to prevent a malicious guest from + * triggering an OOM abort via an inflated cs.size field. + */ +#define VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE (4 * MiB) + #define VIRTIO_GPU_FILL_CMD(out) do { \ size_t virtiogpufillcmd_s_ = \ iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \ @@ -288,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) @@ -336,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.11+ds/include/hw/virtio/virtio-iommu.h qemu-10.0.13+ds/include/hw/virtio/virtio-iommu.h --- qemu-10.0.11+ds/include/hw/virtio/virtio-iommu.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/hw/virtio/virtio-iommu.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/include/hw/virtio/virtio-pmem.h qemu-10.0.13+ds/include/hw/virtio/virtio-pmem.h --- qemu-10.0.11+ds/include/hw/virtio/virtio-pmem.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/hw/virtio/virtio-pmem.h 2026-08-26 19:31:26.000000000 +0000 @@ -32,6 +32,7 @@ VirtQueue *rq_vq; uint64_t start; HostMemoryBackend *memdev; + unsigned int inflight; }; struct VirtIOPMEMClass { diff -Nru qemu-10.0.11+ds/include/net/net.h qemu-10.0.13+ds/include/net/net.h --- qemu-10.0.11+ds/include/net/net.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/net/net.h 2026-08-26 19:31:26.000000000 +0000 @@ -329,9 +329,32 @@ .offset = vmstate_offset_macaddr(_state, _field), \ } +/** + * net_peer_needs_padding: Should we pad as we send out packets? + * @nc: NetClientState + * + * Return true if the peer of this NetClientState (i.e. the + * destination that qemu_send_packet() etc send to) requires us to pad + * out packets that are shorter than the minimum ethernet frame + * length. + */ static inline bool net_peer_needs_padding(NetClientState *nc) { return nc->peer && !nc->peer->do_not_pad; } +/** + * net_client_needs_padding: Should we pad as we queue packets to ourselves? + * @nc: NetClientState + * + * Return true if this NetClientState requires us to pad out packets + * that are shorter than the minimum ethernet frame length. This is + * the check to make in qemu_receive_packet() when we are queuing a + * packet back into ourselves (i.e. loopback). + */ +static inline bool net_client_needs_padding(NetClientState *nc) +{ + return !nc->do_not_pad; +} + #endif diff -Nru qemu-10.0.11+ds/include/qemu/coroutine.h qemu-10.0.13+ds/include/qemu/coroutine.h --- qemu-10.0.11+ds/include/qemu/coroutine.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/qemu/coroutine.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/include/system/rng.h qemu-10.0.13+ds/include/system/rng.h --- qemu-10.0.11+ds/include/system/rng.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/system/rng.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/include/tcg/tcg-op-common.h qemu-10.0.13+ds/include/tcg/tcg-op-common.h --- qemu-10.0.11+ds/include/tcg/tcg-op-common.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/tcg/tcg-op-common.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/include/tcg/tcg-op.h qemu-10.0.13+ds/include/tcg/tcg-op.h --- qemu-10.0.11+ds/include/tcg/tcg-op.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/include/tcg/tcg-op.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/linux-user/elfload.c qemu-10.0.13+ds/linux-user/elfload.c --- qemu-10.0.11+ds/linux-user/elfload.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/linux-user/elfload.c 2026-08-26 19:31:26.000000000 +0000 @@ -1720,6 +1720,20 @@ #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_ALPHA +#define ELF_HWCAP get_elf_hwcap() + +static uint32_t get_elf_hwcap(void) +{ + CPUState *cs = thread_cpu; + /* + * The Linux kernel computes ELF_HWCAP as ~amask(-1), which clears a bit + * for each supported ISA extension. env->amask stores exactly those bits + * set for the extensions supported by the emulated CPU model, matching + * the kernel's convention: bit set in AT_HWCAP ↔ extension present. + */ + return cpu_env(cs)->amask; +} + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -1728,6 +1742,29 @@ regs->usp = infop->start_stack; } +/* + * Matches the kernel's elf_gregset_t (ELF_NGREG = 33): + * r0-r30 at indices 0-30, pc at 31, ps at 32. + * r31 (hardwired zero) is not stored; pc occupies index 31. + */ +typedef struct target_elf_gregset_t { + abi_ulong regs[31]; /* integer registers r0-r30 [0..30] */ + abi_ulong pc; /* program counter [31] */ + abi_ulong unique; /* thread's UNIQUE field [32] */ +} target_elf_gregset_t; + +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUAlphaState *env) +{ + int i; + + for (i = 0; i < 31; i++) { + r->regs[i] = tswap64(env->ir[i]); + } + r->pc = tswap64(env->pc); + r->unique = tswap64(env->unique); +} + +#define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 #endif /* TARGET_ALPHA */ diff -Nru qemu-10.0.11+ds/linux-user/sh4/cpu_loop.c qemu-10.0.13+ds/linux-user/sh4/cpu_loop.c --- qemu-10.0.11+ds/linux-user/sh4/cpu_loop.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/linux-user/sh4/cpu_loop.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/linux-user/sh4/signal.c qemu-10.0.13+ds/linux-user/sh4/signal.c --- qemu-10.0.11+ds/linux-user/sh4/signal.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/linux-user/sh4/signal.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/linux-user/sh4/target_mman.h qemu-10.0.13+ds/linux-user/sh4/target_mman.h --- qemu-10.0.11+ds/linux-user/sh4/target_mman.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/linux-user/sh4/target_mman.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/linux-user/strace.c qemu-10.0.13+ds/linux-user/strace.c --- qemu-10.0.11+ds/linux-user/strace.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/linux-user/strace.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/linux-user/strace.list qemu-10.0.13+ds/linux-user/strace.list --- qemu-10.0.11+ds/linux-user/strace.list 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/linux-user/strace.list 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/linux-user/syscall.c qemu-10.0.13+ds/linux-user/syscall.c --- qemu-10.0.11+ds/linux-user/syscall.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/linux-user/syscall.c 2026-08-26 19:31:26.000000000 +0000 @@ -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; } @@ -5092,6 +5086,9 @@ } #endif /* CONFIG_USBFS */ +#define DM_MAX_TARGETS 1048576 +#define DM_MAX_TARGET_PARAMS 1024 + static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, int cmd, abi_long arg) { @@ -5104,6 +5101,7 @@ abi_long ret; void *big_buf = NULL; char *host_data; + const size_t minimum_data_size = offsetof(struct dm_ioctl, data); arg_type++; target_size = thunk_type_size(arg_type, 0); @@ -5115,9 +5113,26 @@ thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST); unlock_user(argptr, arg, 0); - /* buf_temp is too small, so fetch things into a bigger buffer */ - big_buf = g_malloc0(((struct dm_ioctl*)buf_temp)->data_size * 2); - memcpy(big_buf, buf_temp, target_size); + /* At this point this includes the size of the fixed dm_ioctl parts */ + guest_data_size = ((struct dm_ioctl *)buf_temp)->data_size; + + if (guest_data_size < minimum_data_size || + guest_data_size > DM_MAX_TARGETS * DM_MAX_TARGET_PARAMS) { + ret = -TARGET_EINVAL; + goto out; + } + + /* + * buf_temp is too small, so fetch things into a bigger buffer. Here + * we copy all of the fixed parts of struct dm_ioctl but not the + * data at the end (which in the struct is "char data[7]" but in + * reality is command-specific and might be nothing or might be + * much larger, as defined by data_size). We know struct dm_ioctl's + * size is not target specific so we don't need to distinguish between + * its minimum size for the host vs the target. + */ + big_buf = g_malloc0(guest_data_size * 2); + memcpy(big_buf, buf_temp, minimum_data_size); buf_temp = big_buf; host_dm = big_buf; @@ -5126,7 +5141,8 @@ ret = -TARGET_EINVAL; goto out; } - guest_data_size = host_dm->data_size - host_dm->data_start; + /* Adjust down to only the size of the payload */ + guest_data_size -= host_dm->data_start; host_data = (char*)host_dm + host_dm->data_start; argptr = lock_user(VERIFY_READ, guest_data, guest_data_size, 1); @@ -9436,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 @@ -14162,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.11+ds/linux-user/syscall_defs.h qemu-10.0.13+ds/linux-user/syscall_defs.h --- qemu-10.0.11+ds/linux-user/syscall_defs.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/linux-user/syscall_defs.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/meson.build qemu-10.0.13+ds/meson.build --- qemu-10.0.11+ds/meson.build 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/meson.build 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/migration/multifd-qatzip.c qemu-10.0.13+ds/migration/multifd-qatzip.c --- qemu-10.0.11+ds/migration/multifd-qatzip.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/migration/multifd-qatzip.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/migration/multifd-qpl.c qemu-10.0.13+ds/migration/multifd-qpl.c --- qemu-10.0.11+ds/migration/multifd-qpl.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/migration/multifd-qpl.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/migration/multifd-uadk.c qemu-10.0.13+ds/migration/multifd-uadk.c --- qemu-10.0.11+ds/migration/multifd-uadk.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/migration/multifd-uadk.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/migration/multifd-zlib.c qemu-10.0.13+ds/migration/multifd-zlib.c --- qemu-10.0.11+ds/migration/multifd-zlib.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/migration/multifd-zlib.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/migration/multifd-zstd.c qemu-10.0.13+ds/migration/multifd-zstd.c --- qemu-10.0.11+ds/migration/multifd-zstd.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/migration/multifd-zstd.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/net/filter-rewriter.c qemu-10.0.13+ds/net/filter-rewriter.c --- qemu-10.0.11+ds/net/filter-rewriter.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/net/filter-rewriter.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/net/net.c qemu-10.0.13+ds/net/net.c --- qemu-10.0.11+ds/net/net.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/net/net.c 2026-08-26 19:31:26.000000000 +0000 @@ -764,7 +764,7 @@ return 0; } - if (net_peer_needs_padding(nc)) { + if (net_client_needs_padding(nc)) { if (eth_pad_short_frame(min_pkt, &min_pktsz, buf, size)) { buf = min_pkt; size = min_pktsz; diff -Nru qemu-10.0.11+ds/pc-bios/s390-ccw/bootmap.c qemu-10.0.13+ds/pc-bios/s390-ccw/bootmap.c --- qemu-10.0.11+ds/pc-bios/s390-ccw/bootmap.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/pc-bios/s390-ccw/bootmap.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/pc-bios/s390-ccw/helper.h qemu-10.0.13+ds/pc-bios/s390-ccw/helper.h --- qemu-10.0.11+ds/pc-bios/s390-ccw/helper.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/pc-bios/s390-ccw/helper.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/pc-bios/s390-ccw/menu.c qemu-10.0.13+ds/pc-bios/s390-ccw/menu.c --- qemu-10.0.11+ds/pc-bios/s390-ccw/menu.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/pc-bios/s390-ccw/menu.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/pc-bios/s390-ccw/s390-ccw.h qemu-10.0.13+ds/pc-bios/s390-ccw/s390-ccw.h --- qemu-10.0.11+ds/pc-bios/s390-ccw/s390-ccw.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/pc-bios/s390-ccw/s390-ccw.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/qemu-options.hx qemu-10.0.13+ds/qemu-options.hx --- qemu-10.0.11+ds/qemu-options.hx 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/qemu-options.hx 2026-08-26 19:31:26.000000000 +0000 @@ -1801,19 +1801,19 @@ DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, "-fsdev local,id=id,path=path,security_model=mapped-xattr|mapped-file|passthrough|none\n" - " [,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode]\n" + " [,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,max_xattr=max]\n" " [[,throttling.bps-total=b]|[[,throttling.bps-read=r][,throttling.bps-write=w]]]\n" " [[,throttling.iops-total=i]|[[,throttling.iops-read=r][,throttling.iops-write=w]]]\n" " [[,throttling.bps-total-max=bm]|[[,throttling.bps-read-max=rm][,throttling.bps-write-max=wm]]]\n" " [[,throttling.iops-total-max=im]|[[,throttling.iops-read-max=irm][,throttling.iops-write-max=iwm]]]\n" " [[,throttling.iops-size=is]]\n" - "-fsdev synth,id=id\n", + "-fsdev synth,id=id[,max_xattr=max]\n", QEMU_ARCH_ALL) SRST -``-fsdev local,id=id,path=path,security_model=security_model [,writeout=writeout][,readonly=on][,fmode=fmode][,dmode=dmode] [,throttling.option=value[,throttling.option=value[,...]]]`` +``-fsdev local,id=id,path=path,security_model=security_model [,writeout=writeout][,readonly=on][,fmode=fmode][,dmode=dmode][,max_xattr=max] [,throttling.option=value[,throttling.option=value[,...]]]`` \ -``-fsdev synth,id=id[,readonly=on]`` +``-fsdev synth,id=id[,readonly=on][,max_xattr=max]`` Define a new file system device. Valid options are: ``local`` @@ -1887,6 +1887,12 @@ Let every is bytes of a request count as a new request for iops throttling purposes. + ``max_xattr=max`` + Specifies the maximum number of concurrent xattr FIDs allowed for + this export. The default is 1024. Set to 0 for allowing an infinite + number of xattr FIDs. This limit prevents host memory exhaustion + attacks by capping the number of simultaneous xattr FIDs. + -fsdev option is used along with -device driver "virtio-9p-...". ``-device virtio-9p-type,fsdev=id,mount_tag=mount_tag`` @@ -1906,14 +1912,14 @@ DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs, "-virtfs local,path=path,mount_tag=tag,security_model=mapped-xattr|mapped-file|passthrough|none\n" - " [,id=id][,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,multidevs=remap|forbid|warn]\n" - "-virtfs synth,mount_tag=tag[,id=id][,readonly=on]\n", + " [,id=id][,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,multidevs=remap|forbid|warn][,max_xattr=max]\n" + "-virtfs synth,mount_tag=tag[,id=id][,readonly=on][,max_xattr=max]\n", QEMU_ARCH_ALL) SRST -``-virtfs local,path=path,mount_tag=mount_tag ,security_model=security_model[,writeout=writeout][,readonly=on] [,fmode=fmode][,dmode=dmode][,multidevs=multidevs]`` +``-virtfs local,path=path,mount_tag=mount_tag ,security_model=security_model[,writeout=writeout][,readonly=on] [,fmode=fmode][,dmode=dmode][,multidevs=multidevs][,max_xattr=max]`` \ -``-virtfs synth,mount_tag=mount_tag`` +``-virtfs synth,mount_tag=mount_tag[,max_xattr=max]`` Define a new virtual filesystem device and expose it to the guest using a virtio-9p-device (a.k.a. 9pfs), which essentially means that a certain directory on host is made directly accessible by guest as a pass-through @@ -1979,6 +1985,12 @@ Specifies the tag name to be used by the guest to mount this export point. + ``max_xattr=max`` + Specifies the maximum number of concurrent xattr FIDs allowed for + this export. The default is 1024. Set to 0 for allowing an infinite + number of xattr FIDs. This limit prevents host memory exhaustion + attacks by capping the number of simultaneous xattr FIDs. + ``multidevs=remap|forbid|warn`` Specifies how to deal with multiple devices being shared with the same 9p export in order to avoid file ID collisions on guest. diff -Nru qemu-10.0.11+ds/replay/replay-debugging.c qemu-10.0.13+ds/replay/replay-debugging.c --- qemu-10.0.11+ds/replay/replay-debugging.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/replay/replay-debugging.c 2026-08-26 19:31:26.000000000 +0000 @@ -139,9 +139,8 @@ int64_t *snapshot_icount) { BlockDriverState *bs; - QEMUSnapshotInfo *sn_tab; + g_autofree QEMUSnapshotInfo *sn_tab = NULL; QEMUSnapshotInfo *nearest = NULL; - char *ret = NULL; int rv; int nb_sns, i; @@ -149,15 +148,19 @@ bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, NULL); if (!bs) { - goto fail; + return NULL; } nb_sns = bdrv_snapshot_list(bs, &sn_tab); + if (nb_sns < 0) { + return NULL; + } for (i = 0; i < nb_sns; i++) { rv = bdrv_all_has_snapshot(sn_tab[i].name, false, NULL, NULL); - if (rv < 0) - goto fail; + if (rv < 0) { + return NULL; + } if (rv == 1) { if (sn_tab[i].icount != -1ULL && sn_tab[i].icount <= icount @@ -166,14 +169,12 @@ } } } - if (nearest) { - ret = g_strdup(nearest->name); - *snapshot_icount = nearest->icount; + if (!nearest) { + return NULL; } - g_free(sn_tab); -fail: - return ret; + *snapshot_icount = nearest->icount; + return g_strdup(nearest->name); } static void replay_seek(int64_t icount, QEMUTimerCB callback, Error **errp) diff -Nru qemu-10.0.11+ds/subprojects/libvduse/libvduse.c qemu-10.0.13+ds/subprojects/libvduse/libvduse.c --- qemu-10.0.11+ds/subprojects/libvduse/libvduse.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/subprojects/libvduse/libvduse.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/subprojects/libvhost-user/libvhost-user.c qemu-10.0.13+ds/subprojects/libvhost-user/libvhost-user.c --- qemu-10.0.11+ds/subprojects/libvhost-user/libvhost-user.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/subprojects/libvhost-user/libvhost-user.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/system/vl.c qemu-10.0.13+ds/system/vl.c --- qemu-10.0.11+ds/system/vl.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/system/vl.c 2026-08-26 19:31:26.000000000 +0000 @@ -3256,7 +3256,7 @@ QemuOpts *fsdev; QemuOpts *device; const char *writeout, *sock_fd, *socket, *path, *security_model, - *multidevs; + *multidevs, *max_xattr_str; olist = qemu_find_opts("virtfs"); if (!olist) { @@ -3320,6 +3320,11 @@ if (multidevs) { qemu_opt_set(fsdev, "multidevs", multidevs, &error_abort); } + max_xattr_str = qemu_opt_get(opts, "max_xattr"); + if (max_xattr_str) { + qemu_opt_set(fsdev, "max_xattr", max_xattr_str, + &error_abort); + } device = qemu_opts_create(qemu_find_opts("device"), NULL, 0, &error_abort); qemu_opt_set(device, "driver", "virtio-9p-pci", &error_abort); diff -Nru qemu-10.0.11+ds/target/arm/tcg/tlb-insns.c qemu-10.0.13+ds/target/arm/tcg/tlb-insns.c --- qemu-10.0.11+ds/target/arm/tcg/tlb-insns.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/arm/tcg/tlb-insns.c 2026-08-26 19:31:26.000000000 +0000 @@ -839,7 +839,7 @@ gran = tlbi_range_tg_to_gran_size(page_size_granule); /* The granule encoded in value must match the granule in use. */ - if (gran != param.gran) { + if (gran != param.gran || gran == GranInvalid) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid tlbi page size granule %d\n", page_size_granule); return ret; diff -Nru qemu-10.0.11+ds/target/i386/hvf/x86_decode.c qemu-10.0.13+ds/target/i386/hvf/x86_decode.c --- qemu-10.0.11+ds/target/i386/hvf/x86_decode.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/i386/hvf/x86_decode.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/i386/ops_sse.h qemu-10.0.13+ds/target/i386/ops_sse.h --- qemu-10.0.11+ds/target/i386/ops_sse.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/i386/ops_sse.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/i386/sev.c qemu-10.0.13+ds/target/i386/sev.c --- qemu-10.0.11+ds/target/i386/sev.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/i386/sev.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/i386/tcg/decode-new.c.inc qemu-10.0.13+ds/target/i386/tcg/decode-new.c.inc --- qemu-10.0.11+ds/target/i386/tcg/decode-new.c.inc 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/i386/tcg/decode-new.c.inc 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/i386/tcg/fpu_helper.c qemu-10.0.13+ds/target/i386/tcg/fpu_helper.c --- qemu-10.0.11+ds/target/i386/tcg/fpu_helper.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/i386/tcg/fpu_helper.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/i386/tcg/seg_helper.c qemu-10.0.13+ds/target/i386/tcg/seg_helper.c --- qemu-10.0.11+ds/target/i386/tcg/seg_helper.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/i386/tcg/seg_helper.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+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.11+ds/target/loongarch/tcg/insn_trans/trans_branch.c.inc 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/loongarch/tcg/insn_trans/trans_branch.c.inc 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/riscv/cpu.c qemu-10.0.13+ds/target/riscv/cpu.c --- qemu-10.0.11+ds/target/riscv/cpu.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/riscv/cpu.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/riscv/cpu_helper.c qemu-10.0.13+ds/target/riscv/cpu_helper.c --- qemu-10.0.11+ds/target/riscv/cpu_helper.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/riscv/cpu_helper.c 2026-08-26 19:31:26.000000000 +0000 @@ -1695,10 +1695,18 @@ /* Page table updates need to be atomic with MTTCG enabled */ if (updated_pte != pte && !is_debug) { + int pmp_prot, pmp_ret; + if (!adue) { return TRANSLATE_FAIL; } + pmp_ret = get_physical_address_pmp(env, &pmp_prot, pte_addr, + sxlen_bytes, MMU_DATA_STORE, PRV_S); + if (pmp_ret != TRANSLATE_SUCCESS) { + return TRANSLATE_PMP_FAIL; + } + /* * - if accessed or dirty bits need updating, and the PTE is * in RAM, then we do so atomically with a compare and swap. diff -Nru qemu-10.0.11+ds/target/riscv/csr.c qemu-10.0.13+ds/target/riscv/csr.c --- qemu-10.0.11+ds/target/riscv/csr.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/riscv/csr.c 2026-08-26 19:31:26.000000000 +0000 @@ -914,7 +914,16 @@ target_ulong *val) { uint64_t vill; - switch (env->xl) { + int xl = env->xl; + /* + * TCG plugins can read registers before env->xl is initialized. + * Fall back to the CPU's maximum XLEN in that early-init case. + */ + if (xl == 0) { + xl = riscv_cpu_mxl(env); + } + + switch (xl) { case MXL_RV32: vill = (uint32_t)env->vill << 31; break; @@ -1969,6 +1978,20 @@ return val; } +static uint64_t riscv_write_uxl(CPURISCVState *env, uint64_t val, + uint64_t field) +{ + RISCVMXL xl = riscv_cpu_mxl(env); + uint64_t uxl = get_field(val, field); + + if (uxl == MXL_RV128) { + uxl = xl == MXL_RV128 ? MXL_RV64 : xl; + val = set_field(val, field, uxl); + } + + return val; +} + static RISCVException write_mstatus(CPURISCVState *env, int csrno, target_ulong val) { @@ -2014,18 +2037,14 @@ } 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) { - uint64_t uxl = val & MSTATUS64_UXL >> 32; mask |= MSTATUS64_UXL; - - /* - * uxl = 3 is reserved so write the current xl instead. - * In case xl = MXL_RV128 (3) write MXL_RV64. - */ - if (uxl == 3) { - uxl = xl == MXL_RV128 ? MXL_RV64 : xl; - val = deposit64(val, 32, 2, uxl); - } + val = riscv_write_uxl(env, val, MSTATUS64_UXL); } } @@ -3177,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) | @@ -3184,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) { @@ -3312,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; @@ -3320,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) { @@ -3883,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; } @@ -5100,6 +5129,8 @@ uint64_t mask = (target_ulong)-1; if ((val & VSSTATUS64_UXL) == 0) { mask &= ~VSSTATUS64_UXL; + } else { + val = riscv_write_uxl(env, val, VSSTATUS64_UXL); } if ((env->henvcfg & HENVCFG_DTE)) { if ((val & SSTATUS_SDT) != 0) { diff -Nru qemu-10.0.11+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.11+ds/target/riscv/insn_trans/trans_rvf.c.inc 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/riscv/insn_trans/trans_rvf.c.inc 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+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.11+ds/target/riscv/insn_trans/trans_rvzfh.c.inc 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/riscv/insn_trans/trans_rvzfh.c.inc 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+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.11+ds/target/riscv/insn_trans/trans_rvzicfiss.c.inc 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/riscv/insn_trans/trans_rvzicfiss.c.inc 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/riscv/op_helper.c qemu-10.0.13+ds/target/riscv/op_helper.c --- qemu-10.0.11+ds/target/riscv/op_helper.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/riscv/op_helper.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/s390x/ioinst.c qemu-10.0.13+ds/target/s390x/ioinst.c --- qemu-10.0.11+ds/target/s390x/ioinst.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/s390x/ioinst.c 2026-08-26 19:31:26.000000000 +0000 @@ -601,13 +601,27 @@ #define CHSC_SEI_NT0 (1ULL << 63) #define CHSC_SEI_NT2 (1ULL << 61) +#define CHSC_SEI_0_FMT 0x0f000000 static void ioinst_handle_chsc_sei(ChscReq *req, ChscResp *res) { uint64_t selection_mask = ldq_be_p(&req->param1); + uint32_t param0 = be32_to_cpu(req->param0); uint8_t *res_flags = (uint8_t *)res->data; + uint16_t len = be16_to_cpu(req->len); + uint16_t resp_code; int have_event = 0; int have_more = 0; + if (len != 0x0010) { + resp_code = 0x0003; + goto out_err; + } + + if (param0 & CHSC_SEI_0_FMT) { + resp_code = 0x0007; + goto out_err; + } + /* regarding architecture nt0 can not be masked */ have_event = !chsc_sei_nt0_get_event(res); have_more = chsc_sei_nt0_have_event(); @@ -634,6 +648,12 @@ res->code = cpu_to_be16(0x0005); res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); } + return; + + out_err: + res->code = cpu_to_be16(resp_code); + res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); + res->param = 0; } static void ioinst_handle_chsc_unimplemented(ChscResp *res) diff -Nru qemu-10.0.11+ds/target/s390x/kvm/kvm.c qemu-10.0.13+ds/target/s390x/kvm/kvm.c --- qemu-10.0.11+ds/target/s390x/kvm/kvm.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/s390x/kvm/kvm.c 2026-08-26 19:31:26.000000000 +0000 @@ -1792,6 +1792,15 @@ } else if (s390_cpu_virt_mem_read(cpu, addr, ar, &sysib, sizeof(sysib))) { return; } + + /* + * The memory was filled by the kernel but mapped into the guest. + * If something is fishy, do not touch the buffer. + */ + if (sysib.count == 0 || sysib.count > ARRAY_SIZE(sysib.ext_names)) { + return; + } + /* Shift the stack of Extended Names to prepare for our own data */ memmove(&sysib.ext_names[1], &sysib.ext_names[0], sizeof(sysib.ext_names[0]) * (sysib.count - 1)); diff -Nru qemu-10.0.11+ds/target/s390x/tcg/crypto_helper.c qemu-10.0.13+ds/target/s390x/tcg/crypto_helper.c --- qemu-10.0.11+ds/target/s390x/tcg/crypto_helper.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/s390x/tcg/crypto_helper.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/s390x/tcg/insn-data.h.inc qemu-10.0.13+ds/target/s390x/tcg/insn-data.h.inc --- qemu-10.0.11+ds/target/s390x/tcg/insn-data.h.inc 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/s390x/tcg/insn-data.h.inc 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/s390x/tcg/int_helper.c qemu-10.0.13+ds/target/s390x/tcg/int_helper.c --- qemu-10.0.11+ds/target/s390x/tcg/int_helper.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/s390x/tcg/int_helper.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/s390x/tcg/translate.c qemu-10.0.13+ds/target/s390x/tcg/translate.c --- qemu-10.0.11+ds/target/s390x/tcg/translate.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/s390x/tcg/translate.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/sh4/cpu-param.h qemu-10.0.13+ds/target/sh4/cpu-param.h --- qemu-10.0.11+ds/target/sh4/cpu-param.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/sh4/cpu-param.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/target/sh4/op_helper.c qemu-10.0.13+ds/target/sh4/op_helper.c --- qemu-10.0.11+ds/target/sh4/op_helper.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/sh4/op_helper.c 2026-08-26 19:31:26.000000000 +0000 @@ -485,7 +485,7 @@ float32 p; bank_matrix = (env->sr & FPSCR_FR) ? 0 : 16; - bank_vector = (env->sr & FPSCR_FR) ? 16 : 0; + bank_vector = (env->sr & FPSCR_FR) ? 16 + n : n; set_float_exception_flags(0, &env->fp_status); for (i = 0 ; i < 4 ; i++) { r[i] = float32_zero; diff -Nru qemu-10.0.11+ds/target/sh4/translate.c qemu-10.0.13+ds/target/sh4/translate.c --- qemu-10.0.11+ds/target/sh4/translate.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/sh4/translate.c 2026-08-26 19:31:26.000000000 +0000 @@ -377,11 +377,6 @@ goto do_illegal; \ } -#define CHECK_FPSCR_PR_1 \ - if (!(ctx->tbflags & FPSCR_PR)) { \ - goto do_illegal; \ - } - #define CHECK_SH4A \ if (!(ctx->features & SH_FEATURE_SH4A)) { \ goto do_illegal; \ @@ -1746,22 +1741,22 @@ return; case 0xf0ed: /* fipr FVm,FVn */ CHECK_FPU_ENABLED - CHECK_FPSCR_PR_1 + CHECK_FPSCR_PR_0 { - TCGv m = tcg_constant_i32((ctx->opcode >> 8) & 3); - TCGv n = tcg_constant_i32((ctx->opcode >> 10) & 3); + TCGv m = tcg_constant_i32(((ctx->opcode >> 8) & 3) << 2); + TCGv n = tcg_constant_i32(((ctx->opcode >> 10) & 3) << 2); gen_helper_fipr(tcg_env, m, n); return; } break; case 0xf0fd: /* ftrv XMTRX,FVn */ CHECK_FPU_ENABLED - CHECK_FPSCR_PR_1 + CHECK_FPSCR_PR_0 { if ((ctx->opcode & 0x0300) != 0x0100) { goto do_illegal; } - TCGv n = tcg_constant_i32((ctx->opcode >> 10) & 3); + TCGv n = tcg_constant_i32(((ctx->opcode >> 10) & 3) << 2); gen_helper_ftrv(tcg_env, n); return; } diff -Nru qemu-10.0.11+ds/target/sparc/machine.c qemu-10.0.13+ds/target/sparc/machine.c --- qemu-10.0.11+ds/target/sparc/machine.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/target/sparc/machine.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tcg/loongarch64/tcg-target.c.inc qemu-10.0.13+ds/tcg/loongarch64/tcg-target.c.inc --- qemu-10.0.11+ds/tcg/loongarch64/tcg-target.c.inc 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tcg/loongarch64/tcg-target.c.inc 2026-08-26 19:31:26.000000000 +0000 @@ -211,12 +211,24 @@ if ((ct & TCG_CT_CONST_WSZ) && val == (type == TCG_TYPE_I32 ? 32 : 64)) { return true; } - int64_t vec_val = sextract64(val, 0, 8 << vece); - if ((ct & TCG_CT_CONST_VCMP) && -0x10 <= vec_val && vec_val <= 0x1f) { - return true; - } - if ((ct & TCG_CT_CONST_VADD) && -0x1f <= vec_val && vec_val <= 0x1f) { - return true; + if (ct & (TCG_CT_CONST_VCMP | TCG_CT_CONST_VADD)) { + int64_t vec_val = sextract64(val, 0, 8 << vece); + if (ct & TCG_CT_CONST_VCMP) { + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_LE: + case TCG_COND_LT: + return -0x10 <= vec_val && vec_val <= 0x0f; + case TCG_COND_LEU: + case TCG_COND_LTU: + return 0x00 <= vec_val && vec_val <= 0x1f; + default: + return false; + } + } + if ((ct & TCG_CT_CONST_VADD) && -0x1f <= vec_val && vec_val <= 0x1f) { + return true; + } } return false; } @@ -2027,40 +2039,51 @@ * Try vseqi/vslei/vslti */ int64_t value = sextract64(a2, 0, 8 << vece); - if ((cond == TCG_COND_EQ || - cond == TCG_COND_LE || - cond == TCG_COND_LT) && - (-0x10 <= value && value <= 0x0f)) { + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_LE: + case TCG_COND_LT: insn = cmp_vec_imm_insn[cond][lasx][vece]; tcg_out32(s, encode_vdvjsk5_insn(insn, a0, a1, value)); break; - } else if ((cond == TCG_COND_LEU || - cond == TCG_COND_LTU) && - (0x00 <= value && value <= 0x1f)) { + case TCG_COND_LEU: + case TCG_COND_LTU: insn = cmp_vec_imm_insn[cond][lasx][vece]; tcg_out32(s, encode_vdvjuk5_insn(insn, a0, a1, value)); break; + default: + g_assert_not_reached(); + } + } else { + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_LE: + case TCG_COND_LEU: + case TCG_COND_LT: + case TCG_COND_LTU: + insn = cmp_vec_insn[cond][lasx][vece]; + tcg_out32(s, encode_vdvjvk_insn(insn, a0, a1, a2)); + break; + case TCG_COND_GE: + case TCG_COND_GEU: + case TCG_COND_GT: + case TCG_COND_GTU: + insn = cmp_vec_insn[tcg_swap_cond(cond)][lasx][vece]; + tcg_out32(s, encode_vdvjvk_insn(insn, a0, a2, a1)); + break; + case TCG_COND_NE: + /* ne -> not(eq) */ + insn = cmp_vec_insn[TCG_COND_EQ][lasx][vece]; + tcg_out32(s, encode_vdvjvk_insn(insn, a0, a1, a2)); + insn = lasx ? OPC_XVNOR_V : OPC_VNOR_V; + tcg_out32(s, encode_vdvjvk_insn(insn, a0, a0, a0)); + break; + default: + g_assert_not_reached(); } - - /* - * Fallback to: - * dupi_vec temp, a2 - * cmp_vec a0, a1, temp, cond - */ - tcg_out_dupi_vec(s, type, vece, TCG_VEC_TMP0, a2); - a2 = TCG_VEC_TMP0; - } - - insn = cmp_vec_insn[cond][lasx][vece]; - if (insn == 0) { - TCGArg t; - t = a1, a1 = a2, a2 = t; - cond = tcg_swap_cond(cond); - insn = cmp_vec_insn[cond][lasx][vece]; - tcg_debug_assert(insn != 0); } } - goto vdvjvk; + break; case INDEX_op_add_vec: tcg_out_addsub_vec(s, lasx, vece, a0, a1, a2, const_args[2], true); break; diff -Nru qemu-10.0.11+ds/tcg/optimize.c qemu-10.0.13+ds/tcg/optimize.c --- qemu-10.0.11+ds/tcg/optimize.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tcg/optimize.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tcg/region.c qemu-10.0.13+ds/tcg/region.c --- qemu-10.0.11+ds/tcg/region.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tcg/region.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tcg/tcg-internal.h qemu-10.0.13+ds/tcg/tcg-internal.h --- qemu-10.0.11+ds/tcg/tcg-internal.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tcg/tcg-internal.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tcg/tcg-op-gvec.c qemu-10.0.13+ds/tcg/tcg-op-gvec.c --- qemu-10.0.11+ds/tcg/tcg-op-gvec.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tcg/tcg-op-gvec.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tcg/tcg-op.c qemu-10.0.13+ds/tcg/tcg-op.c --- qemu-10.0.11+ds/tcg/tcg-op.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tcg/tcg-op.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tcg/tcg.c qemu-10.0.13+ds/tcg/tcg.c --- qemu-10.0.11+ds/tcg/tcg.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tcg/tcg.c 2026-08-26 19:31:26.000000000 +0000 @@ -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) Binary files /srv/release.debian.org/tmp/TH2J9XVfTM/qemu-10.0.11+ds/tests/data/acpi/riscv64/virt/SPCR and /srv/release.debian.org/tmp/Ke1pPJsG5l/qemu-10.0.13+ds/tests/data/acpi/riscv64/virt/SPCR differ diff -Nru qemu-10.0.11+ds/tests/qemu-iotests/039 qemu-10.0.13+ds/tests/qemu-iotests/039 --- qemu-10.0.11+ds/tests/qemu-iotests/039 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qemu-iotests/039 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tests/qemu-iotests/039.out qemu-10.0.13+ds/tests/qemu-iotests/039.out --- qemu-10.0.11+ds/tests/qemu-iotests/039.out 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qemu-iotests/039.out 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tests/qemu-iotests/testrunner.py qemu-10.0.13+ds/tests/qemu-iotests/testrunner.py --- qemu-10.0.11+ds/tests/qemu-iotests/testrunner.py 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qemu-iotests/testrunner.py 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tests/qtest/ahci-test.c qemu-10.0.13+ds/tests/qtest/ahci-test.c --- qemu-10.0.11+ds/tests/qtest/ahci-test.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qtest/ahci-test.c 2026-08-26 19:31:26.000000000 +0000 @@ -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,174 @@ 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 + * PIO and DMA; the DMA variant is the reliable guard. + */ +static void test_atapi_engine_restart_in_flight(bool dma) +{ + AHCIQState *ahci; + AHCICommand *cmd; + unsigned char *tx; + char *iso; + int fd; + uint8_t port; + uint64_t buffer; + uint64_t iso_size = (uint64_t)ATAPI_SECTOR_SIZE * 2; + + fd = prepare_iso(iso_size, &tx, &iso); + + ahci = ahci_boot_and_enable("-drive if=none,id=drive0," + "file=blkdebug::%s,format=raw,readonly=on " + "-M q35 " + "-device ide-cd,drive=drive0 ", iso); + port = ahci_port_select(ahci); + + buffer = ahci_alloc(ahci, ATAPI_SECTOR_SIZE); + qtest_memset(ahci->parent->qts, buffer, 0x00, ATAPI_SECTOR_SIZE); + + /* Suspend the next backend read so the ATAPI read stays in flight. */ + g_free(qtest_hmp(ahci->parent->qts, + "qemu-io drive0 \"break read_aio rd\"")); + + cmd = ahci_atapi_command_create(CMD_ATAPI_READ_10, ATAPI_SECTOR_SIZE, + dma); + ahci_command_adjust(cmd, 0, buffer, ATAPI_SECTOR_SIZE, 0); + ahci_command_commit(ahci, cmd, port); + ahci_command_issue_async(ahci, cmd); + + /* Stop and restart the command engine to re-map the command list. */ + ahci_px_clr(ahci, port, AHCI_PX_CMD, AHCI_PX_CMD_ST); + ahci_px_set(ahci, port, AHCI_PX_CMD, AHCI_PX_CMD_ST); + + g_free(qtest_hmp(ahci->parent->qts, "qemu-io drive0 \"resume rd\"")); + + /* 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_engine_restart_pio(void) +{ + test_atapi_engine_restart_in_flight(false); +} + +static void test_atapi_engine_restart_dma(void) +{ + 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) @@ -2038,11 +2231,19 @@ 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); + qtest_add_func("/ahci/cdrom/engine_restart/pio", + 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.11+ds/tests/qtest/ide-test.c qemu-10.0.13+ds/tests/qtest/ide-test.c --- qemu-10.0.11+ds/tests/qtest/ide-test.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qtest/ide-test.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tests/qtest/libqos/ahci.c qemu-10.0.13+ds/tests/qtest/libqos/ahci.c --- qemu-10.0.11+ds/tests/qtest/libqos/ahci.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qtest/libqos/ahci.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tests/qtest/libqos/ahci.h qemu-10.0.13+ds/tests/qtest/libqos/ahci.h --- qemu-10.0.11+ds/tests/qtest/libqos/ahci.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qtest/libqos/ahci.h 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tests/qtest/libqos/virtio-9p-client.c qemu-10.0.13+ds/tests/qtest/libqos/virtio-9p-client.c --- qemu-10.0.11+ds/tests/qtest/libqos/virtio-9p-client.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qtest/libqos/virtio-9p-client.c 2026-08-26 19:31:26.000000000 +0000 @@ -240,6 +240,9 @@ id == P9_RUNLINKAT ? "RUNLINKAT" : id == P9_RFLUSH ? "RFLUSH" : id == P9_RREADDIR ? "RREADDIR" : + id == P9_RREAD ? "RREAD" : + id == P9_RCLUNK ? "RCLUNK" : + id == P9_RXATTRCREATE ? "RXATTRCREATE" : ""; } @@ -1053,3 +1056,124 @@ v9fs_req_recv(req, P9_RUNLINKAT); v9fs_req_free(req); } + +/* size[4] Tread tag[2] fid[4] offset[8] count[4] */ +TReadRes v9fs_tread(TReadOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + + uint32_t body_size = 4 + 8 + 4; + + req = v9fs_req_init(opt.client, body_size, P9_TREAD, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_uint64_write(req, opt.offset); + v9fs_uint32_write(req, opt.count); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rread(req, opt.rread.count, opt.rread.data); + } + req = NULL; /* request was freed */ + } + + return (TReadRes) { + .req = req, + .count = opt.rread.count ? *opt.rread.count : 0 + }; +} + +/* size[4] Rread tag[2] count[4] data[count] */ +void v9fs_rread(P9Req *req, uint32_t *count, void *data) +{ + v9fs_req_recv(req, P9_RREAD); + v9fs_uint32_read(req, count); + if (data && *count > 0) { + v9fs_memread(req, data, *count); + } + v9fs_req_free(req); +} + +/* size[4] Tclunk tag[2] fid[4] */ +TClunkRes v9fs_tclunk(TClunkOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + + req = v9fs_req_init(opt.client, 4, P9_TCLUNK, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rclunk(req); + } + req = NULL; /* request was freed */ + } + + return (TClunkRes) { .req = req }; +} + +/* size[4] Rclunk tag[2] */ +void v9fs_rclunk(P9Req *req) +{ + v9fs_req_recv(req, P9_RCLUNK); + v9fs_req_free(req); +} + +/* size[4] Txattrcreate tag[2] fid[4] name[s] attr_size[8] flags[4] */ +TXattrCreateRes v9fs_txattrcreate(TXattrCreateOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + g_assert(opt.name); + + uint32_t body_size = 4 + 8 + 4; + uint16_t string_size = v9fs_string_size(opt.name); + + g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); + body_size += string_size; + + req = v9fs_req_init(opt.client, body_size, P9_TXATTRCREATE, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_string_write(req, opt.name); + v9fs_uint64_write(req, opt.size); + v9fs_uint32_write(req, opt.flags); + v9fs_req_send(req); + + err = 0; + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rxattrcreate(req); + } + req = NULL; /* request was freed */ + } + + return (TXattrCreateRes) { .req = req, .err = err }; +} + +/* size[4] Rxattrcreate tag[2] */ +void v9fs_rxattrcreate(P9Req *req) +{ + v9fs_req_recv(req, P9_RXATTRCREATE); + v9fs_req_free(req); +} diff -Nru qemu-10.0.11+ds/tests/qtest/libqos/virtio-9p-client.h qemu-10.0.13+ds/tests/qtest/libqos/virtio-9p-client.h --- qemu-10.0.11+ds/tests/qtest/libqos/virtio-9p-client.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qtest/libqos/virtio-9p-client.h 2026-08-26 19:31:26.000000000 +0000 @@ -21,7 +21,8 @@ #include "qgraph.h" #include "tests/qtest/libqtest-single.h" -#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */ +/* Max size of a T-message or R-message */ +#define P9_MAX_SIZE (32 * 1024) typedef struct { QTestState *qts; @@ -441,6 +442,85 @@ P9Req *req; } TunlinkatRes; +/* options for 'Tread' 9p request */ +typedef struct TReadOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID of file to read from (required) */ + uint32_t fid; + /* start position of read from beginning of file (optional) */ + uint64_t offset; + /* how many bytes to read (required) */ + uint32_t count; + /* data being received from 9p server as 'Rread' response (optional) */ + struct { + uint32_t *count; + void *data; + } rread; + /* only send Tread request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TReadOpt; + +/* result of 'Tread' 9p request */ +typedef struct TReadRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; + /* amount of bytes read */ + uint32_t count; +} TReadRes; + +/* options for 'Tclunk' 9p request */ +typedef struct TClunkOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID to clunk (required) */ + uint32_t fid; + /* only send Tclunk request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TClunkOpt; + +/* result of 'Tclunk' 9p request */ +typedef struct TClunkRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TClunkRes; + +/* options for 'Txattrcreate' 9p request */ +typedef struct TXattrCreateOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID to convert to xattr fid (required) */ + uint32_t fid; + /* name of the xattr (required) */ + const char *name; + /* size of the xattr value (required) */ + uint64_t size; + /* flags: P9_XATTR_CREATE or P9_XATTR_REPLACE (optional) */ + uint32_t flags; + /* only send Txattrcreate request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TXattrCreateOpt; + +/* result of 'Txattrcreate' 9p request */ +typedef struct TXattrCreateRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; + /* error code if Rlerror received */ + uint32_t err; +} TXattrCreateRes; + void v9fs_set_allocator(QGuestAllocator *t_alloc); void v9fs_memwrite(P9Req *req, const void *addr, size_t len); void v9fs_memskip(P9Req *req, size_t len); @@ -490,5 +570,11 @@ void v9fs_rlink(P9Req *req); TunlinkatRes v9fs_tunlinkat(TunlinkatOpt); void v9fs_runlinkat(P9Req *req); +TReadRes v9fs_tread(TReadOpt opt); +void v9fs_rread(P9Req *req, uint32_t *count, void *data); +TClunkRes v9fs_tclunk(TClunkOpt opt); +void v9fs_rclunk(P9Req *req); +TXattrCreateRes v9fs_txattrcreate(TXattrCreateOpt opt); +void v9fs_rxattrcreate(P9Req *req); #endif diff -Nru qemu-10.0.11+ds/tests/qtest/libqos/virtio-9p.c qemu-10.0.13+ds/tests/qtest/libqos/virtio-9p.c --- qemu-10.0.11+ds/tests/qtest/libqos/virtio-9p.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qtest/libqos/virtio-9p.c 2026-08-26 19:31:26.000000000 +0000 @@ -228,6 +228,12 @@ g_string_assign(haystack, s); } +void virtio_9p_add_synth_driver_args(GString *cmd_line, const char *args) +{ + /* append passed args to '-fsdev ...' group */ + regex_replace(cmd_line, "(-fsdev \\w[^ ]*)", "\\1,%s", args); +} + void virtio_9p_assign_local_driver(GString *cmd_line, const char *args) { g_assert_nonnull(local_test_path); diff -Nru qemu-10.0.11+ds/tests/qtest/libqos/virtio-9p.h qemu-10.0.13+ds/tests/qtest/libqos/virtio-9p.h --- qemu-10.0.11+ds/tests/qtest/libqos/virtio-9p.h 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qtest/libqos/virtio-9p.h 2026-08-26 19:31:26.000000000 +0000 @@ -45,6 +45,12 @@ }; /** + * Add required test specific args to the QEMU command line for the 9pfs + * 'synth' fs driver. + */ +void virtio_9p_add_synth_driver_args(GString *cmd_line, const char *args); + +/** * Creates the directory for the 9pfs 'local' filesystem driver to access. */ void virtio_9p_create_local_test_dir(void); diff -Nru qemu-10.0.11+ds/tests/qtest/ufs-test.c qemu-10.0.13+ds/tests/qtest/ufs-test.c --- qemu-10.0.11+ds/tests/qtest/ufs-test.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qtest/ufs-test.c 2026-08-26 19:31:26.000000000 +0000 @@ -33,6 +33,8 @@ #define TEST_QID 0 #define QUEUE_SIZE 32 #define UFS_MCQ_MAX_QNUM 32 +#define ACPI_PCIHP_ADDR 0xae00 +#define PCI_EJ_BASE 0x0008 typedef struct QUfs QUfs; @@ -634,6 +636,17 @@ qpci_iounmap(&ufs->dev, ufs->bar); } +static void ufstest_acpi_eject(void *obj, void *data, QGuestAllocator *alloc) +{ + QUfs *ufs = obj; + QTestState *qts = ufs->dev.bus->qts; + + qtest_outl(qts, ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << 4); + qtest_qmp_assert_success(qts, "{ 'execute': 'query-status' }"); + g_usleep(3 * G_USEC_PER_SEC); + qtest_qmp_assert_success(qts, "{ 'execute': 'query-status' }"); +} + static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc) { QUfs *ufs = obj; @@ -1233,6 +1246,8 @@ .edge.extra_device_opts = "mcq=true,mcq-maxq=1" }; + QOSGraphTestOptions acpi_eject_test_opts = { .subprocess = true }; + add_qpci_address(&edge_opts, &(QPCIAddress){ .devfn = QPCI_DEVFN(4, 0) }); qos_node_create_driver("ufs", ufs_create); @@ -1250,6 +1265,10 @@ g_test_message("Skipping ufs io tests for ppc64"); return; } + if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) { + qos_add_test("acpi-eject", "ufs", ufstest_acpi_eject, + &acpi_eject_test_opts); + } qos_add_test("init", "ufs", ufstest_init, NULL); qos_add_test("legacy-read-write", "ufs", ufstest_read_write, &io_test_opts); qos_add_test("mcq-read-write", "ufs", ufstest_read_write, &mcq_test_opts); diff -Nru qemu-10.0.11+ds/tests/qtest/usb-hcd-xhci-test.c qemu-10.0.13+ds/tests/qtest/usb-hcd-xhci-test.c --- qemu-10.0.11+ds/tests/qtest/usb-hcd-xhci-test.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qtest/usb-hcd-xhci-test.c 2026-08-26 19:31:26.000000000 +0000 @@ -10,6 +10,72 @@ #include "qemu/osdep.h" #include "libqtest-single.h" #include "libqos/usb.h" +#include "qobject/qdict.h" + +static void wait_device_deleted_event(QTestState *qtest, const char *id) +{ + QDict *resp, *data; + const char *device; + + /* + * Other devices might get removed along with the removed device. Skip + * these. The device of interest will be the last one. + */ + for (;;) { + resp = qtest_qmp_eventwait_ref(qtest, "DEVICE_DELETED"); + data = qdict_get_qdict(resp, "data"); + device = data ? qdict_get_try_str(data, "device") : NULL; + if (device && !strcmp(device, id)) { + qobject_unref(resp); + break; + } + qobject_unref(resp); + } +} + +/* + * Regression test for the xHCI-PCI "host" strong-link reference cycle. + * + * The xHCI PCI wrapper embeds an xhci-core child whose strong "host" link + * points back at the PCI device, forming a refcount cycle. If + * usb_xhci_pci_exit() does not break that cycle, the device's refcount never + * reaches 0 on unplug, device_finalize() never runs, and therefore the + * DEVICE_DELETED event (emitted from device_finalize()) is never sent. + * + * This test hot-plugs an xHCI controller into an ACPI-hotpluggable bus, + * requests its removal and waits for DEVICE_DELETED. Without the fix the event + * is never delivered (device_finalize() is blocked), so the test would + * hang/time out. + */ +static void test_xhci_unplug_finalize(void) +{ + QTestState *qtest; + const char *arch = qtest_get_arch(); + + if (strcmp(arch, "i386") != 0 && strcmp(arch, "x86_64") != 0) { + g_test_skip("Test only runs on x86 (ACPI PCI hotplug)"); + return; + } + if (!qtest_has_device("nec-usb-xhci")) { + g_test_skip("Device nec-usb-xhci not available"); + return; + } + + qtest = qtest_initf("-machine pc"); + + qtest_qmp_device_add(qtest, "nec-usb-xhci", "xhci-finalize", "{}"); + + /* + * Request device removal. As the guest is not running, the unplug request + * won't be processed until the next system reset, which performs the + * removal and triggers device_finalize() (and thus DEVICE_DELETED). + */ + qtest_qmp_device_del_send(qtest, "xhci-finalize"); + qtest_system_reset_nowait(qtest); + wait_device_deleted_event(qtest, "xhci-finalize"); + + qtest_quit(qtest); +} static void test_xhci_hotplug(void) { @@ -50,6 +116,7 @@ g_test_init(&argc, &argv, NULL); qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug); + qtest_add_func("/xhci/pci/unplug/finalize", test_xhci_unplug_finalize); if (qtest_has_device("usb-uas")) { qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); } diff -Nru qemu-10.0.11+ds/tests/qtest/virtio-9p-test.c qemu-10.0.13+ds/tests/qtest/virtio-9p-test.c --- qemu-10.0.11+ds/tests/qtest/virtio-9p-test.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/qtest/virtio-9p-test.c 2026-08-26 19:31:26.000000000 +0000 @@ -30,6 +30,18 @@ #define tsymlink(...) v9fs_tsymlink((TsymlinkOpt) __VA_ARGS__) #define tlink(...) v9fs_tlink((TlinkOpt) __VA_ARGS__) #define tunlinkat(...) v9fs_tunlinkat((TunlinkatOpt) __VA_ARGS__) +#define tread(...) v9fs_tread((TReadOpt) __VA_ARGS__) +#define tclunk(...) v9fs_tclunk((TClunkOpt) __VA_ARGS__) +#define txattrcreate(...) v9fs_txattrcreate((TXattrCreateOpt) __VA_ARGS__) + +/* + * xattr size to be used for xattr tests + * + * 64k is the max. xattr size supported by the Linux kernel, However btrfs + * for instance supports only 16219 bytes. So let's be conservative and + * just use 8k for the xattr tests. + */ +#define TEST_XATTR_SIZE (8 * 1024) static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc) { @@ -103,6 +115,42 @@ return false; } +/* + * Returns the current internal xattr FID count (works with synth driver only). + */ +static size_t get_xattr_count(QVirtio9P *v9p) +{ + uint16_t nwqid; + v9fs_qid *wqid; + const char *xattr_count_path[] = { "stat", "xattr_count" }; + size_t xattr_count; + uint32_t bytes_read; + + /* walk to /stat/xattr_count file */ + uint32_t fid = twalk({ + .client = v9p, .fid = 0, + .nwname = 2, .wnames = (char **)xattr_count_path, + .rwalk = { .nwqid = &nwqid, .wqid = &wqid } + }).newfid; + + /* open for read */ + tlopen({ + .client = v9p, .fid = fid, .flags = O_RDONLY, + .rlopen = { .qid = NULL, .iounit = NULL } + }); + + /* read the internal xattr FID count */ + tread({ + .client = v9p, .fid = fid, .offset = 0, .count = sizeof(xattr_count), + .rread = { .count = &bytes_read, .data = &xattr_count } + }); + + /* cleanup */ + tclunk({ .client = v9p, .fid = fid }); + + return xattr_count; +} + /* basic readdir test where reply fits into a single response message */ static void fs_readdir(void *obj, void *data, QGuestAllocator *t_alloc) { @@ -244,6 +292,121 @@ g_free(wnames[0]); } +/* + * Test 9p server's xattr FID count limit enforcement. + * + * Shared test code for both 'synth' and 'local' driver to verify correct + * behaviour of 9p server enforcing preconfigured xattr FID count limit + * correctly. + * + * @v9p: 9pfs client + * + * @max_xattr: max. allowed xattr FIDs, or -1 for infinite + * + * @check_counter: whether to verify 9p server internal xattr FID counter + * (only works with 'synth' fs driver) + */ +static void do_xattr_limit(QVirtio9P *v9p, int max_xattr, bool check_counter) +{ + size_t count; + int i; + int limit = (max_xattr != -1) ? max_xattr : V9FS_MAX_XATTR_DEFAULT + 100; + g_autofree uint32_t *fids = g_new0(uint32_t, limit); + uint32_t err_fid = 0; + const char *file_path[] = { QTEST_V9FS_SYNTH_WRITE_FILE }; + g_autofree uint8_t *xattr_data = g_malloc(TEST_XATTR_SIZE); + + if (!g_test_slow()) { + g_test_skip("This is a slow test, run with -m slow"); + return; + } + + /* prepare xattr data with 'X' characters */ + memset(xattr_data, 'X', TEST_XATTR_SIZE); + + tattach({ .client = v9p }); + + /* create max. amount of permitted xattrs */ + for (i = 0; i < limit; i++) { + /* walk to create a new fid */ + fids[i] = twalk({ + .client = v9p, .fid = 0, + .nwname = 1, .wnames = (char **) file_path + }).newfid; + + /* create new xattr fid */ + txattrcreate({ + .client = v9p, .fid = fids[i], .name = "user.test", + .size = TEST_XATTR_SIZE, .flags = 0 + }); + + /* transfer the xattr data */ + twrite({ + .client = v9p, .fid = fids[i], .offset = 0, + .count = TEST_XATTR_SIZE, .data = xattr_data + }); + + /* verify server internal xattr counter */ + if (check_counter) { + count = get_xattr_count(v9p); + g_assert_cmpuint(count, ==, (i + 1)); + } + + /* avoid virtio descriptor exhaustion */ + qvirtqueue_reset_pool(v9p->vq); + } + + /* if xattrs are limited, the next xattr should fail */ + if (max_xattr != -1) { + /* walk to create another fid */ + err_fid = twalk({ + .client = v9p, .fid = 0, + .nwname = 1, .wnames = (char **) file_path + }).newfid; + + /* try to create one more xattr fid - should fail */ + txattrcreate({ + .client = v9p, .fid = err_fid, .name = "user.test_exceed", + .size = TEST_XATTR_SIZE, .flags = 0, + .expectErr = ENOSPC + }); + + /* verify internal xattr counter hasn't changed */ + if (check_counter) { + count = get_xattr_count(v9p); + g_assert_cmpuint(count, ==, limit); + } + } + + /* clunk all fids (should decrement xattr counter) */ + for (i = 0; i < limit; i++) { + tclunk({ .client = v9p, .fid = fids[i] }); + qvirtqueue_reset_pool(v9p->vq); + } + if (err_fid) { + tclunk({ .client = v9p, .fid = err_fid }); + } + + /* verify internal xattr counter is zero */ + if (check_counter) { + count = get_xattr_count(v9p); + g_assert_cmpuint(count, ==, 0); + } +} + +static void do_local_xattr_limit(QVirtio9P *v9p, int max_xattr) +{ + g_autofree char *test_file = virtio_9p_test_path("WRITE"); + + /* + * this file must be created for the test to work with the 'local' fs driver + */ + g_file_set_contents(test_file, "", 0, NULL); + + /* the actual test code shared with the 'synth' fs driver tests */ + do_xattr_limit(v9p, max_xattr, false); +} + static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; @@ -504,6 +667,27 @@ do_readdir_split(obj, 512); } +static void fs_synth_xattr_limit_default(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + v9fs_set_allocator(t_alloc); + do_xattr_limit(obj, V9FS_MAX_XATTR_DEFAULT, true); +} + +static void fs_synth_xattr_limit_custom(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + v9fs_set_allocator(t_alloc); + do_xattr_limit(obj, 100, true); +} + +static void fs_synth_xattr_limit_unlimited(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + v9fs_set_allocator(t_alloc); + do_xattr_limit(obj, -1, true); +} + /* tests using the 9pfs 'local' fs driver */ @@ -804,26 +988,81 @@ g_string_free(path, TRUE); } +static void fs_local_xattr_limit_default(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + v9fs_set_allocator(t_alloc); + do_local_xattr_limit(obj, V9FS_MAX_XATTR_DEFAULT); +} + +static void fs_local_xattr_limit_custom(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + v9fs_set_allocator(t_alloc); + do_local_xattr_limit(obj, 100); +} + +static void fs_local_xattr_limit_unlimited(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + v9fs_set_allocator(t_alloc); + do_local_xattr_limit(obj, -1); +} + +static void *synth_max_xattr_custom_opt(GString *cmd_line, void *arg) +{ + virtio_9p_add_synth_driver_args(cmd_line, "max_xattr=100"); + return arg; +} + +static void *synth_max_xattr_unlimited_opt(GString *cmd_line, void *arg) +{ + virtio_9p_add_synth_driver_args(cmd_line, "max_xattr=0"); + return arg; +} + static void cleanup_9p_local_driver(void *data) { /* remove previously created test dir when test is completed */ virtio_9p_remove_local_test_dir(); } -static void *assign_9p_local_driver(GString *cmd_line, void *arg) +static void assign_9p_local_driver_with_args(GString *cmd_line, + const char *extra_opts) { /* make sure test dir for the 'local' tests exists */ virtio_9p_create_local_test_dir(); - virtio_9p_assign_local_driver(cmd_line, "security_model=mapped-xattr"); + g_autofree char *opts = + (extra_opts) ? + g_strdup_printf("security_model=mapped-xattr,%s", extra_opts) : + g_strdup("security_model=mapped-xattr"); + + virtio_9p_assign_local_driver(cmd_line, opts); g_test_queue_destroy(cleanup_9p_local_driver, NULL); +} + +static void *assign_9p_local_driver(GString *cmd_line, void *arg) +{ + assign_9p_local_driver_with_args(cmd_line, NULL); return arg; } -static void register_virtio_9p_test(void) +static void *local_max_xattr_custom_opt(GString *cmd_line, void *arg) { + assign_9p_local_driver_with_args(cmd_line, "max_xattr=100"); + return arg; +} +static void *local_max_xattr_unlimited_opt(GString *cmd_line, void *arg) +{ + assign_9p_local_driver_with_args(cmd_line, "max_xattr=0"); + return arg; +} + +static void register_virtio_9p_test(void) +{ QOSGraphTestOptions opts = { }; @@ -854,7 +1093,14 @@ fs_readdir_split_256, &opts); qos_add_test("synth/readdir/split_128", "virtio-9p", fs_readdir_split_128, &opts); - + qos_add_test("synth/xattr_limit/default", "virtio-9p", + fs_synth_xattr_limit_default, &opts); + opts.before = synth_max_xattr_custom_opt; + qos_add_test("synth/xattr_limit/custom", "virtio-9p", + fs_synth_xattr_limit_custom, &opts); + opts.before = synth_max_xattr_unlimited_opt; + qos_add_test("synth/xattr_limit/unlimited", "virtio-9p", + fs_synth_xattr_limit_unlimited, &opts); /* 9pfs test cases using the 'local' filesystem driver */ opts.before = assign_9p_local_driver; @@ -873,6 +1119,14 @@ &opts); qos_add_test("local/deep_absolute_path", "virtio-9p", fs_deep_absolute_path, &opts); + qos_add_test("local/xattr_limit/default", "virtio-9p", + fs_local_xattr_limit_default, &opts); + opts.before = local_max_xattr_custom_opt; + qos_add_test("local/xattr_limit/custom", "virtio-9p", + fs_local_xattr_limit_custom, &opts); + opts.before = local_max_xattr_unlimited_opt; + qos_add_test("local/xattr_limit/unlimited", "virtio-9p", + fs_local_xattr_limit_unlimited, &opts); } libqos_init(register_virtio_9p_test); diff -Nru qemu-10.0.11+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.11+ds/tests/tcg/i386/test-i386-opt-shr.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-10.0.13+ds/tests/tcg/i386/test-i386-opt-shr.c 2026-08-26 19:31:26.000000000 +0000 @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Regression test for tcg optimize vs sign bit repetition counting. */ + +#include + +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.11+ds/tests/tcg/s390x/Makefile.target qemu-10.0.13+ds/tests/tcg/s390x/Makefile.target --- qemu-10.0.11+ds/tests/tcg/s390x/Makefile.target 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/tcg/s390x/Makefile.target 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tests/tcg/s390x/div.c qemu-10.0.13+ds/tests/tcg/s390x/div.c --- qemu-10.0.11+ds/tests/tcg/s390x/div.c 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/tcg/s390x/div.c 2026-08-26 19:31:26.000000000 +0000 @@ -1,6 +1,15 @@ #include +#include #include +/* 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.11+ds/tests/tcg/s390x/prno-trng.c qemu-10.0.13+ds/tests/tcg/s390x/prno-trng.c --- qemu-10.0.11+ds/tests/tcg/s390x/prno-trng.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-10.0.13+ds/tests/tcg/s390x/prno-trng.c 2026-08-26 19:31:26.000000000 +0000 @@ -0,0 +1,67 @@ +/* + * Test that PERFORM RANDOM NUMBER OPERATION TRNG is interruptible. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include +#include +#include + +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.11+ds/tests/tcg/s390x/stckf.c qemu-10.0.13+ds/tests/tcg/s390x/stckf.c --- qemu-10.0.11+ds/tests/tcg/s390x/stckf.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-10.0.13+ds/tests/tcg/s390x/stckf.c 2026-08-26 19:31:26.000000000 +0000 @@ -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 +#include +#include +#include + +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.11+ds/tests/tcg/x86_64/Makefile.target qemu-10.0.13+ds/tests/tcg/x86_64/Makefile.target --- qemu-10.0.11+ds/tests/tcg/x86_64/Makefile.target 2026-06-25 21:39:14.000000000 +0000 +++ qemu-10.0.13+ds/tests/tcg/x86_64/Makefile.target 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tests/tcg/x86_64/segment-prefixes.c qemu-10.0.13+ds/tests/tcg/x86_64/segment-prefixes.c --- qemu-10.0.11+ds/tests/tcg/x86_64/segment-prefixes.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-10.0.13+ds/tests/tcg/x86_64/segment-prefixes.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tests/unit/meson.build qemu-10.0.13+ds/tests/unit/meson.build --- qemu-10.0.11+ds/tests/unit/meson.build 2026-06-25 21:39:15.000000000 +0000 +++ qemu-10.0.13+ds/tests/unit/meson.build 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tests/unit/test-block-accounting.c qemu-10.0.13+ds/tests/unit/test-block-accounting.c --- qemu-10.0.11+ds/tests/unit/test-block-accounting.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-10.0.13+ds/tests/unit/test-block-accounting.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/tests/unit/test-coroutine.c qemu-10.0.13+ds/tests/unit/test-coroutine.c --- qemu-10.0.11+ds/tests/unit/test-coroutine.c 2026-06-25 21:39:15.000000000 +0000 +++ qemu-10.0.13+ds/tests/unit/test-coroutine.c 2026-08-26 19:31:26.000000000 +0000 @@ -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.11+ds/ui/gtk-egl.c qemu-10.0.13+ds/ui/gtk-egl.c --- qemu-10.0.11+ds/ui/gtk-egl.c 2026-06-25 21:39:15.000000000 +0000 +++ qemu-10.0.13+ds/ui/gtk-egl.c 2026-08-26 19:31:26.000000000 +0000 @@ -91,6 +91,7 @@ } else { qemu_dmabuf_set_draw_submitted(dmabuf, false); } + graphic_hw_gl_block(vc->gfx.dcl.con, true); } #endif gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); @@ -383,14 +384,11 @@ if (vc->gfx.guest_fb.dmabuf && !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { - graphic_hw_gl_block(vc->gfx.dcl.con, true); qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); gtk_egl_set_scanout_mode(vc, true); - gtk_widget_queue_draw_area(area, x, y, w, h); - return; } - gd_egl_scanout_flush(&vc->gfx.dcl, x, y, w, h); + gtk_widget_queue_draw_area(area, x, y, w, h); } void gtk_egl_init(DisplayGLMode mode) diff -Nru qemu-10.0.11+ds/ui/gtk-gl-area.c qemu-10.0.13+ds/ui/gtk-gl-area.c --- qemu-10.0.11+ds/ui/gtk-gl-area.c 2026-06-25 21:39:15.000000000 +0000 +++ qemu-10.0.13+ds/ui/gtk-gl-area.c 2026-08-26 19:31:26.000000000 +0000 @@ -73,6 +73,7 @@ } else { qemu_dmabuf_set_draw_submitted(dmabuf, false); } + graphic_hw_gl_block(vc->gfx.dcl.con, true); } #endif @@ -135,27 +136,6 @@ gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area); - if (vc->gfx.guest_fb.dmabuf && - qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { - /* - * gd_egl_refresh() calls gd_egl_draw() if a DMA-BUF draw has already - * been submitted, but this function does not call gd_gl_area_draw() in - * such a case due to display corruption. - * - * Calling gd_gl_area_draw() is necessary to prevent a situation where - * there is a scheduled draw event but it won't happen bacause the window - * is currently in inactive state (minimized or tabified). If draw is not - * done for a long time, gl_block timeout and/or fence timeout (on the - * guest) will happen eventually. - * - * However, it is found that calling gd_gl_area_draw() here causes guest - * display corruption on a Wayland Compositor. The display corruption is - * more serious than the possible fence timeout so gd_gl_area_draw() is - * omitted for now. - */ - return; - } - if (!vc->gfx.gls) { if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { return; @@ -314,7 +294,6 @@ if (vc->gfx.guest_fb.dmabuf && !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { - graphic_hw_gl_block(vc->gfx.dcl.con, true); qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); gtk_gl_area_set_scanout_mode(vc, true); } diff -Nru qemu-10.0.11+ds/ui/gtk.c qemu-10.0.13+ds/ui/gtk.c --- qemu-10.0.11+ds/ui/gtk.c 2026-06-25 21:39:15.000000000 +0000 +++ qemu-10.0.13+ds/ui/gtk.c 2026-08-26 19:31:26.000000000 +0000 @@ -2552,7 +2552,9 @@ if (!con) { break; } - gtk_widget_realize(s->vc[idx].gfx.drawing_area); + if (s->vc[idx].type == GD_VC_GFX) { + gtk_widget_realize(s->vc[idx].gfx.drawing_area); + } } if (opts->u.gtk.has_show_menubar && diff -Nru qemu-10.0.11+ds/ui/input-barrier.c qemu-10.0.13+ds/ui/input-barrier.c --- qemu-10.0.11+ds/ui/input-barrier.c 2026-06-25 21:39:15.000000000 +0000 +++ qemu-10.0.13+ds/ui/input-barrier.c 2026-08-26 19:31:26.000000000 +0000 @@ -87,7 +87,7 @@ static int input_barrier_to_qcode(uint16_t keyid, uint16_t keycode) { /* keycode is optional, if it is not provided use keyid */ - if (keycode && keycode <= qemu_input_map_xorgkbd_to_qcode_len) { + if (keycode && keycode < qemu_input_map_xorgkbd_to_qcode_len) { return qemu_input_map_xorgkbd_to_qcode[keycode]; } diff -Nru qemu-10.0.11+ds/ui/vnc.c qemu-10.0.13+ds/ui/vnc.c --- qemu-10.0.11+ds/ui/vnc.c 2026-06-25 21:39:15.000000000 +0000 +++ qemu-10.0.13+ds/ui/vnc.c 2026-08-26 19:31:26.000000000 +0000 @@ -2308,6 +2308,18 @@ return; } + if (red_max > UINT8_MAX || green_max > UINT8_MAX || blue_max > UINT8_MAX) { + vnc_client_error(vs); + return; + } + + if (red_shift >= bits_per_pixel || red_shift >= 32 || + green_shift >= bits_per_pixel || green_shift >= 32 || + blue_shift >= bits_per_pixel || blue_shift >= 32) { + vnc_client_error(vs); + return; + } + vs->client_pf.rmax = red_max ? red_max : 0xFF; vs->client_pf.rbits = ctpopl(red_max); vs->client_pf.rshift = red_shift; @@ -2982,16 +2994,23 @@ } } -static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) +static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y, + int height) { VncState *vs; int sty = y / VNC_STAT_RECT; int stx = x / VNC_STAT_RECT; int has_dirty = 0; + int rows; y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT); x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT); + rows = MIN(VNC_STAT_RECT, height - y); + if (rows <= 0) { + return 0; + } + QTAILQ_FOREACH(vs, &vd->clients, next) { int j; @@ -3005,7 +3024,7 @@ } vs->lossy_rect[sty][stx] = 0; - for (j = 0; j < VNC_STAT_RECT; ++j) { + for (j = 0; j < rows; ++j) { bitmap_set(vs->dirty[y + j], x / VNC_DIRTY_PIXELS_PER_BIT, VNC_STAT_RECT / VNC_DIRTY_PIXELS_PER_BIT); @@ -3056,7 +3075,7 @@ if (timercmp(&res, &VNC_REFRESH_LOSSY, >)) { rect->freq = 0; - has_dirty += vnc_refresh_lossy_rect(vd, x, y); + has_dirty += vnc_refresh_lossy_rect(vd, x, y, height); memset(rect->times, 0, sizeof (rect->times)); continue ; } diff -Nru qemu-10.0.11+ds/util/qemu-coroutine-sleep.c qemu-10.0.13+ds/util/qemu-coroutine-sleep.c --- qemu-10.0.11+ds/util/qemu-coroutine-sleep.c 2026-06-25 21:39:15.000000000 +0000 +++ qemu-10.0.13+ds/util/qemu-coroutine-sleep.c 2026-08-26 19:31:26.000000000 +0000 @@ -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);