Version in base suite: 10.0.8+ds-0+deb13u1 Base version: qemu_10.0.8+ds-0+deb13u1 Target version: qemu_10.0.10+ds-0+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/q/qemu/qemu_10.0.8+ds-0+deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/q/qemu/qemu_10.0.10+ds-0+deb13u1.dsc VERSION | 2 block.c | 38 - block/blkdebug.c | 15 block/block-backend.c | 47 +- block/curl.c | 13 block/export/fuse.c | 17 block/export/virtio-blk-handler.c | 2 block/graph-lock.c | 12 block/linux-aio.c | 88 +++- block/mirror.c | 84 ++- block/nfs.c | 19 block/throttle-groups.c | 100 +++- block/vmdk.c | 8 bsd-user/mmap.c | 8 bsd-user/signal.c | 10 chardev/char-win.c | 16 contrib/plugins/cache.c | 12 contrib/plugins/cflow.c | 10 contrib/plugins/hotblocks.c | 14 contrib/plugins/hotpages.c | 4 contrib/plugins/howvec.c | 4 contrib/plugins/hwprofile.c | 8 debian/changelog | 220 ++++++++++ debian/control.mk | 2 debian/patches/series | 1 debian/patches/virtio-gpu-virgl-Add-virtio-gpu-virgl-hostmem-region.patch | 179 -------- docs/about/build-platforms.rst | 2 docs/about/emulation.rst | 10 hw/9pfs/9p.c | 13 hw/audio/sb16.c | 7 hw/audio/virtio-snd.c | 68 +-- hw/block/virtio-blk.c | 100 +++- hw/char/riscv_htif.c | 6 hw/display/cirrus_vga_rop2.h | 52 ++ hw/display/virtio-gpu-virgl.c | 66 ++- hw/display/virtio-gpu.c | 44 +- hw/dma/pl080.c | 43 + hw/hyperv/syndbg.c | 23 - hw/i2c/aspeed_i2c.c | 22 - hw/i2c/microbit_i2c.c | 7 hw/i386/vmmouse.c | 10 hw/ide/core.c | 119 ++--- hw/intc/arm_gicv3_cpuif.c | 35 + hw/intc/riscv_aclint.c | 10 hw/intc/xics.c | 8 hw/misc/aspeed_hace.c | 25 + hw/misc/avr_power.c | 3 hw/misc/bcm2835_rng.c | 4 hw/misc/virt_ctrl.c | 2 hw/net/allwinner-sun8i-emac.c | 3 hw/net/ftgmac100.c | 10 hw/net/npcm_gmac.c | 14 hw/net/rocker/rocker_of_dpa.c | 1 hw/net/rtl8139.c | 23 + hw/net/smc91c111.c | 16 hw/net/trace-events | 1 hw/net/xilinx_ethlite.c | 12 hw/nvme/ctrl.c | 8 hw/nvme/ns.c | 2 hw/ppc/e500.c | 20 hw/ppc/e500.h | 5 hw/ppc/e500plat.c | 3 hw/ppc/mpc8544ds.c | 3 hw/ppc/pnv.c | 75 +-- hw/riscv/virt-acpi-build.c | 7 hw/s390x/s390-pci-inst.c | 2 hw/scsi/lsi53c895a.c | 71 ++- hw/scsi/scsi-bus.c | 7 hw/scsi/virtio-scsi.c | 26 - hw/sh4/sh7750.c | 1 hw/ssi/aspeed_smc.c | 49 +- hw/ssi/xilinx_spips.c | 4 hw/uefi/var-service-auth.c | 24 - hw/uefi/var-service-core.c | 9 hw/uefi/var-service-pkcs7.c | 23 - hw/uefi/var-service-utils.c | 42 + hw/uefi/var-service-vars.c | 22 - hw/ufs/trace-events | 3 hw/ufs/ufs.c | 92 +++- hw/ufs/ufs.h | 9 hw/usb/hcd-ohci.c | 11 hw/virtio/virtio.c | 10 include/block/block-common.h | 11 include/block/throttle-groups.h | 3 include/hw/block/block.h | 12 include/hw/i2c/aspeed_i2c.h | 3 include/hw/net/npcm_gmac.h | 3 include/hw/ppc/pnv.h | 2 include/hw/virtio/virtio.h | 10 include/io/task.h | 29 - include/system/block-backend-io.h | 6 io/channel-tls.c | 68 ++- io/channel-websock.c | 46 +- io/task.c | 8 linux-user/alpha/sockbits.h | 7 linux-user/arm/cpu_loop.c | 2 linux-user/arm/nwfpe/fpa11.c | 12 linux-user/arm/nwfpe/fpa11.h | 25 - linux-user/elfload.c | 40 + linux-user/fd-trans.c | 2 linux-user/generic/sockbits.h | 8 linux-user/hppa/sockbits.h | 7 linux-user/i386/signal.c | 25 + linux-user/ioctls.h | 31 - linux-user/main.c | 10 linux-user/mips/sockbits.h | 7 linux-user/mips/target_cpu.h | 5 linux-user/mmap.c | 61 ++ linux-user/ppc/signal.c | 16 linux-user/qemu.h | 1 linux-user/sh4/signal.c | 44 +- linux-user/signal.c | 9 linux-user/sparc/sockbits.h | 7 linux-user/strace.list | 4 linux-user/syscall.c | 194 +++++--- linux-user/syscall_defs.h | 11 linux-user/user-internals.h | 1 meson.build | 10 migration/vmstate.c | 7 monitor/monitor.c | 21 nbd/server.c | 2 plugins/meson.build | 9 python/setup.py | 4 qapi/block-core.json | 6 qapi/qmp-dispatch.c | 10 qemu-io-cmds.c | 2 qemu-keymap.c | 3 scripts/qemu-guest-agent/fsfreeze-hook | 34 + scsi/utils.c | 1 subprojects/berkeley-softfloat-3/.meson-subproject-wrap-hash.txt | 1 subprojects/berkeley-testfloat-3/.meson-subproject-wrap-hash.txt | 1 subprojects/keycodemapdb/.meson-subproject-wrap-hash.txt | 1 subprojects/libvfio-user/.meson-subproject-wrap-hash.txt | 1 target/arm/cpu.h | 1 target/arm/cpu64.c | 1 target/arm/helper.c | 5 target/arm/internals.h | 14 target/arm/ptw.c | 12 target/arm/tcg/op_helper.c | 2 target/arm/tcg/translate-sve.c | 4 target/arm/tcg/translate.c | 3 target/i386/cpu.c | 8 target/i386/hvf/x86_mmu.c | 6 target/i386/tcg/decode-new.c.inc | 16 target/i386/tcg/user/excp_helper.c | 7 target/loongarch/cpu.c | 13 target/loongarch/cpu.h | 1 target/loongarch/tcg/tlb_helper.c | 23 - target/microblaze/cpu.c | 4 target/riscv/cpu_helper.c | 5 target/riscv/csr.c | 8 target/riscv/helper.h | 3 target/riscv/insn_trans/trans_rva.c.inc | 6 target/riscv/op_helper.c | 14 target/riscv/vector_helper.c | 67 +-- tcg/tcg-op-ldst.c | 52 +- tests/docker/dockerfiles/debian-hexagon-cross.docker | 11 tests/docker/dockerfiles/debian-loongarch-cross.docker | 10 tests/docker/dockerfiles/debian-toolchain.docker | 8 tests/docker/dockerfiles/debian-tricore-cross.docker | 5 tests/docker/dockerfiles/debian-xtensa-cross.docker | 2 tests/docker/dockerfiles/fedora-rust-nightly.docker | 2 tests/docker/dockerfiles/ubuntu2204.docker | 2 tests/functional/qemu_test/asset.py | 13 tests/lcitool/refresh | 4 tests/qemu-iotests/257 | 8 tests/qemu-iotests/257.out | 14 tests/qtest/ide-test.c | 137 +++++- tests/tcg/multiarch/test-mmap.c | 15 tests/tcg/plugins/mem.c | 4 tests/tcg/plugins/syscall.c | 4 tests/unit/rcutorture.c | 2 tests/unit/test-block-iothread.c | 4 tests/unit/test-io-task.c | 26 + ui/console-vc.c | 2 ui/spice-app.c | 2 ui/vnc-jobs.c | 19 util/cutils.c | 3 util/meson.build | 2 util/readline.c | 4 180 files changed, 2466 insertions(+), 1175 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpl5ytvd82/qemu_10.0.8+ds-0+deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpl5ytvd82/qemu_10.0.10+ds-0+deb13u1.dsc: no acceptable signature found diff -Nru qemu-10.0.8+ds/VERSION qemu-10.0.10+ds/VERSION --- qemu-10.0.8+ds/VERSION 2026-02-12 20:30:13.000000000 +0000 +++ qemu-10.0.10+ds/VERSION 2026-05-25 22:03:52.000000000 +0000 @@ -1 +1 @@ -10.0.8 +10.0.10 diff -Nru qemu-10.0.8+ds/block/blkdebug.c qemu-10.0.10+ds/block/blkdebug.c --- qemu-10.0.8+ds/block/blkdebug.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/block/blkdebug.c 2026-05-25 22:03:52.000000000 +0000 @@ -95,6 +95,7 @@ int immediately; int once; int64_t offset; + int64_t delay_ns; } inject; struct { int new_state; @@ -144,6 +145,10 @@ .name = "immediately", .type = QEMU_OPT_BOOL, }, + { + .name = "delay-ns", + .type = QEMU_OPT_NUMBER, + }, { /* end of list */ } }, }; @@ -216,6 +221,8 @@ rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0); rule->options.inject.immediately = qemu_opt_get_bool(opts, "immediately", 0); + rule->options.inject.delay_ns = + qemu_opt_get_number(opts, "delay-ns", 0); sector = qemu_opt_get_number(opts, "sector", -1); rule->options.inject.offset = sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE; @@ -594,6 +601,7 @@ BlkdebugRule *rule = NULL; int error; bool immediately; + int64_t delay_ns; qemu_mutex_lock(&s->lock); QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) { @@ -608,13 +616,14 @@ } } - if (!rule || !rule->options.inject.error) { + if (!rule) { qemu_mutex_unlock(&s->lock); return 0; } immediately = rule->options.inject.immediately; error = rule->options.inject.error; + delay_ns = rule->options.inject.delay_ns; if (rule->options.inject.once) { QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next); @@ -622,6 +631,10 @@ } qemu_mutex_unlock(&s->lock); + + if (delay_ns) { + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, delay_ns); + } if (!immediately) { aio_co_schedule(qemu_get_current_aio_context(), qemu_coroutine_self()); qemu_coroutine_yield(); diff -Nru qemu-10.0.8+ds/block/block-backend.c qemu-10.0.10+ds/block/block-backend.c --- qemu-10.0.8+ds/block/block-backend.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/block/block-backend.c 2026-05-25 22:03:52.000000000 +0000 @@ -82,6 +82,7 @@ QemuMutex queued_requests_lock; /* protects queued_requests */ CoQueue queued_requests; bool disable_request_queuing; /* atomic */ + int start_request_count; /* atomic */ VMChangeStateEntry *vmsh; bool force_allow_inactivate; @@ -1306,10 +1307,16 @@ } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -static void coroutine_fn blk_wait_while_drained(BlockBackend *blk) +static void coroutine_fn blk_wait_while_drained(BlockBackend *blk, + BdrvRequestFlags flags) { assert(blk->in_flight > 0); + if (flags & BDRV_REQ_NO_QUEUE) { + assert(qatomic_read(&blk->start_request_count)); + return; + } + if (qatomic_read(&blk->quiesce_counter) && !qatomic_read(&blk->disable_request_queuing)) { /* @@ -1335,7 +1342,7 @@ BlockDriverState *bs; IO_CODE(); - blk_wait_while_drained(blk); + blk_wait_while_drained(blk, flags); GRAPH_RDLOCK_GUARD(); /* Call blk_bs() only after waiting, the graph may have changed */ @@ -1410,7 +1417,7 @@ BlockDriverState *bs; IO_CODE(); - blk_wait_while_drained(blk); + blk_wait_while_drained(blk, flags); GRAPH_RDLOCK_GUARD(); /* Call blk_bs() only after waiting, the graph may have changed */ @@ -1523,6 +1530,19 @@ aio_wait_kick(); } +void coroutine_fn blk_co_start_request(BlockBackend *blk) +{ + blk_inc_in_flight(blk); + blk_wait_while_drained(blk, 0); + qatomic_inc(&blk->start_request_count); +} + +void blk_end_request(BlockBackend *blk) +{ + qatomic_dec(&blk->start_request_count); + blk_dec_in_flight(blk); +} + static void error_callback_bh(void *opaque) { struct BlockBackendAIOCB *acb = opaque; @@ -1741,7 +1761,7 @@ { IO_CODE(); - blk_wait_while_drained(blk); + blk_wait_while_drained(blk, 0); GRAPH_RDLOCK_GUARD(); if (!blk_co_is_available(blk)) { @@ -1783,12 +1803,13 @@ /* To be called between exactly one pair of blk_inc/dec_in_flight() */ static int coroutine_fn -blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) +blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { int ret; IO_CODE(); - blk_wait_while_drained(blk); + blk_wait_while_drained(blk, flags); GRAPH_RDLOCK_GUARD(); ret = blk_check_byte_request(blk, offset, bytes); @@ -1804,7 +1825,7 @@ BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; - rwco->ret = blk_co_do_pdiscard(rwco->blk, rwco->offset, acb->bytes); + rwco->ret = blk_co_do_pdiscard(rwco->blk, rwco->offset, acb->bytes, 0); blk_aio_complete(acb); } @@ -1818,13 +1839,13 @@ } int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, - int64_t bytes) + int64_t bytes, BdrvRequestFlags flags) { int ret; IO_OR_GS_CODE(); blk_inc_in_flight(blk); - ret = blk_co_do_pdiscard(blk, offset, bytes); + ret = blk_co_do_pdiscard(blk, offset, bytes, flags); blk_dec_in_flight(blk); return ret; @@ -1834,7 +1855,7 @@ static int coroutine_fn blk_co_do_flush(BlockBackend *blk) { IO_CODE(); - blk_wait_while_drained(blk); + blk_wait_while_drained(blk, 0); GRAPH_RDLOCK_GUARD(); if (!blk_co_is_available(blk)) { @@ -2009,7 +2030,7 @@ IO_CODE(); blk_inc_in_flight(blk); /* increase before waiting */ - blk_wait_while_drained(blk); + blk_wait_while_drained(blk, 0); GRAPH_RDLOCK_GUARD(); if (!blk_is_available(blk)) { blk_dec_in_flight(blk); @@ -2034,7 +2055,7 @@ IO_CODE(); blk_inc_in_flight(blk); - blk_wait_while_drained(blk); + blk_wait_while_drained(blk, 0); GRAPH_RDLOCK_GUARD(); ret = blk_check_byte_request(blk, offset, len); @@ -2058,7 +2079,7 @@ IO_CODE(); blk_inc_in_flight(blk); - blk_wait_while_drained(blk); + blk_wait_while_drained(blk, flags); GRAPH_RDLOCK_GUARD(); if (!blk_is_available(blk)) { blk_dec_in_flight(blk); diff -Nru qemu-10.0.8+ds/block/curl.c qemu-10.0.10+ds/block/curl.c --- qemu-10.0.8+ds/block/curl.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/block/curl.c 2026-05-25 22:03:52.000000000 +0000 @@ -324,17 +324,11 @@ static void curl_multi_check_completion(BDRVCURLState *s) { int msgs_in_queue; + CURLMsg *msg; /* Try to find done transfers, so we can free the easy * handle again. */ - for (;;) { - CURLMsg *msg; - msg = curl_multi_info_read(s->multi, &msgs_in_queue); - - /* Quit when there are no more completions */ - if (!msg) - break; - + while ((msg = curl_multi_info_read(s->multi, &msgs_in_queue))) { if (msg->msg == CURLMSG_DONE) { int i; CURLState *state = NULL; @@ -397,7 +391,6 @@ } curl_clean_state(state); - break; } } } @@ -883,6 +876,7 @@ g_free(s->cookie); g_free(s->url); g_free(s->username); + g_free(s->password); g_free(s->proxyusername); g_free(s->proxypassword); if (s->sockets) { @@ -994,6 +988,7 @@ g_free(s->cookie); g_free(s->url); g_free(s->username); + g_free(s->password); g_free(s->proxyusername); g_free(s->proxypassword); } diff -Nru qemu-10.0.8+ds/block/export/fuse.c qemu-10.0.10+ds/block/export/fuse.c --- qemu-10.0.8+ds/block/export/fuse.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/block/export/fuse.c 2026-05-25 22:03:52.000000000 +0000 @@ -301,6 +301,12 @@ goto out; } + /* + * Note that aio_poll() in any request-processing function can lead to a + * nested read_from_fuse_export() call, which will overwrite the contents of + * exp->fuse_buf. Anything that takes a buffer needs to take care that the + * content is copied before potentially polling via aio_poll(). + */ fuse_session_process_buf(exp->fuse_session, &exp->fuse_buf); out: @@ -624,6 +630,7 @@ size_t size, off_t offset, struct fuse_file_info *fi) { FuseExport *exp = fuse_req_userdata(req); + QEMU_AUTO_VFREE void *copied = NULL; int64_t length; int ret; @@ -638,6 +645,14 @@ return; } + /* + * Heed the note on read_from_fuse_export(): If we call aio_poll() (which + * any blk_*() I/O function may do), read_from_fuse_export() may be nested, + * overwriting the request buffer content. Therefore, we must copy it here. + */ + copied = blk_blockalign(exp->common.blk, size); + memcpy(copied, buf, size); + /** * Clients will expect short writes at EOF, so we have to limit * offset+size to the image length. @@ -660,7 +675,7 @@ } } - ret = blk_pwrite(exp->common.blk, offset, size, buf, 0); + ret = blk_pwrite(exp->common.blk, offset, size, copied, 0); if (ret >= 0) { fuse_reply_write(req, size); } else { diff -Nru qemu-10.0.8+ds/block/export/virtio-blk-handler.c qemu-10.0.10+ds/block/export/virtio-blk-handler.c --- qemu-10.0.8+ds/block/export/virtio-blk-handler.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/block/export/virtio-blk-handler.c 2026-05-25 22:03:52.000000000 +0000 @@ -121,7 +121,7 @@ } if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS, - bytes) == 0) { + bytes, 0) == 0) { return VIRTIO_BLK_S_OK; } } diff -Nru qemu-10.0.8+ds/block/graph-lock.c qemu-10.0.10+ds/block/graph-lock.c --- qemu-10.0.8+ds/block/graph-lock.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/block/graph-lock.c 2026-05-25 22:03:52.000000000 +0000 @@ -244,14 +244,12 @@ smp_mb(); /* - * has_writer == 0: this means reader will read reader_count decreased - * has_writer == 1: we don't know if writer read reader_count old or - * new. Therefore, kick again so on next iteration - * writer will for sure read the updated value. + * Always kick: bdrv_graph_wrlock() zeroes has_writer while polling (to + * let callbacks take the reader lock via the fast path), so we cannot + * rely on has_writer to detect a waiting writer. aio_wait_kick() is a + * no-op when no one is waiting, so it is cheap in the common case. */ - if (qatomic_read(&has_writer)) { - aio_wait_kick(); - } + aio_wait_kick(); } void bdrv_graph_rdlock_main_loop(void) diff -Nru qemu-10.0.8+ds/block/linux-aio.c qemu-10.0.10+ds/block/linux-aio.c --- qemu-10.0.8+ds/block/linux-aio.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/block/linux-aio.c 2026-05-25 22:03:52.000000000 +0000 @@ -41,9 +41,19 @@ LinuxAioState *ctx; struct iocb iocb; ssize_t ret; + off_t offset; size_t nbytes; QEMUIOVector *qiov; - bool is_read; + + /* For handling short reads/writes */ + size_t total_done; + QEMUIOVector resubmit_qiov; + + int fd; + int type; + BdrvRequestFlags flags; + + uint64_t dev_max_batch; QSIMPLEQ_ENTRY(qemu_laiocb) next; }; @@ -68,28 +78,61 @@ }; static void ioq_submit(LinuxAioState *s); +static int laio_do_submit(struct qemu_laiocb *laiocb); static inline ssize_t io_event_ret(struct io_event *ev) { return (ssize_t)(((uint64_t)ev->res2 << 32) | ev->res); } +/** + * Retry tail of short requests. + */ +static int laio_resubmit_short_io(struct qemu_laiocb *laiocb, size_t done) +{ + QEMUIOVector *resubmit_qiov = &laiocb->resubmit_qiov; + + laiocb->total_done += done; + + if (!resubmit_qiov->iov) { + qemu_iovec_init(resubmit_qiov, laiocb->qiov->niov); + } else { + qemu_iovec_reset(resubmit_qiov); + } + qemu_iovec_concat(resubmit_qiov, laiocb->qiov, + laiocb->total_done, laiocb->nbytes - laiocb->total_done); + + return laio_do_submit(laiocb); +} + /* * Completes an AIO request. */ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb) { - int ret; + ssize_t ret; ret = laiocb->ret; if (ret != -ECANCELED) { - if (ret == laiocb->nbytes) { + if (ret == laiocb->nbytes - laiocb->total_done) { ret = 0; + } else if (ret > 0 && (laiocb->type == QEMU_AIO_READ || + laiocb->type == QEMU_AIO_WRITE)) { + ret = laio_resubmit_short_io(laiocb, ret); + if (!ret) { + return; + } } else if (ret >= 0) { - /* Short reads mean EOF, pad with zeros. */ - if (laiocb->is_read) { - qemu_iovec_memset(laiocb->qiov, ret, 0, - laiocb->qiov->size - ret); + /* + * For normal reads and writes, we only get here if ret == 0, which + * means EOF for reads and ENOSPC for writes. + * For zone-append, we get here with any ret >= 0, which we just + * treat as ENOSPC, too (safer than resubmitting, probably, but not + * 100 % clear). + */ + if (laiocb->type == QEMU_AIO_READ) { + qemu_iovec_memset(laiocb->qiov, laiocb->total_done, 0, + laiocb->qiov->size - laiocb->total_done); } else { ret = -ENOSPC; } @@ -97,6 +140,9 @@ } laiocb->ret = ret; + if (laiocb->resubmit_qiov.iov) { + qemu_iovec_destroy(&laiocb->resubmit_qiov); + } /* * If the coroutine is already entered it must be in ioq_submit() and @@ -367,23 +413,27 @@ } } -static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, - int type, BdrvRequestFlags flags, - uint64_t dev_max_batch) +static int laio_do_submit(struct qemu_laiocb *laiocb) { LinuxAioState *s = laiocb->ctx; struct iocb *iocbs = &laiocb->iocb; QEMUIOVector *qiov = laiocb->qiov; + int fd = laiocb->fd; + off_t offset = laiocb->offset + laiocb->total_done; + + if (laiocb->resubmit_qiov.iov) { + qiov = &laiocb->resubmit_qiov; + } - switch (type) { + switch (laiocb->type) { case QEMU_AIO_WRITE: #ifdef HAVE_IO_PREP_PWRITEV2 { - int laio_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0; + int laio_flags = (laiocb->flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0; io_prep_pwritev2(iocbs, fd, qiov->iov, qiov->niov, offset, laio_flags); } #else - assert(flags == 0); + assert(laiocb->flags == 0); io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); #endif break; @@ -399,7 +449,7 @@ /* Currently Linux kernel does not support other operations */ default: fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", - __func__, type); + __func__, laiocb->type); return -EIO; } io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e)); @@ -407,7 +457,7 @@ QSIMPLEQ_INSERT_TAIL(&s->io_q.pending, laiocb, next); s->io_q.in_queue++; if (!s->io_q.blocked) { - if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch)) { + if (s->io_q.in_queue >= laio_max_batch(s, laiocb->dev_max_batch)) { ioq_submit(s); } else { defer_call(laio_deferred_fn, s); @@ -425,14 +475,18 @@ AioContext *ctx = qemu_get_current_aio_context(); struct qemu_laiocb laiocb = { .co = qemu_coroutine_self(), + .offset = offset, .nbytes = qiov ? qiov->size : 0, .ctx = aio_get_linux_aio(ctx), .ret = -EINPROGRESS, - .is_read = (type == QEMU_AIO_READ), .qiov = qiov, + .fd = fd, + .type = type, + .flags = flags, + .dev_max_batch = dev_max_batch, }; - ret = laio_do_submit(fd, &laiocb, offset, type, flags, dev_max_batch); + ret = laio_do_submit(&laiocb); if (ret < 0) { return ret; } diff -Nru qemu-10.0.8+ds/block/mirror.c qemu-10.0.10+ds/block/mirror.c --- qemu-10.0.8+ds/block/mirror.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/block/mirror.c 2026-05-25 22:03:52.000000000 +0000 @@ -98,6 +98,7 @@ typedef struct MirrorBDSOpaque { MirrorBlockJob *job; + BdrvDirtyBitmap *dirty_bitmap; bool stop; bool is_commit; } MirrorBDSOpaque; @@ -430,7 +431,7 @@ *op->bytes_handled = op->bytes; op->is_in_flight = true; - ret = blk_co_pdiscard(op->s->target, op->offset, op->bytes); + ret = blk_co_pdiscard(op->s->target, op->offset, op->bytes, 0); mirror_write_complete(op, ret); } @@ -1184,23 +1185,25 @@ return; } - /* block all operations on to_replace bs */ - if (s->replaces) { - s->to_replace = bdrv_find_node(s->replaces); - if (!s->to_replace) { - error_setg(errp, "Node name '%s' not found", s->replaces); - return; + if (!s->should_complete) { + /* block all operations on to_replace bs */ + if (s->replaces) { + s->to_replace = bdrv_find_node(s->replaces); + if (!s->to_replace) { + error_setg(errp, "Node name '%s' not found", s->replaces); + return; + } + + /* TODO Translate this into child freeze system. */ + error_setg(&s->replace_blocker, + "block device is in use by block-job-complete"); + bdrv_op_block_all(s->to_replace, s->replace_blocker); + bdrv_ref(s->to_replace); } - /* TODO Translate this into child freeze system. */ - error_setg(&s->replace_blocker, - "block device is in use by block-job-complete"); - bdrv_op_block_all(s->to_replace, s->replace_blocker); - bdrv_ref(s->to_replace); + s->should_complete = true; } - s->should_complete = true; - /* If the job is paused, it will be re-entered when it is resumed */ WITH_JOB_LOCK_GUARD() { if (!job->paused) { @@ -1411,7 +1414,7 @@ case MIRROR_METHOD_DISCARD: assert(!qiov); - ret = blk_co_pdiscard(job->target, offset, bytes); + ret = blk_co_pdiscard(job->target, offset, bytes, 0); break; default: @@ -1557,9 +1560,11 @@ abort(); } - if (!copy_to_target && s->job && s->job->dirty_bitmap) { - qatomic_set(&s->job->actively_synced, false); - bdrv_set_dirty_bitmap(s->job->dirty_bitmap, offset, bytes); + if (!copy_to_target) { + if (s->job) { + qatomic_set(&s->job->actively_synced, false); + } + bdrv_set_dirty_bitmap(s->dirty_bitmap, offset, bytes); } if (ret < 0) { @@ -1785,13 +1790,35 @@ bdrv_drained_begin(bs); ret = bdrv_append(mirror_top_bs, bs, errp); - bdrv_drained_end(bs); - if (ret < 0) { + bdrv_drained_end(bs); + bdrv_unref(mirror_top_bs); + return NULL; + } + + bs_opaque->dirty_bitmap = bdrv_create_dirty_bitmap(mirror_top_bs, + granularity, + NULL, errp); + if (!bs_opaque->dirty_bitmap) { + bdrv_drained_end(bs); bdrv_unref(mirror_top_bs); return NULL; } + /* + * The mirror job doesn't use the block layer's dirty tracking because it + * needs to be able to switch seemlessly between background copy mode (which + * does need dirty tracking) and write blocking mode (which doesn't) and + * doing that would require draining the node. Instead, mirror_top_bs takes + * care of updating the dirty bitmap as appropriate. + * + * Note that write blocking mode only becomes effective after mirror_run() + * sets mirror_top_opaque->job (see should_copy_to_target()). Until then, + * we're still in background copy mode irrespective of @copy_mode. + */ + bdrv_disable_dirty_bitmap(bs_opaque->dirty_bitmap); + bdrv_drained_end(bs); + /* Make sure that the source is not resized while the job is running */ s = block_job_create(job_id, driver, NULL, mirror_top_bs, BLK_PERM_CONSISTENT_READ, @@ -1886,24 +1913,13 @@ s->base_overlay = bdrv_find_overlay(bs, base); s->granularity = granularity; s->buf_size = ROUND_UP(buf_size, granularity); + s->dirty_bitmap = bs_opaque->dirty_bitmap; s->unmap = unmap; if (auto_complete) { s->should_complete = true; } bdrv_graph_rdunlock_main_loop(); - s->dirty_bitmap = bdrv_create_dirty_bitmap(s->mirror_top_bs, granularity, - NULL, errp); - if (!s->dirty_bitmap) { - goto fail; - } - - /* - * The dirty bitmap is set by bdrv_mirror_top_do_write() when not in active - * mode. - */ - bdrv_disable_dirty_bitmap(s->dirty_bitmap); - bdrv_graph_wrlock(); ret = block_job_add_bdrv(&s->common, "source", bs, 0, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | @@ -1983,9 +1999,6 @@ g_free(s->replaces); blk_unref(s->target); bs_opaque->job = NULL; - if (s->dirty_bitmap) { - bdrv_release_dirty_bitmap(s->dirty_bitmap); - } job_early_fail(&s->common.job); } @@ -1999,6 +2012,7 @@ bdrv_graph_wrunlock(); bdrv_drained_end(bs); + bdrv_release_dirty_bitmap(bs_opaque->dirty_bitmap); bdrv_unref(mirror_top_bs); return NULL; diff -Nru qemu-10.0.8+ds/block/nfs.c qemu-10.0.10+ds/block/nfs.c --- qemu-10.0.8+ds/block/nfs.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/block/nfs.c 2026-05-25 22:03:52.000000000 +0000 @@ -249,14 +249,15 @@ } /* - * Safe to call: nfs_service(), which called us, is only run from the FD - * handlers, never from the request coroutine. The request coroutine in - * turn will yield unconditionally. - * No need to release the lock, even if we directly enter the coroutine, as - * the lock is never re-taken after yielding. (Note: If we do enter the - * coroutine, @task will probably be dangling once aio_co_wake() returns.) + * Using aio_co_wake() here could re-enter the coroutine directly, while we + * still hold the mutex. The current request will not attempt to re-take + * the mutex, so that is fine; but if the same coroutine then goes on to + * submit another request, that new request will try to re-take the mutex, + * resulting in a deadlock. + * To prevent that, only schedule the coroutine so it will be entered later, + * with the mutex released. */ - aio_co_wake(task->co); + aio_co_schedule(qemu_coroutine_get_aio_context(task->co), task->co); } static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, int64_t offset, @@ -716,8 +717,8 @@ if (task->ret < 0) { error_report("NFS Error: %s", nfs_get_error(nfs)); } - /* Safe to call, see nfs_co_generic_cb() */ - aio_co_wake(task->co); + /* Must not use aio_co_wake(), see nfs_co_generic_cb() */ + aio_co_schedule(qemu_coroutine_get_aio_context(task->co), task->co); } static int64_t coroutine_fn nfs_co_get_allocated_file_size(BlockDriverState *bs) diff -Nru qemu-10.0.8+ds/block/throttle-groups.c qemu-10.0.10+ds/block/throttle-groups.c --- qemu-10.0.8+ds/block/throttle-groups.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/block/throttle-groups.c 2026-05-25 22:03:52.000000000 +0000 @@ -295,19 +295,15 @@ /* Start the next pending I/O request for a ThrottleGroupMember. Return whether * any request was actually pending. * + * This assumes that tg->lock is held. + * * @tgm: the current ThrottleGroupMember * @direction: the ThrottleDirection */ static bool coroutine_fn throttle_group_co_restart_queue(ThrottleGroupMember *tgm, ThrottleDirection direction) { - bool ret; - - qemu_co_mutex_lock(&tgm->throttled_reqs_lock); - ret = qemu_co_queue_next(&tgm->throttled_reqs[direction]); - qemu_co_mutex_unlock(&tgm->throttled_reqs_lock); - - return ret; + return qemu_co_queue_next(&tgm->throttled_reqs[direction]); } /* Look for the next pending I/O request and schedule it. @@ -378,12 +374,8 @@ /* Wait if there's a timer set or queued requests of this type */ if (must_wait || tgm->pending_reqs[direction]) { tgm->pending_reqs[direction]++; - qemu_mutex_unlock(&tg->lock); - qemu_co_mutex_lock(&tgm->throttled_reqs_lock); qemu_co_queue_wait(&tgm->throttled_reqs[direction], - &tgm->throttled_reqs_lock); - qemu_co_mutex_unlock(&tgm->throttled_reqs_lock); - qemu_mutex_lock(&tg->lock); + &tg->lock); tgm->pending_reqs[direction]--; } @@ -399,6 +391,7 @@ typedef struct { ThrottleGroupMember *tgm; ThrottleDirection direction; + bool reset_timer_armed; } RestartData; static void coroutine_fn throttle_group_restart_queue_entry(void *opaque) @@ -410,15 +403,18 @@ ThrottleDirection direction = data->direction; bool empty_queue; + qemu_mutex_lock(&tg->lock); + if (data->reset_timer_armed) { + tg->any_timer_armed[direction] = false; + } empty_queue = !throttle_group_co_restart_queue(tgm, direction); /* If the request queue was empty then we have to take care of * scheduling the next one */ if (empty_queue) { - qemu_mutex_lock(&tg->lock); schedule_next_request(tgm, direction); - qemu_mutex_unlock(&tg->lock); } + qemu_mutex_unlock(&tg->lock); g_free(data); @@ -427,18 +423,23 @@ } static void throttle_group_restart_queue(ThrottleGroupMember *tgm, - ThrottleDirection direction) + ThrottleDirection direction, + bool reset_timer_armed) { Coroutine *co; RestartData *rd = g_new0(RestartData, 1); rd->tgm = tgm; rd->direction = direction; + rd->reset_timer_armed = reset_timer_armed; - /* This function is called when a timer is fired or when - * throttle_group_restart_tgm() is called. Either way, there can + /* If reset_timer_armed is set then this means that this function + * was called when a timer was fired (either from timer_cb() or + * from throttle_group_restart_tgm()). In this case there can * be no timer pending on this tgm at this point */ - assert(!timer_pending(tgm->throttle_timers.timers[direction])); + if (reset_timer_armed) { + assert(!timer_pending(tgm->throttle_timers.timers[direction])); + } qatomic_inc(&tgm->restart_pending); @@ -452,15 +453,50 @@ if (tgm->throttle_state) { for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) { - QEMUTimer *t = tgm->throttle_timers.timers[dir]; + QEMUTimer *t; + ThrottleState *ts = tgm->throttle_state; + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + bool reset_timer_armed; + + /* + * This function restarts the tgm's queue immediately. + * This is used for example for callers to drain all requests. + * There are three different scenarios depending on whether + * a timer is armed for this tg and which tgm owns the timer. + */ + + qemu_mutex_lock(&tg->lock); + + t = tgm->throttle_timers.timers[dir]; if (timer_pending(t)) { - /* If there's a pending timer on this tgm, fire it now */ + /* + * Case 1: this tgm has a pending timer. + * We can fire the timer immediately. + */ timer_del(t); - timer_cb(tgm, dir); + reset_timer_armed = true; + } else if (tg->any_timer_armed[dir]) { + /* + * Case 2: another tgm has a pending timer. + * In this case we can still restart the queue but we + * have to leave any_timer_armed untouched so the + * other tgm's timer is not disrupted. + */ + reset_timer_armed = false; } else { - /* Else run the next request from the queue manually */ - throttle_group_restart_queue(tgm, dir); + /* + * Case 3: there is no timer set for this group. + * Here we can simulate a timer that fires immediately, + * so the queue is restarted but no other thread + * can arm a timer in the meantime. + */ + tg->any_timer_armed[dir] = true; + reset_timer_armed = true; } + + qemu_mutex_unlock(&tg->lock); + + throttle_group_restart_queue(tgm, dir, reset_timer_armed); } } } @@ -507,16 +543,13 @@ */ static void timer_cb(ThrottleGroupMember *tgm, ThrottleDirection direction) { - ThrottleState *ts = tgm->throttle_state; - ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); - - /* The timer has just been fired, so we can update the flag */ - qemu_mutex_lock(&tg->lock); - tg->any_timer_armed[direction] = false; - qemu_mutex_unlock(&tg->lock); - - /* Run the request that was waiting for this timer */ - throttle_group_restart_queue(tgm, direction); + /* + * Run the request that was waiting for this timer. + * tg->any_timer_armed needs to be cleared, but we'll do it later + * when the queue is restarted in order to prevent another thread + * from arming the timer before that. + */ + throttle_group_restart_queue(tgm, direction, true); } static void read_timer_cb(void *opaque) @@ -569,7 +602,6 @@ read_timer_cb, write_timer_cb, tgm); - qemu_co_mutex_init(&tgm->throttled_reqs_lock); } /* Unregister a ThrottleGroupMember from its group, removing it from the list, diff -Nru qemu-10.0.8+ds/block/vmdk.c qemu-10.0.10+ds/block/vmdk.c --- qemu-10.0.8+ds/block/vmdk.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/block/vmdk.c 2026-05-25 22:03:52.000000000 +0000 @@ -1949,10 +1949,10 @@ marker = (VmdkGrainMarker *)cluster_buf; compressed_data = marker->data; data_len = le32_to_cpu(marker->size); - } - if (!data_len || data_len > buf_bytes) { - ret = -EINVAL; - goto out; + if (!data_len || data_len > buf_bytes - sizeof(VmdkGrainMarker)) { + ret = -EINVAL; + goto out; + } } ret = uncompress(uncomp_buf, &buf_len, compressed_data, data_len); if (ret != Z_OK) { diff -Nru qemu-10.0.8+ds/block.c qemu-10.0.10+ds/block.c --- qemu-10.0.8+ds/block.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/block.c 2026-05-25 22:03:52.000000000 +0000 @@ -5377,17 +5377,13 @@ * * With auto_skip=false the error is returned if from has a parent which should * not be updated. - * - * With @detach_subchain=true @to must be in a backing chain of @from. In this - * case backing link of the cow-parent of @to is removed. */ static int GRAPH_WRLOCK bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to, - bool auto_skip, bool detach_subchain, Error **errp) + bool auto_skip, Error **errp) { Transaction *tran = tran_new(); g_autoptr(GSList) refresh_list = NULL; - BlockDriverState *to_cow_parent = NULL; int ret; GLOBAL_STATE_CODE(); @@ -5396,17 +5392,6 @@ assert(to->quiesce_counter); assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to)); - if (detach_subchain) { - assert(bdrv_chain_contains(from, to)); - assert(from != to); - for (to_cow_parent = from; - bdrv_filter_or_cow_bs(to_cow_parent) != to; - to_cow_parent = bdrv_filter_or_cow_bs(to_cow_parent)) - { - ; - } - } - /* * Do the replacement without permission update. * Replacement may influence the permissions, we should calculate new @@ -5418,11 +5403,6 @@ goto out; } - if (detach_subchain) { - /* to_cow_parent is already drained because from is drained */ - bdrv_remove_child(bdrv_filter_or_cow_child(to_cow_parent), tran); - } - refresh_list = g_slist_prepend(refresh_list, to); refresh_list = g_slist_prepend(refresh_list, from); @@ -5441,7 +5421,7 @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp) { - return bdrv_replace_node_common(from, to, true, false, errp); + return bdrv_replace_node_common(from, to, true, errp); } int bdrv_drop_filter(BlockDriverState *bs, Error **errp) @@ -5457,7 +5437,7 @@ bdrv_drained_begin(child_bs); bdrv_graph_wrlock(); - ret = bdrv_replace_node_common(bs, child_bs, true, true, errp); + ret = bdrv_replace_node_common(bs, child_bs, true, errp); bdrv_graph_wrunlock(); bdrv_drained_end(child_bs); @@ -5914,17 +5894,7 @@ updated_children = g_slist_prepend(updated_children, c); } - /* - * It seems correct to pass detach_subchain=true here, but it triggers - * one more yet not fixed bug, when due to nested aio_poll loop we switch to - * another drained section, which modify the graph (for example, removing - * the child, which we keep in updated_children list). So, it's a TODO. - * - * Note, bug triggered if pass detach_subchain=true here and run - * test-bdrv-drain. test_drop_intermediate_poll() test-case will crash. - * That's a FIXME. - */ - bdrv_replace_node_common(top, base, false, false, &local_err); + bdrv_replace_node_common(top, base, false, &local_err); bdrv_graph_wrunlock(); if (local_err) { diff -Nru qemu-10.0.8+ds/bsd-user/mmap.c qemu-10.0.10+ds/bsd-user/mmap.c --- qemu-10.0.8+ds/bsd-user/mmap.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/bsd-user/mmap.c 2026-05-25 22:03:52.000000000 +0000 @@ -257,12 +257,14 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size, abi_ulong alignment) { - abi_ulong ret; + abi_ulong ret = -1; - ret = page_find_range_empty(start, reserved_va, size, alignment); + if (start <= reserved_va) { + ret = page_find_range_empty(start, reserved_va, size, alignment); + } if (ret == -1 && start > TARGET_PAGE_SIZE) { /* Restart at the beginning of the address space. */ - ret = page_find_range_empty(TARGET_PAGE_SIZE, start - 1, + ret = page_find_range_empty(TARGET_PAGE_SIZE, MIN(start - 1, reserved_va), size, alignment); } diff -Nru qemu-10.0.8+ds/bsd-user/signal.c qemu-10.0.10+ds/bsd-user/signal.c --- qemu-10.0.8+ds/bsd-user/signal.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/bsd-user/signal.c 2026-05-25 22:03:52.000000000 +0000 @@ -999,7 +999,12 @@ sigdelset(&ts->signal_mask, target_to_host_signal(sig)); sigact_table[sig - 1]._sa_handler = TARGET_SIG_DFL; } + /* + * Restart scan from the beginning, as handle_pending_signal + * might have resulted in a new synchronous signal (eg SIGSEGV). + */ handle_pending_signal(env, sig, &ts->sync_signal); + goto restart_scan; } k = ts->sigtab; @@ -1009,10 +1014,7 @@ if (k->pending && !sigismember(blocked_set, target_to_host_signal(sig))) { handle_pending_signal(env, sig, k); - /* - * Restart scan from the beginning, as handle_pending_signal - * might have resulted in a new synchronous signal (eg SIGSEGV). - */ + /* Restart scan, explained above. */ goto restart_scan; } } diff -Nru qemu-10.0.8+ds/chardev/char-win.c qemu-10.0.10+ds/chardev/char-win.c --- qemu-10.0.8+ds/chardev/char-win.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/chardev/char-win.c 2026-05-25 22:03:52.000000000 +0000 @@ -28,7 +28,7 @@ #include "qapi/error.h" #include "chardev/char-win.h" -static void win_chr_read(Chardev *chr, DWORD len) +static int win_chr_read(Chardev *chr, DWORD len) { WinChardev *s = WIN_CHARDEV(chr); int max_size = qemu_chr_be_can_write(chr); @@ -40,7 +40,7 @@ len = max_size; } if (len == 0) { - return; + return 0; } ZeroMemory(&s->orecv, sizeof(s->orecv)); @@ -56,6 +56,8 @@ if (size > 0) { qemu_chr_be_write(chr, buf, size); } + + return size > 0 ? 1 : 0; } static int win_chr_serial_poll(void *opaque) @@ -67,8 +69,9 @@ ClearCommError(s->file, &comerr, &status); if (status.cbInQue > 0) { - win_chr_read(chr, status.cbInQue); - return 1; + if (win_chr_read(chr, status.cbInQue)) { + return 1; + } } return 0; } @@ -147,8 +150,9 @@ PeekNamedPipe(s->file, NULL, 0, NULL, &size, NULL); if (size > 0) { - win_chr_read(chr, size); - return 1; + if (win_chr_read(chr, size)) { + return 1; + } } return 0; } diff -Nru qemu-10.0.8+ds/contrib/plugins/cache.c qemu-10.0.10+ds/contrib/plugins/cache.c --- qemu-10.0.8+ds/contrib/plugins/cache.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/contrib/plugins/cache.c 2026-05-25 22:03:52.000000000 +0000 @@ -576,7 +576,7 @@ } } -static int dcmp(gconstpointer a, gconstpointer b) +static int dcmp(gconstpointer a, gconstpointer b, gpointer d) { InsnData *insn_a = (InsnData *) a; InsnData *insn_b = (InsnData *) b; @@ -584,7 +584,7 @@ return insn_a->l1_dmisses < insn_b->l1_dmisses ? 1 : -1; } -static int icmp(gconstpointer a, gconstpointer b) +static int icmp(gconstpointer a, gconstpointer b, gpointer d) { InsnData *insn_a = (InsnData *) a; InsnData *insn_b = (InsnData *) b; @@ -592,7 +592,7 @@ return insn_a->l1_imisses < insn_b->l1_imisses ? 1 : -1; } -static int l2_cmp(gconstpointer a, gconstpointer b) +static int l2_cmp(gconstpointer a, gconstpointer b, gpointer d) { InsnData *insn_a = (InsnData *) a; InsnData *insn_b = (InsnData *) b; @@ -645,7 +645,7 @@ InsnData *insn; miss_insns = g_hash_table_get_values(miss_ht); - miss_insns = g_list_sort(miss_insns, dcmp); + miss_insns = g_list_sort_with_data(miss_insns, dcmp, NULL); g_autoptr(GString) rep = g_string_new(""); g_string_append_printf(rep, "%s", "address, data misses, instruction\n"); @@ -659,7 +659,7 @@ insn->l1_dmisses, insn->disas_str); } - miss_insns = g_list_sort(miss_insns, icmp); + miss_insns = g_list_sort_with_data(miss_insns, icmp, NULL); g_string_append_printf(rep, "%s", "\naddress, fetch misses, instruction\n"); for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) { @@ -676,7 +676,7 @@ goto finish; } - miss_insns = g_list_sort(miss_insns, l2_cmp); + miss_insns = g_list_sort_with_data(miss_insns, l2_cmp, NULL); g_string_append_printf(rep, "%s", "\naddress, L2 misses, instruction\n"); for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) { diff -Nru qemu-10.0.8+ds/contrib/plugins/cflow.c qemu-10.0.10+ds/contrib/plugins/cflow.c --- qemu-10.0.8+ds/contrib/plugins/cflow.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/contrib/plugins/cflow.c 2026-05-25 22:03:52.000000000 +0000 @@ -98,7 +98,7 @@ struct qemu_plugin_scoreboard *state; /* SORT_HOTTEST */ -static gint hottest(gconstpointer a, gconstpointer b) +static gint hottest(gconstpointer a, gconstpointer b, gpointer d) { NodeData *na = (NodeData *) a; NodeData *nb = (NodeData *) b; @@ -107,7 +107,7 @@ na->dest_count == nb->dest_count ? 0 : 1; } -static gint exception(gconstpointer a, gconstpointer b) +static gint exception(gconstpointer a, gconstpointer b, gpointer d) { NodeData *na = (NodeData *) a; NodeData *nb = (NodeData *) b; @@ -116,7 +116,7 @@ na->early_exit == nb->early_exit ? 0 : 1; } -static gint popular(gconstpointer a, gconstpointer b) +static gint popular(gconstpointer a, gconstpointer b, gpointer d) { NodeData *na = (NodeData *) a; NodeData *nb = (NodeData *) b; @@ -138,7 +138,7 @@ { g_autoptr(GString) result = g_string_new("collected "); GList *data; - GCompareFunc sort = &hottest; + GCompareDataFunc sort = &hottest; int i = 0; g_mutex_lock(&node_lock); @@ -162,7 +162,7 @@ break; } - data = g_list_sort(data, sort); + data = g_list_sort_with_data(data, sort, NULL); for (GList *l = data; l != NULL && i < topn; diff -Nru qemu-10.0.8+ds/contrib/plugins/hotblocks.c qemu-10.0.10+ds/contrib/plugins/hotblocks.c --- qemu-10.0.8+ds/contrib/plugins/hotblocks.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/contrib/plugins/hotblocks.c 2026-05-25 22:03:52.000000000 +0000 @@ -39,7 +39,7 @@ unsigned long insns; } ExecCount; -static gint cmp_exec_count(gconstpointer a, gconstpointer b) +static gint cmp_exec_count(gconstpointer a, gconstpointer b, gpointer d) { ExecCount *ea = (ExecCount *) a; ExecCount *eb = (ExecCount *) b; @@ -73,28 +73,28 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) { g_autoptr(GString) report = g_string_new("collected "); - GList *counts, *it; + GList *counts, *sorted_counts, *it; int i; g_string_append_printf(report, "%d entries in the hash table\n", g_hash_table_size(hotblocks)); counts = g_hash_table_get_values(hotblocks); - it = g_list_sort(counts, cmp_exec_count); + sorted_counts = g_list_sort_with_data(counts, cmp_exec_count, NULL); - if (it) { + if (sorted_counts) { g_string_append_printf(report, "pc, tcount, icount, ecount\n"); - for (i = 0; i < limit && it->next; i++, it = it->next) { + for (i = 0, it = sorted_counts; i < limit && it; i++, it = it->next) { ExecCount *rec = (ExecCount *) it->data; g_string_append_printf( - report, "0x%016"PRIx64", %d, %ld, %"PRId64"\n", + report, "0x%016"PRIx64", %d, %ld, %"PRIu64"\n", rec->start_addr, rec->trans_count, rec->insns, qemu_plugin_u64_sum( qemu_plugin_scoreboard_u64(rec->exec_count))); } - g_list_free(it); + g_list_free(sorted_counts); } qemu_plugin_outs(report->str); diff -Nru qemu-10.0.8+ds/contrib/plugins/hotpages.c qemu-10.0.10+ds/contrib/plugins/hotpages.c --- qemu-10.0.8+ds/contrib/plugins/hotpages.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/contrib/plugins/hotpages.c 2026-05-25 22:03:52.000000000 +0000 @@ -48,7 +48,7 @@ static GMutex lock; static GHashTable *pages; -static gint cmp_access_count(gconstpointer a, gconstpointer b) +static gint cmp_access_count(gconstpointer a, gconstpointer b, gpointer d) { PageCounters *ea = (PageCounters *) a; PageCounters *eb = (PageCounters *) b; @@ -83,7 +83,7 @@ if (counts && g_list_next(counts)) { GList *it; - it = g_list_sort(counts, cmp_access_count); + it = g_list_sort_with_data(counts, cmp_access_count, NULL); for (i = 0; i < limit && it->next; i++, it = it->next) { PageCounters *rec = (PageCounters *) it->data; diff -Nru qemu-10.0.8+ds/contrib/plugins/howvec.c qemu-10.0.10+ds/contrib/plugins/howvec.c --- qemu-10.0.8+ds/contrib/plugins/howvec.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/contrib/plugins/howvec.c 2026-05-25 22:03:52.000000000 +0000 @@ -155,7 +155,7 @@ static InsnClassExecCount *class_table; static int class_table_sz; -static gint cmp_exec_count(gconstpointer a, gconstpointer b) +static gint cmp_exec_count(gconstpointer a, gconstpointer b, gpointer d) { InsnExecCount *ea = (InsnExecCount *) a; InsnExecCount *eb = (InsnExecCount *) b; @@ -208,7 +208,7 @@ counts = g_hash_table_get_values(insns); if (counts && g_list_next(counts)) { g_string_append_printf(report, "Individual Instructions:\n"); - counts = g_list_sort(counts, cmp_exec_count); + counts = g_list_sort_with_data(counts, cmp_exec_count, NULL); for (i = 0; i < limit && g_list_next(counts); i++, counts = g_list_next(counts)) { diff -Nru qemu-10.0.8+ds/contrib/plugins/hwprofile.c qemu-10.0.10+ds/contrib/plugins/hwprofile.c --- qemu-10.0.8+ds/contrib/plugins/hwprofile.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/contrib/plugins/hwprofile.c 2026-05-25 22:03:52.000000000 +0000 @@ -71,7 +71,7 @@ devices = g_hash_table_new(NULL, NULL); } -static gint sort_cmp(gconstpointer a, gconstpointer b) +static gint sort_cmp(gconstpointer a, gconstpointer b, gpointer d) { DeviceCounts *ea = (DeviceCounts *) a; DeviceCounts *eb = (DeviceCounts *) b; @@ -79,7 +79,7 @@ eb->totals.reads + eb->totals.writes ? -1 : 1; } -static gint sort_loc(gconstpointer a, gconstpointer b) +static gint sort_loc(gconstpointer a, gconstpointer b, gpointer d) { IOLocationCounts *ea = (IOLocationCounts *) a; IOLocationCounts *eb = (IOLocationCounts *) b; @@ -126,13 +126,13 @@ if (counts && g_list_next(counts)) { GList *it; - it = g_list_sort(counts, sort_cmp); + it = g_list_sort_with_data(counts, sort_cmp, NULL); while (it) { DeviceCounts *rec = (DeviceCounts *) it->data; if (rec->detail) { GList *accesses = g_hash_table_get_values(rec->detail); - GList *io_it = g_list_sort(accesses, sort_loc); + GList *io_it = g_list_sort_with_data(accesses, sort_loc, NULL); const char *prefix = pattern ? "off" : "pc"; g_string_append_printf(report, "%s @ 0x%"PRIx64"\n", rec->name, rec->base); diff -Nru qemu-10.0.8+ds/debian/changelog qemu-10.0.10+ds/debian/changelog --- qemu-10.0.8+ds/debian/changelog 2026-02-28 19:03:46.000000000 +0000 +++ qemu-10.0.10+ds/debian/changelog 2026-06-11 06:28:48.000000000 +0000 @@ -1,3 +1,223 @@ +qemu (1:10.0.10+ds-0+deb13u1) trixie; urgency=medium + + * 10.0.10 upstream stable/bugfix release: + - Update version for 10.0.10 release + - block/graph-lock: fix missed wakeup in bdrv_graph_co_rdunlock() + - block: Add more defaults to DEFAULT_BLOCK_CONF + - block: Create DEFAULT_BLOCK_CONF macro + - ide-test: Test reset during TRIM + - ide-test: Factor out wait_dma_completion() + - ide: Clean up ide_trim_co_entry() to be idiomatic coroutine code + - ide: Minimal fix for deadlock between TRIM and drain + - block: Add flags parameter to blk_*_pdiscard() + - block: Add blk_co_start/end_request() and BDRV_REQ_NO_QUEUE + - blkdebug: Add 'delay-ns' option + - linux-user/sh4: Fix setup_sigtramp to match Linux kernel + trampoline pattern + - linux-user/sh4: Fix target_ucontext tuc_link field type + - linux-user: Fix AT_EXECFN in AUXV for symlinked programs + - hw/nvme: fix admin cq msix setup + - tests/functional/qemu_test/asset.py: Don't use setxattr + when it doesn't exist + - meson.build: Add -fzero-init-padding-bits=all + - hw/i2c/microbit_i2c: Don't index off end of twi_read_sequence[] + - aspeed/hace: Prevent total_req_len overflow + - aspeed/hace: Fix out-of-bounds read in has_padding() + - hw/display/cirrus_vga: Fix packed-24 color-expansion transparent copies + - hw/display/cirrus_vga: Fix packed-24 color-expansion + transparent pattern fills + - hw/ufs: Keep MCQ SQs alive while requests are outstanding + - hw/ufs: Reject zero-depth MCQ queues + - hw/ufs: Guard MCQ CQ accesses against missing queues + - hw/ufs: Validate MCQ SQ references before use + - hw/uefi: check auth.hdr_length minimum size + (Closes: CVE-2026-8341) + - hw/uefi: avoid possibly unaligned variable_auth_2 struct field access + (Closes: CVE-2026-41440) + - hw/uefi: verify data size before accessing it in wrap_pkcs7 + (Closes: CVE-2026-41439) + - hw/uefi: add name_size check to uefi_vars_mm_lock_variable() + (Closes: CVE-2026-41438) + - hw/uefi: fix ucs2 string helper functions + (Closes: CVE-2026-41437) + - hw/uefi: verify pio_xfer_offset before calculating buffer checksum + (Closes: CVE-2026-41436) + - hw/uefi: fix buffer overruns + (Closes: CVE-2026-41435) + - hw/misc/bcm2835_rng: Specify valid memory access sizes + - target/arm: Report IL=0 for Thumb 16-bit BKPT insn + - target/microblaze: Fix endianness used to disassemble + - hw/intc/arm_gicv3: Fix NS write to ICC_AP1Rn_EL1 when prebits < 7 + - hw/net/allwinner-sun8i-emac: Flush queued packets when rx is enabled + - hw/ppc/e500: fix bus-frequency property hardcoded to zero in CPU FDT node + - hw/ppc/e500: Move clock and TB frequency to machine class + - tests/rcutorture: Fix build error + - hw/intc/xics: Add a check for an invalid server id + - linux-user: Translate errno in IP_RECVERR and IPV6_RECVERR + - linux-user: Allow getsockopt() with NULL optval address + - linux-user: Flush errors by using exit() instead of _exit() in error path + - linux-user: Add missing CDROM ioctls + - target/riscv: Use ELEN for Fractional LMUL check + - target/riscv: Don't OR mip.SEIP when mvien is one + - target/riscv: Generate access fault if sc comparison fails + - riscv_htif: reject invalid signature ranges (end <= begin) + - hw/intc: fix heap OOB in ACLINT MTIMER multi-socket + - target/riscv: fix stale ptshift and base on page walk restart + - hw/riscv/virt-acpi-build.c: Use kvm timer frequency when kvm enabled + - linux-user: Flush errors by using exit() instead of _exit() in error path + - linux-user: Use abi_int for imr_ifindex in ip_mreqn struct + - linux-user: Fix CLONE_PARENT_SETTID when using fork-like clone + - linux-user: Add getsockopt() for SO_RCVTIMEO_NEW and SO_SNDTIMEO_NEW + - linux-user: Add setsockopt() for SO_RCVTIMEO_NEW and SO_SNDTIMEO_NEW + - linux-user: Define SO_TIMESTAMP*_NEW and SO_RCVTIMEIO_NEW + - linux-user/mips: sync k0 TLS for EF_MIPS_MACH_OCTEON userlands + - linux-user/strace: Use pointer type for read and write values + - linux-user/arm/nwfpe: Use thread-local storage for qemufpa + - linux-user/arm/nwfpe: Replace user_registers with current_cpu + - linux-user: Don't define target_stat64 struct for loongarch64 + - linux-user: fix off-by-one in host_to_target_for_each_rtattr() + - linux-user/ppc: Fix ppc64 rt_sigframe stack offset + - hw/sh4/sh7750: Remove forgotten abort() in the MM_ITLB_DATA handler + - hw/misc: Fix the valid access size to the avr-power device + - migration: vmstate_save_state_v: fix double error_setg + - hw/display: don't accidentally autofree existing virgl resources + (Closes: CVE-2026-6502) + - meson: add missing semicolon in pthread_condattr_setclock test + - target/i386/tcg: fix decoding of MOVBE and CRC32 in 16-bit mode + - target/i386: fix missing PF_INSTR in SIGSEGV context + - target/i386: fix strList leak in x86_cpu_get_unavailable_features + - target/arm/tcg/translate.c: remove MO_TE usage + - ui/console-vc: fix off-by-one in CSI J 2 (clear entire screen) + - ui/spice-app: detect runtime directory creation failures + - serial COM: windows serial COM PollingFunc don't sleep + - util/cutils: Fix heap corruption under Windows + - virtio-blk: fix zone report buffer out-of-memory + (Closes: CVE-2026-5761) + - qemu-keymap: fix altgr modifier lookup for newer xkeyboard-config + - hw/uefi: fix heap overflow + (Closes: CVE-2026-5744) + - virtio-scsi: pass the same cdb_size to virtio_scsi_pop_req + and virtio_scsi_handle_cmd_req_prepare + (Closes: CVE-2026-5763) + - util/readline: Fix out-of-bounds access in readline_insert_char() + - target/arm: fix fault_s1ns for stage 2 faults + - target/arm: do_ats_write(): avoid assertion when ptw failed + - bsd-user, linux-user: signal: recursive signal delivery fix + - linux-user: Make openat2() use -L for absolute paths + - linux-user: update select timeout writeback + - linux-user: fix name_to_handle_at when AT_HANDLE_MNT_ID_UNIQUE flag is set + - util: fix missing aio_wait sym in qemu guest agent only build + - monitor: Fix deadlock in monitor_cleanup + - scsi: Don't consider LOGICAL UNIT NOT SUPPORTED guest recoverable + - ide: Fix potential assertion failure on VM stop for PIO read error + - ui/vnc-jobs: fix VncRectEntry leak on job cleanup + - hw/net/rocker: Avoid double-free of l2_flood.group_ids + - lsi53c895a: keep SCSIRequest alive during DMA + - lsi53c895a: keep lsi_request alive as long as the SCSIRequest + - lsi53c895a: keep lsi_request and SCSIRequest in local variables + - lsi53c895a: do not do anything else if a reset is requested + by writing ISTAT0 + - lsi53c895a: keep a reference to the device while SCRIPTS execute + (Closes: #1085299, CVE-2024-6519) + - scripts/qemu-guest-agent/fsfreeze-hook: Fix syslog-fallback logic + - scripts/qemu-guest-agent/fsfreeze-hook: Avoid use of PIPESTATUS + - scripts/qemu-guest-agent/fsfreeze-hook: Avoid bash-isms + - hw/nvme: fix heap-buffer-overflow in nvme_abort + - hw/nvme: re-enable wzds bit in namespace dlfeat + - tcg: Pass host-endian values to plugin_gen_mem_callbacks_* + - hw/audio/sb16: validate VMState fields in post_load + - block/curl: free s->password in cleanup paths + - linux-aio: Resubmit tails of short reads/writes + - linux-aio: Put all parameters into qemu_laiocb + - hw/dma/pl080: Fix transfer logic in PL080 + - linux-user/i386/signal.c: Correct definition of target_fpstate_32 + - hw/ssi/aspeed_smc: Convert mem ops to read/write_with_attrs + for error handling + - hw/net/ftgmac100: Improve DMA error handling + - hw/usb/hcd-ohci: check for MPS=0 to avoid infinite loop + (Closes: CVE-2026-3890) + - rust: suggest passing --locked to "cargo install" + - target/riscv: rvv: Fix page probe issues in vext_ldff + - target/riscv: rvv: Fix missing flags merge in probe_pages + for cross-page accesses + - Expand the probe_pages helper function to handle probe flags + - block: Drop detach_subchain for bdrv_replace_node + - virtio-gpu: fix overflow check when allocating 2d image + (Closes: CVE-2026-3886) + - io: Fix TLS bye task leak + - ppc/pnv: generate dtb after machine initialization is complete + - ppc/pnv: fix dumpdtb option + - block/mirror: fix assertion failure upon duplicate complete + for job using 'replaces' + - throttle-group: Fix race condition in throttle_group_restart_queue() + - target/i386: fix NULL pointer dereference in legacy-cache=off handling + - hw/dma/pl080: Ignore bottom 2 bits of LLI register + - hw/dma/pl080: Update interrupts after pl080_run() + - hw/dma/pl080: Handle bogus swidth and dwidth in transfers + - linux-user: fix mremap with old_size=0 for shared mappings + - linux-user: Fix zero_bss for RX PT_LOAD segments + - hw/net/rtl8319: Work around GCC sanitizer / -Wstringop-overflow bug + + * 10.0.9 stable/bugfix release: + - Update version for 10.0.9 release + - hyperv/syndbg: check length returned by cpu_physical_memory_map() + (Closes: CVE-2026-3842) + - fuse: Copy write buffer content before polling + - target/loongarch: Avoid recursive PNX exception on CSR_BADI fetch + - target/loongarch: Preserve PTE permission bits in LDPTE + - hw/net/npcm_gmac: Catch accesses off the end of the register array + - linux-user: fix TIOCGSID ioctl + - tests/tcg/multiarch/test-mmap: Check mmaps beyond reserved_va + - bsd-user: Deal with mmap where start > reserved_va + - linux-user: Deal with mmap where start > reserved_va + - hw/net/xilinx_ethlite: Check for oversized TX packets + - virtio-gpu: Ensure BHs are invoked only from main-loop thread + - block/nfs: Do not enter coroutine from CB + - block: Never drop BLOCK_IO_ERROR with action=stop for rate limiting + - block/throttle-groups: fix deadlock with iolimits and muliple iothreads + - mirror: Fix missed dirty bitmap writes during startup + (Closes: #1129349) + - block/curl: fix concurrent completion handling + - block/vmdk: fix OOB read in vmdk_read_extent() + (Closes: #1128478, CVE-2026-2243) + - hw/net/smc91c111: Don't allow negative-length packets + - io: fix cleanup for websock I/O source data on cancellation + - io: fix cleanup for TLS I/O source data on cancellation + - io: separate freeing of tasks from marking them as complete + - target/i386/hvf/x86_mmu: Fix compiler warning + - hw/i386/vmmouse: Fix hypercall clobbers + - tests/docker: upgrade most non-lcitool debian tests to debian 13 + - hw/9pfs: fix missing EOPNOTSUPP on Twstat and Trenameat + for fs synth driver + - hw/9pfs: fix data race in v9fs_mark_fids_unreclaim() + - target/arm: set the correct TI bits for WFIT traps + - hw/ssi/xilinx_spips: Reset TX FIFO in reset + - hw/misc/virt_ctrl: Fix incorrect trace event in read operation + - virtio-snd: tighten read amount in in_cb + (Closes: #1129604, CVE-2026-3195) + - virtio-snd: fix max_size bounds check in input cb + (Closes: #1129604, CVE-2026-3195) + - virtio-snd: handle 5.14.6.2 for PCM_INFO properly + (Closes: #1129605, CVE-2026-3196) + - virtio-snd: remove TODO comments + - virtio-gpu-virgl: Add virtio-gpu-virgl-hostmem-region type + (was in virtio-gpu-virgl-Add-virtio-gpu-virgl-hostmem-region.patch) + - target/arm: Fix feature check in DO_SVE2_RRX, DO_SVE2_RRX_TB + - target/arm: Account for SME in aarch64_sve_narrow_vq() assertion + - target/arm: Introduce ARMCPU.sme_max_vq + - hw/i2c/aspeed_i2c: Fix out-of-bounds read in I2C MMIO handlers + - docs/about/emulation: Add documentation for hotblocks plugin arguments + - contrib/plugins/hotblocks: Print uint64_t with PRIu64 rather than PRId64 + - contrib/plugins/hotblocks: Fix off by one error in iteration + of sorted blocks + - contrib/plugins/hotblocks: Correctly free sorted counts list + - contrib/plugins: Fix type conflict of GLib function pointers + - python: drop uses of pkg_resources + - plugins: fix cross-build using LLVM for Windows targets + - s390x/pci: Fix endianness for zPCI BAR values + + -- Michael Tokarev Thu, 11 Jun 2026 09:28:48 +0300 + qemu (1:10.0.8+ds-0+deb13u1) trixie; urgency=medium * 10.0.8 upstream stable/bugfix release: diff -Nru qemu-10.0.8+ds/debian/control.mk qemu-10.0.10+ds/debian/control.mk --- qemu-10.0.8+ds/debian/control.mk 2026-02-28 17:43:45.000000000 +0000 +++ qemu-10.0.10+ds/debian/control.mk 2026-06-11 06:26:56.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.8+ds +checked-version := 10.0.10+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.8+ds/debian/patches/series qemu-10.0.10+ds/debian/patches/series --- qemu-10.0.8+ds/debian/patches/series 2026-02-28 17:31:14.000000000 +0000 +++ qemu-10.0.10+ds/debian/patches/series 2026-06-11 06:26:35.000000000 +0000 @@ -15,4 +15,3 @@ slof-ensure-ld-is-called-with-C-locale.patch qemu-img-options.patch disable-pycotap.patch -virtio-gpu-virgl-Add-virtio-gpu-virgl-hostmem-region.patch diff -Nru qemu-10.0.8+ds/debian/patches/virtio-gpu-virgl-Add-virtio-gpu-virgl-hostmem-region.patch qemu-10.0.10+ds/debian/patches/virtio-gpu-virgl-Add-virtio-gpu-virgl-hostmem-region.patch --- qemu-10.0.8+ds/debian/patches/virtio-gpu-virgl-Add-virtio-gpu-virgl-hostmem-region.patch 2026-02-28 17:31:14.000000000 +0000 +++ qemu-10.0.10+ds/debian/patches/virtio-gpu-virgl-Add-virtio-gpu-virgl-hostmem-region.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,179 +0,0 @@ -From: Akihiko Odaki -Date: Sat, 14 Feb 2026 13:33:36 +0900 -Subject: virtio-gpu-virgl: Add virtio-gpu-virgl-hostmem-region type -Origin: upstream, https://gitlab.com/qemu-project/qemu/-/commit/b2a279094c3b86667969cc645f7fb1087e08dd19 -Forwarded: not-needed - -Commit e27194e087ae ("virtio-gpu-virgl: correct parent for blob memory -region") made the name member of MemoryRegion unset, causing a NULL -pointer dereference[1]: -> Thread 2 "qemu-system-x86" received signal SIGSEGV, Segmentation fault. -> (gdb) bt -> #0 0x00007ffff56565e2 in __strcmp_evex () at /lib64/libc.so.6 -> #1 0x0000555555841bdb in find_fd (head=0x5555572337d0 , -> name=0x0, id=0) at ../migration/cpr.c:68 -> #2 cpr_delete_fd (name=name@entry=0x0, id=id@entry=0) at -> ../migration/cpr.c:77 -> #3 0x000055555582290a in qemu_ram_free (block=0x7ff7e93aa7f0) at -> ../system/physmem.c:2615 -> #4 0x000055555581ae02 in memory_region_finalize (obj=) -> at ../system/memory.c:1816 -> #5 0x0000555555a70ab9 in object_deinit (obj=, -> type=) at ../qom/object.c:715 -> #6 object_finalize (data=0x7ff7e936eff0) at ../qom/object.c:729 -> #7 object_unref (objptr=0x7ff7e936eff0) at ../qom/object.c:1232 -> #8 0x0000555555814fae in memory_region_unref (mr=) at -> ../system/memory.c:1848 -> #9 flatview_destroy (view=0x555559ed6c40) at ../system/memory.c:301 -> #10 0x0000555555bfc122 in call_rcu_thread (opaque=) at -> ../util/rcu.c:324 -> #11 0x0000555555bf17a7 in qemu_thread_start (args=0x555557b99520) at -> ../util/qemu-thread-posix.c:393 -> #12 0x00007ffff556f464 in start_thread () at /lib64/libc.so.6 -> #13 0x00007ffff55f25ac in __clone3 () at /lib64/libc.so.6 - -The intention of the aforementioned commit is to prevent a MemoryRegion -from parenting itself while its references is counted indendependently -of the device. To achieve the same goal, add a type of QOM objects that -count references and parent MemoryRegions. - -[1] https://lore.kernel.org/qemu-devel/4eb93d7a-1fa9-4b3c-8ad7-a2eb64f025a0@collabora.com/ - -Cc: qemu-stable@nongnu.org -Fixes: e27194e087ae ("virtio-gpu-virgl: correct parent for blob memory region") -Fixes: 8d5a8ebaaff2 ("virtio-gpu-virgl: correct parent for blob memory region") in 10.0.x -Signed-off-by: Akihiko Odaki -Tested-by: Dmitry Osipenko -Tested-by: Joelle van Dyne -Reviewed-by: Michael S. Tsirkin -Signed-off-by: Michael S. Tsirkin -Message-Id: <20260214-region-v1-1-229f00ae1f38@rsg.ci.i.u-tokyo.ac.jp> -(cherry picked from commit b2a279094c3b86667969cc645f7fb1087e08dd19) -Signed-off-by: Michael Tokarev ---- - hw/display/virtio-gpu-virgl.c | 54 +++++++++++++++++++++++++---------- - 1 file changed, 39 insertions(+), 15 deletions(-) - -diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c -index b25ddc0746..362828f54e 100644 ---- a/hw/display/virtio-gpu-virgl.c -+++ b/hw/display/virtio-gpu-virgl.c -@@ -52,11 +52,17 @@ virgl_get_egl_display(G_GNUC_UNUSED void *cookie) - - #if VIRGL_VERSION_MAJOR >= 1 - struct virtio_gpu_virgl_hostmem_region { -+ Object parent_obj; - MemoryRegion mr; - struct VirtIOGPU *g; - bool finish_unmapping; - }; - -+#define TYPE_VIRTIO_GPU_VIRGL_HOSTMEM_REGION "virtio-gpu-virgl-hostmem-region" -+ -+OBJECT_DECLARE_SIMPLE_TYPE(virtio_gpu_virgl_hostmem_region, -+ VIRTIO_GPU_VIRGL_HOSTMEM_REGION) -+ - static struct virtio_gpu_virgl_hostmem_region * - to_hostmem_region(MemoryRegion *mr) - { -@@ -70,14 +76,22 @@ static void virtio_gpu_virgl_resume_cmdq_bh(void *opaque) - virtio_gpu_process_cmdq(g); - } - --static void virtio_gpu_virgl_hostmem_region_free(void *obj) -+/* -+ * MR could outlive the resource if MR's reference is held outside of -+ * virtio-gpu. In order to prevent unmapping resource while MR is alive, -+ * and thus, making the data pointer invalid, we will block virtio-gpu -+ * command processing until MR is fully unreferenced and freed. -+ */ -+static void virtio_gpu_virgl_hostmem_region_finalize(Object *obj) - { -- MemoryRegion *mr = MEMORY_REGION(obj); -- struct virtio_gpu_virgl_hostmem_region *vmr; -+ struct virtio_gpu_virgl_hostmem_region *vmr = VIRTIO_GPU_VIRGL_HOSTMEM_REGION(obj); - VirtIOGPUBase *b; - VirtIOGPUGL *gl; - -- vmr = to_hostmem_region(mr); -+ if (!vmr->g) { -+ return; -+ } -+ - vmr->finish_unmapping = true; - - b = VIRTIO_GPU_BASE(vmr->g); -@@ -92,11 +106,26 @@ static void virtio_gpu_virgl_hostmem_region_free(void *obj) - qemu_bh_schedule(gl->cmdq_resume_bh); - } - -+static const TypeInfo virtio_gpu_virgl_hostmem_region_info = { -+ .parent = TYPE_OBJECT, -+ .name = TYPE_VIRTIO_GPU_VIRGL_HOSTMEM_REGION, -+ .instance_size = sizeof(struct virtio_gpu_virgl_hostmem_region), -+ .instance_finalize = virtio_gpu_virgl_hostmem_region_finalize -+}; -+ -+static void virtio_gpu_virgl_types(void) -+{ -+ type_register_static(&virtio_gpu_virgl_hostmem_region_info); -+} -+ -+type_init(virtio_gpu_virgl_types) -+ - static int - virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g, - struct virtio_gpu_virgl_resource *res, - uint64_t offset) - { -+ g_autofree char *name = NULL; - struct virtio_gpu_virgl_hostmem_region *vmr; - VirtIOGPUBase *b = VIRTIO_GPU_BASE(g); - MemoryRegion *mr; -@@ -117,21 +146,16 @@ virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g, - } - - vmr = g_new0(struct virtio_gpu_virgl_hostmem_region, 1); -+ name = g_strdup_printf("blob[%" PRIu32 "]", res->base.resource_id); -+ object_initialize_child(OBJECT(g), name, vmr, -+ TYPE_VIRTIO_GPU_VIRGL_HOSTMEM_REGION); - vmr->g = g; - - mr = &vmr->mr; -- memory_region_init_ram_ptr(mr, OBJECT(mr), NULL, size, data); -+ memory_region_init_ram_ptr(mr, OBJECT(vmr), "mr", size, data); - memory_region_add_subregion(&b->hostmem, offset, mr); - memory_region_set_enabled(mr, true); - -- /* -- * MR could outlive the resource if MR's reference is held outside of -- * virtio-gpu. In order to prevent unmapping resource while MR is alive, -- * and thus, making the data pointer invalid, we will block virtio-gpu -- * command processing until MR is fully unreferenced and freed. -- */ -- OBJECT(mr)->free = virtio_gpu_virgl_hostmem_region_free; -- - res->mr = mr; - - return 0; -@@ -159,7 +183,7 @@ virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g, - * 1. Begin async unmapping with memory_region_del_subregion() - * and suspend/block cmd processing. - * 2. Wait for res->mr to be freed and cmd processing resumed -- * asynchronously by virtio_gpu_virgl_hostmem_region_free(). -+ * asynchronously by virtio_gpu_virgl_hostmem_region_finalize(). - * 3. Finish the unmapping with final virgl_renderer_resource_unmap(). - */ - if (vmr->finish_unmapping) { -@@ -182,7 +206,7 @@ virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g, - /* memory region owns self res->mr object and frees it by itself */ - memory_region_set_enabled(mr, false); - memory_region_del_subregion(&b->hostmem, mr); -- object_unref(OBJECT(mr)); -+ object_unparent(OBJECT(vmr)); - } - - return 0; --- -2.47.3 - diff -Nru qemu-10.0.8+ds/docs/about/build-platforms.rst qemu-10.0.10+ds/docs/about/build-platforms.rst --- qemu-10.0.8+ds/docs/about/build-platforms.rst 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/docs/about/build-platforms.rst 2026-05-25 22:03:52.000000000 +0000 @@ -116,7 +116,7 @@ bindgen tool, which is too big to package and distribute. The minimum supported version of bindgen is 0.60.x. For distributions that do not include bindgen or have an older version, it is recommended to install - a newer version using ``cargo install bindgen-cli``. + a newer version using ``cargo install --locked bindgen-cli``. Developers may want to use Cargo-based tools in the QEMU source tree; this requires Cargo 1.74.0. Note that Cargo is not required in order diff -Nru qemu-10.0.8+ds/docs/about/emulation.rst qemu-10.0.10+ds/docs/about/emulation.rst --- qemu-10.0.8+ds/docs/about/emulation.rst 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/docs/about/emulation.rst 2026-05-25 22:03:52.000000000 +0000 @@ -463,6 +463,16 @@ 0x000000004002b0, 1, 4, 66087 ... +Behaviour can be tweaked with the following arguments: + +.. list-table:: Hot Blocks plugin arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - inline=true|false + - Use faster inline addition of a single counter. Hot Pages ......... diff -Nru qemu-10.0.8+ds/hw/9pfs/9p.c qemu-10.0.10+ds/hw/9pfs/9p.c --- qemu-10.0.8+ds/hw/9pfs/9p.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/9pfs/9p.c 2026-05-25 22:03:52.000000000 +0000 @@ -551,6 +551,7 @@ sizeof(V9fsFidState *), 1); gint i; + v9fs_path_read_lock(s); g_hash_table_iter_init(&iter, s->fids); /* @@ -571,6 +572,7 @@ g_array_append_val(to_reopen, fidp); } } + v9fs_path_unlock(s); for (i = 0; i < to_reopen->len; i++) { fidp = g_array_index(to_reopen, V9fsFidState*, i); @@ -3494,6 +3496,12 @@ goto out_err; } + /* if fs driver is not path based, return EOPNOTSUPP */ + if (!(s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) { + err = -EOPNOTSUPP; + goto out_err; + } + v9fs_path_write_lock(s); err = v9fs_complete_renameat(pdu, olddirfid, &old_name, newdirfid, &new_name); @@ -3584,6 +3592,11 @@ } } if (v9stat.name.size != 0) { + /* if fs driver is not path based, return EOPNOTSUPP */ + if (!(s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) { + err = -EOPNOTSUPP; + goto out; + } v9fs_path_write_lock(s); err = v9fs_complete_rename(pdu, fidp, -1, &v9stat.name); v9fs_path_unlock(s); diff -Nru qemu-10.0.8+ds/hw/audio/sb16.c qemu-10.0.10+ds/hw/audio/sb16.c --- qemu-10.0.8+ds/hw/audio/sb16.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/audio/sb16.c 2026-05-25 22:03:52.000000000 +0000 @@ -1287,6 +1287,13 @@ { SB16State *s = opaque; + + if (s->in_index < 0 || s->in_index > (int)sizeof(s->in2_data)) { + return -1; + } + if (s->out_data_len < 0 || s->out_data_len > (int)sizeof(s->out_data)) { + return -1; + } if (s->voice) { AUD_close_out (&s->card, s->voice); s->voice = NULL; diff -Nru qemu-10.0.8+ds/hw/audio/virtio-snd.c qemu-10.0.10+ds/hw/audio/virtio-snd.c --- qemu-10.0.8+ds/hw/audio/virtio-snd.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/audio/virtio-snd.c 2026-05-25 22:03:52.000000000 +0000 @@ -156,7 +156,7 @@ static void virtio_snd_handle_pcm_info(VirtIOSound *s, virtio_snd_ctrl_command *cmd) { - uint32_t stream_id, start_id, count, size; + uint32_t stream_id, start_id, count, size, tmp; virtio_snd_pcm_info val; virtio_snd_query_info req; VirtIOSoundPCMStream *stream = NULL; @@ -168,9 +168,6 @@ sizeof(virtio_snd_query_info)); if (msg_sz != sizeof(virtio_snd_query_info)) { - /* - * TODO: do we need to set DEVICE_NEEDS_RESET? - */ qemu_log_mask(LOG_GUEST_ERROR, "%s: virtio-snd command size incorrect %zu vs \ %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info)); @@ -182,14 +179,34 @@ count = le32_to_cpu(req.count); size = le32_to_cpu(req.size); - if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) < - sizeof(virtio_snd_hdr) + size * count) { - /* - * TODO: do we need to set DEVICE_NEEDS_RESET? - */ + /* + * 5.14.6.2 Driver Requirements: Item Information Request + * "The driver MUST NOT set start_id and count such that start_id + count + * is greater than the total number of particular items that is indicated + * in the device configuration space." + */ + if (start_id > s->snd_conf.streams + || !g_uint_checked_add(&tmp, start_id, count) + || start_id + count > s->snd_conf.streams) { + error_report("pcm info: start_id + count is greater than the total " + "number of streams, got: start_id = %u, count = %u", + start_id, count); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + + /* + * 5.14.6.2 Driver Requirements: Item Information Request + * "The driver MUST provide a buffer of sizeof(struct virtio_snd_hdr) + + * count * size bytes for the response." + */ + if (!g_uint_checked_mul(&tmp, size, count) + || !g_uint_checked_add(&tmp, tmp, sizeof(virtio_snd_hdr)) + || iov_size(cmd->elem->in_sg, cmd->elem->in_num) < + sizeof(virtio_snd_hdr) + size * count) { error_report("pcm info: buffer too small, got: %zu, needed: %zu", iov_size(cmd->elem->in_sg, cmd->elem->in_num), - sizeof(virtio_snd_pcm_info)); + sizeof(virtio_snd_pcm_info) * count); cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); return; } @@ -244,9 +261,6 @@ virtio_snd_pcm_set_params *st_params; if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) { - /* - * TODO: do we need to set DEVICE_NEEDS_RESET? - */ virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n"); return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); } @@ -297,9 +311,6 @@ sizeof(virtio_snd_pcm_set_params)); if (msg_sz != sizeof(virtio_snd_pcm_set_params)) { - /* - * TODO: do we need to set DEVICE_NEEDS_RESET? - */ qemu_log_mask(LOG_GUEST_ERROR, "%s: virtio-snd command size incorrect %zu vs \ %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_set_params)); @@ -609,9 +620,6 @@ sizeof(stream_id)); if (msg_sz != sizeof(stream_id)) { - /* - * TODO: do we need to set DEVICE_NEEDS_RESET? - */ qemu_log_mask(LOG_GUEST_ERROR, "%s: virtio-snd command size incorrect %zu vs \ %zu\n", __func__, msg_sz, sizeof(stream_id)); @@ -623,9 +631,6 @@ trace_virtio_snd_handle_pcm_release(stream_id); stream = virtio_snd_pcm_get_stream(s, stream_id); if (stream == NULL) { - /* - * TODO: do we need to set DEVICE_NEEDS_RESET? - */ error_report("already released stream %"PRIu32, stream_id); virtio_error(VIRTIO_DEVICE(s), "already released stream %"PRIu32, @@ -668,9 +673,6 @@ sizeof(virtio_snd_hdr)); if (msg_sz != sizeof(virtio_snd_hdr)) { - /* - * TODO: do we need to set DEVICE_NEEDS_RESET? - */ qemu_log_mask(LOG_GUEST_ERROR, "%s: virtio-snd command size incorrect %zu vs \ %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr)); @@ -1238,7 +1240,7 @@ { VirtIOSoundPCMStream *stream = data; VirtIOSoundPCMBuffer *buffer; - size_t size, max_size; + size_t size, max_size, to_read; WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { while (!QSIMPLEQ_EMPTY(&stream->queue)) { @@ -1253,15 +1255,23 @@ } max_size = iov_size(buffer->elem->in_sg, buffer->elem->in_num); + if (max_size <= sizeof(virtio_snd_pcm_status)) { + return_rx_buffer(stream, buffer); + continue; + } + max_size -= sizeof(virtio_snd_pcm_status); + for (;;) { if (buffer->size >= max_size) { return_rx_buffer(stream, buffer); break; } + to_read = stream->params.period_bytes - buffer->size; + to_read = MIN(to_read, available); + to_read = MIN(to_read, max_size - buffer->size); size = AUD_read(stream->voice.in, - buffer->data + buffer->size, - MIN(available, (stream->params.period_bytes - - buffer->size))); + buffer->data + buffer->size, + to_read); if (!size) { available = 0; break; diff -Nru qemu-10.0.8+ds/hw/block/virtio-blk.c qemu-10.0.10+ds/hw/block/virtio-blk.c --- qemu-10.0.8+ds/hw/block/virtio-blk.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/block/virtio-blk.c 2026-05-25 22:03:52.000000000 +0000 @@ -38,6 +38,9 @@ #include "hw/virtio/virtio-blk-common.h" #include "qemu/coroutine.h" +/* Internal buffer size limit for zone report */ +#define VIRTIO_BLK_MAX_ZONES_PER_BATCH 4096 + static void virtio_blk_ioeventfd_attach(VirtIOBlock *s); static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq, @@ -451,15 +454,22 @@ return err_status; } +typedef struct { + unsigned int total_nr_zones; /* max zones to fill in this request */ + unsigned int nr_zones_done; /* how many zones have been filled in */ + int64_t iov_offset; /* current byte position in in_iov[] */ + int64_t offset; /* current zone report disk offset */ + unsigned int nr_zones; /* for zone report calls */ + unsigned int zones_per_batch; /* size of zone report buffer */ + BlockZoneDescriptor *zones; /* zone report buffer */ +} ZoneReportData; + typedef struct ZoneCmdData { VirtIOBlockReq *req; struct iovec *in_iov; unsigned in_num; union { - struct { - unsigned int nr_zones; - BlockZoneDescriptor *zones; - } zone_report_data; + ZoneReportData zone_report_data; struct { int64_t offset; } zone_append_data; @@ -516,16 +526,15 @@ static void virtio_blk_zone_report_complete(void *opaque, int ret) { ZoneCmdData *data = opaque; + ZoneReportData *zrd = &data->zone_report_data; VirtIOBlockReq *req = data->req; VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); struct iovec *in_iov = data->in_iov; unsigned in_num = data->in_num; - int64_t zrp_size, n, j = 0; - int64_t nz = data->zone_report_data.nr_zones; + int64_t n; + unsigned nz = zrd->nr_zones; int8_t err_status = VIRTIO_BLK_S_OK; - struct virtio_blk_zone_report zrp_hdr = (struct virtio_blk_zone_report) { - .nr_zones = cpu_to_le64(nz), - }; + struct virtio_blk_zone_report zrp_hdr = {}; trace_virtio_blk_zone_report_complete(vdev, req, nz, ret); if (ret) { @@ -533,28 +542,18 @@ goto out; } - zrp_size = sizeof(struct virtio_blk_zone_report) - + sizeof(struct virtio_blk_zone_descriptor) * nz; - n = iov_from_buf(in_iov, in_num, 0, &zrp_hdr, sizeof(zrp_hdr)); - if (n != sizeof(zrp_hdr)) { - virtio_error(vdev, "Driver provided input buffer that is too small!"); - err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; - goto out; - } - - for (size_t i = sizeof(zrp_hdr); i < zrp_size; - i += sizeof(struct virtio_blk_zone_descriptor), ++j) { + for (unsigned j = 0; j < nz; j++) { struct virtio_blk_zone_descriptor desc = (struct virtio_blk_zone_descriptor) { - .z_start = cpu_to_le64(data->zone_report_data.zones[j].start + .z_start = cpu_to_le64(zrd->zones[j].start >> BDRV_SECTOR_BITS), - .z_cap = cpu_to_le64(data->zone_report_data.zones[j].cap + .z_cap = cpu_to_le64(zrd->zones[j].cap >> BDRV_SECTOR_BITS), - .z_wp = cpu_to_le64(data->zone_report_data.zones[j].wp + .z_wp = cpu_to_le64(zrd->zones[j].wp >> BDRV_SECTOR_BITS), }; - switch (data->zone_report_data.zones[j].type) { + switch (zrd->zones[j].type) { case BLK_ZT_CONV: desc.z_type = VIRTIO_BLK_ZT_CONV; break; @@ -568,7 +567,7 @@ g_assert_not_reached(); } - switch (data->zone_report_data.zones[j].state) { + switch (zrd->zones[j].state) { case BLK_ZS_RDONLY: desc.z_state = VIRTIO_BLK_ZS_RDONLY; break; @@ -598,18 +597,47 @@ } /* TODO: it takes O(n^2) time complexity. Optimizations required. */ - n = iov_from_buf(in_iov, in_num, i, &desc, sizeof(desc)); + n = iov_from_buf(in_iov, in_num, zrd->iov_offset, &desc, sizeof(desc)); if (n != sizeof(desc)) { virtio_error(vdev, "Driver provided input buffer " "for descriptors that is too small!"); err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + goto out; } + + zrd->iov_offset += sizeof(desc); + } + + if (nz > 0) { + BlockZoneDescriptor *zone = &zrd->zones[nz - 1]; + zrd->offset = zone->start + zone->length; + } + + zrd->nr_zones_done += nz; + + /* Call zone report again if the end hasn't been reached yet */ + if (nz == zrd->zones_per_batch && + zrd->nr_zones_done < zrd->total_nr_zones) { + zrd->nr_zones = MIN(zrd->zones_per_batch, + zrd->total_nr_zones - zrd->nr_zones_done); + blk_aio_zone_report(req->dev->blk, zrd->offset, &zrd->nr_zones, + zrd->zones, virtio_blk_zone_report_complete, data); + return; + } + + /* Fill in header now that all zones have been reported */ + zrp_hdr.nr_zones = cpu_to_le64(zrd->nr_zones_done); + n = iov_from_buf(in_iov, in_num, 0, &zrp_hdr, sizeof(zrp_hdr)); + if (n != sizeof(zrp_hdr)) { + virtio_error(vdev, "Driver provided input buffer that is too small!"); + err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + goto out; } out: virtio_blk_req_complete(req, err_status); g_free(req); - g_free(data->zone_report_data.zones); + g_free(zrd->zones); g_free(data); } @@ -621,7 +649,8 @@ VirtIODevice *vdev = VIRTIO_DEVICE(s); unsigned int nr_zones; ZoneCmdData *data; - int64_t zone_size, offset; + ZoneReportData *zrd; + int64_t offset; uint8_t err_status; if (req->in_len < sizeof(struct virtio_blk_inhdr) + @@ -643,16 +672,21 @@ trace_virtio_blk_handle_zone_report(vdev, req, offset >> BDRV_SECTOR_BITS, nr_zones); - zone_size = sizeof(BlockZoneDescriptor) * nr_zones; data = g_malloc(sizeof(ZoneCmdData)); data->req = req; data->in_iov = in_iov; data->in_num = in_num; - data->zone_report_data.nr_zones = nr_zones; - data->zone_report_data.zones = g_malloc(zone_size), - blk_aio_zone_report(s->blk, offset, &data->zone_report_data.nr_zones, - data->zone_report_data.zones, + zrd = &data->zone_report_data; + zrd->total_nr_zones = nr_zones; + zrd->nr_zones_done = 0; + zrd->iov_offset = sizeof(struct virtio_blk_zone_report); + zrd->offset = offset; + zrd->zones_per_batch = MIN(nr_zones, VIRTIO_BLK_MAX_ZONES_PER_BATCH); + zrd->zones = g_malloc(zrd->zones_per_batch * sizeof(BlockZoneDescriptor)); + + zrd->nr_zones = zrd->zones_per_batch; + blk_aio_zone_report(s->blk, offset, &zrd->nr_zones, zrd->zones, virtio_blk_zone_report_complete, data); return; out: diff -Nru qemu-10.0.8+ds/hw/char/riscv_htif.c qemu-10.0.10+ds/hw/char/riscv_htif.c --- qemu-10.0.8+ds/hw/char/riscv_htif.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/char/riscv_htif.c 2026-05-25 22:03:52.000000000 +0000 @@ -171,6 +171,12 @@ * begin/end_signature symbols exist. */ if (sig_file && begin_sig_addr && end_sig_addr) { + if (end_sig_addr <= begin_sig_addr) { + error_report("Invalid HTIF signature range:" + " begin=0x%" PRIx64 " end=0x%" PRIx64, + begin_sig_addr, end_sig_addr); + return; + } uint64_t sig_len = end_sig_addr - begin_sig_addr; char *sig_data = g_malloc(sig_len); dma_memory_read(&address_space_memory, begin_sig_addr, diff -Nru qemu-10.0.8+ds/hw/display/cirrus_vga_rop2.h qemu-10.0.10+ds/hw/display/cirrus_vga_rop2.h --- qemu-10.0.8+ds/hw/display/cirrus_vga_rop2.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/display/cirrus_vga_rop2.h 2026-05-25 22:03:52.000000000 +0000 @@ -108,12 +108,34 @@ unsigned int col; unsigned bitmask; unsigned index; + + /* + * Raster ops where the source is a monochrome bitmap with + * color expansion to 8/16/24/32bpp destination. + */ + #if DEPTH == 24 + /* + * For packed-24 modes, GR2F bits [4:0] are a count of destination + * bytes to be suppressed for each scanline, which we keep in + * dstskipleft. We want to track the number of whole bytes + * to skip in the source (always either 0 or 1) and the number + * of bits within the byte to skip. + */ int dstskipleft = s->vga.gr[0x2f] & 0x1f; - int srcskipleft = dstskipleft / 3; + int srcskipleftbits = (dstskipleft / 3) & 0x7; + int srcskipleftbytes = (dstskipleft / 3) >> 3; #else - int srcskipleft = s->vga.gr[0x2f] & 0x07; - int dstskipleft = srcskipleft * (DEPTH / 8); + /* + * In all other modes, GR2F bits [2:0] are a count of how many + * destination pixels to suppress for each scanline, which is our + * srcskipleftbits. We get dstskipleft, the number of bytes to + * skip, by multiplying this by the bytes-per-pixel. In these + * modes we never need to skip an entire source byte. + */ + int srcskipleftbits = s->vga.gr[0x2f] & 0x07; + int srcskipleftbytes = 0; + int dstskipleft = srcskipleftbits * (DEPTH / 8); #endif if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) { @@ -125,7 +147,8 @@ } for(y = 0; y < bltheight; y++) { - bitmask = 0x80 >> srcskipleft; + bitmask = 0x80 >> srcskipleftbits; + srcaddr += srcskipleftbytes; bits = cirrus_src(s, srcaddr++) ^ bits_xor; addr = dstaddr + dstskipleft; for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { @@ -191,10 +214,29 @@ int x, y, bitpos, pattern_y; unsigned int bits, bits_xor; unsigned int col; + + /* + * Copy from an 8x8 monochrome pattern with color expansion. + */ + #if DEPTH == 24 + /* + * For packed-24 modes, GR2F bits [4:0] are a count of destination + * bytes to be suppressed for each scanline, which we keep in + * dstskipleft. Our srcskipleft is the number of pixels to skip + * within the 8x8 source pattern to match up with that number + * of suppressed bytes. As the pattern repeats every 8 bits we + * take the number of pixels mod 8. + */ int dstskipleft = s->vga.gr[0x2f] & 0x1f; - int srcskipleft = dstskipleft / 3; + int srcskipleft = (dstskipleft / 3) & 0x7; #else + /* + * In all other modes, GR2F bits [2:0] are a count of how many + * destination pixels to suppress for each scanline, which is our + * srcskipleft. We get dstskipleft, the number of bytes to skip, + * by multiplying this by the bytes-per-pixel. + */ int srcskipleft = s->vga.gr[0x2f] & 0x07; int dstskipleft = srcskipleft * (DEPTH / 8); #endif diff -Nru qemu-10.0.8+ds/hw/display/virtio-gpu-virgl.c qemu-10.0.10+ds/hw/display/virtio-gpu-virgl.c --- qemu-10.0.8+ds/hw/display/virtio-gpu-virgl.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/display/virtio-gpu-virgl.c 2026-05-25 22:03:52.000000000 +0000 @@ -52,11 +52,17 @@ #if VIRGL_VERSION_MAJOR >= 1 struct virtio_gpu_virgl_hostmem_region { + Object parent_obj; MemoryRegion mr; struct VirtIOGPU *g; bool finish_unmapping; }; +#define TYPE_VIRTIO_GPU_VIRGL_HOSTMEM_REGION "virtio-gpu-virgl-hostmem-region" + +OBJECT_DECLARE_SIMPLE_TYPE(virtio_gpu_virgl_hostmem_region, + VIRTIO_GPU_VIRGL_HOSTMEM_REGION) + static struct virtio_gpu_virgl_hostmem_region * to_hostmem_region(MemoryRegion *mr) { @@ -70,14 +76,22 @@ virtio_gpu_process_cmdq(g); } -static void virtio_gpu_virgl_hostmem_region_free(void *obj) +/* + * MR could outlive the resource if MR's reference is held outside of + * virtio-gpu. In order to prevent unmapping resource while MR is alive, + * and thus, making the data pointer invalid, we will block virtio-gpu + * command processing until MR is fully unreferenced and freed. + */ +static void virtio_gpu_virgl_hostmem_region_finalize(Object *obj) { - MemoryRegion *mr = MEMORY_REGION(obj); - struct virtio_gpu_virgl_hostmem_region *vmr; + struct virtio_gpu_virgl_hostmem_region *vmr = VIRTIO_GPU_VIRGL_HOSTMEM_REGION(obj); VirtIOGPUBase *b; VirtIOGPUGL *gl; - vmr = to_hostmem_region(mr); + if (!vmr->g) { + return; + } + vmr->finish_unmapping = true; b = VIRTIO_GPU_BASE(vmr->g); @@ -92,11 +106,26 @@ qemu_bh_schedule(gl->cmdq_resume_bh); } +static const TypeInfo virtio_gpu_virgl_hostmem_region_info = { + .parent = TYPE_OBJECT, + .name = TYPE_VIRTIO_GPU_VIRGL_HOSTMEM_REGION, + .instance_size = sizeof(struct virtio_gpu_virgl_hostmem_region), + .instance_finalize = virtio_gpu_virgl_hostmem_region_finalize +}; + +static void virtio_gpu_virgl_types(void) +{ + type_register_static(&virtio_gpu_virgl_hostmem_region_info); +} + +type_init(virtio_gpu_virgl_types) + static int virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g, struct virtio_gpu_virgl_resource *res, uint64_t offset) { + g_autofree char *name = NULL; struct virtio_gpu_virgl_hostmem_region *vmr; VirtIOGPUBase *b = VIRTIO_GPU_BASE(g); MemoryRegion *mr; @@ -117,21 +146,16 @@ } vmr = g_new0(struct virtio_gpu_virgl_hostmem_region, 1); + name = g_strdup_printf("blob[%" PRIu32 "]", res->base.resource_id); + object_initialize_child(OBJECT(g), name, vmr, + TYPE_VIRTIO_GPU_VIRGL_HOSTMEM_REGION); vmr->g = g; mr = &vmr->mr; - memory_region_init_ram_ptr(mr, OBJECT(mr), NULL, size, data); + memory_region_init_ram_ptr(mr, OBJECT(vmr), "mr", size, data); memory_region_add_subregion(&b->hostmem, offset, mr); memory_region_set_enabled(mr, true); - /* - * MR could outlive the resource if MR's reference is held outside of - * virtio-gpu. In order to prevent unmapping resource while MR is alive, - * and thus, making the data pointer invalid, we will block virtio-gpu - * command processing until MR is fully unreferenced and freed. - */ - OBJECT(mr)->free = virtio_gpu_virgl_hostmem_region_free; - res->mr = mr; return 0; @@ -159,7 +183,7 @@ * 1. Begin async unmapping with memory_region_del_subregion() * and suspend/block cmd processing. * 2. Wait for res->mr to be freed and cmd processing resumed - * asynchronously by virtio_gpu_virgl_hostmem_region_free(). + * asynchronously by virtio_gpu_virgl_hostmem_region_finalize(). * 3. Finish the unmapping with final virgl_renderer_resource_unmap(). */ if (vmr->finish_unmapping) { @@ -182,7 +206,7 @@ /* memory region owns self res->mr object and frees it by itself */ memory_region_set_enabled(mr, false); memory_region_del_subregion(&b->hostmem, mr); - object_unref(OBJECT(mr)); + object_unparent(OBJECT(vmr)); } return 0; @@ -684,8 +708,7 @@ return; } - res = virtio_gpu_virgl_find_resource(g, cblob.resource_id); - if (res) { + if (virtio_gpu_virgl_find_resource(g, cblob.resource_id)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n", __func__, cblob.resource_id); cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; @@ -738,8 +761,9 @@ res->base.dmabuf_fd = info.fd; + /* Now live, cleaned up in virtio_gpu_virgl_resource_unref */ QTAILQ_INSERT_HEAD(&g->reslist, &res->base, next); - res = NULL; + g_steal_pointer(&res); } static void virgl_cmd_resource_map_blob(VirtIOGPU *g, @@ -1175,9 +1199,9 @@ } #if VIRGL_VERSION_MAJOR >= 1 - gl->cmdq_resume_bh = aio_bh_new(qemu_get_aio_context(), - virtio_gpu_virgl_resume_cmdq_bh, - g); + gl->cmdq_resume_bh = virtio_bh_io_new_guarded(DEVICE(g), + virtio_gpu_virgl_resume_cmdq_bh, + g); #endif return 0; diff -Nru qemu-10.0.8+ds/hw/display/virtio-gpu.c qemu-10.0.10+ds/hw/display/virtio-gpu.c --- qemu-10.0.8+ds/hw/display/virtio-gpu.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/display/virtio-gpu.c 2026-05-25 22:03:52.000000000 +0000 @@ -227,16 +227,20 @@ virtio_gpu_ctrl_response(g, cmd, &edid.hdr, sizeof(edid)); } -static uint32_t calc_image_hostmem(pixman_format_code_t pformat, - uint32_t width, uint32_t height) -{ - /* Copied from pixman/pixman-bits-image.c, skip integer overflow check. - * pixman_image_create_bits will fail in case it overflow. - */ +static bool calc_image_hostmem(pixman_format_code_t pformat, + uint32_t width, uint32_t height, + uint32_t *hostmem) +{ + uint64_t bpp = PIXMAN_FORMAT_BPP(pformat); + uint64_t stride = (((uint64_t)width * bpp + 0x1f) >> 5) * sizeof(uint32_t); + uint64_t size = (uint64_t)height * stride; + + if (size > UINT32_MAX) { + return false; + } - int bpp = PIXMAN_FORMAT_BPP(pformat); - int stride = ((width * bpp + 0x1f) >> 5) * sizeof(uint32_t); - return height * stride; + *hostmem = size; + return true; } static void virtio_gpu_resource_create_2d(VirtIOGPU *g, @@ -245,6 +249,7 @@ pixman_format_code_t pformat; struct virtio_gpu_simple_resource *res; struct virtio_gpu_resource_create_2d c2d; + uint32_t hostmem; VIRTIO_GPU_FILL_CMD(c2d); virtio_gpu_bswap_32(&c2d, sizeof(c2d)); @@ -283,7 +288,12 @@ return; } - res->hostmem = calc_image_hostmem(pformat, c2d.width, c2d.height); + if (!calc_image_hostmem(pformat, c2d.width, c2d.height, &hostmem)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: image dimensions overflow\n", + __func__); + goto end; + } + res->hostmem = hostmem; if (res->hostmem + g->hostmem < g->conf_max_hostmem) { if (!qemu_pixman_image_new_shareable( &res->image, @@ -1283,7 +1293,7 @@ { VirtIOGPU *g = opaque; struct virtio_gpu_simple_resource *res; - uint32_t resource_id, pformat; + uint32_t resource_id, pformat, hostmem; int i; g->hostmem = 0; @@ -1309,7 +1319,11 @@ return -EINVAL; } - res->hostmem = calc_image_hostmem(pformat, res->width, res->height); + if (!calc_image_hostmem(pformat, res->width, res->height, &hostmem)) { + g_free(res); + return -EINVAL; + } + res->hostmem = hostmem; if (!qemu_pixman_image_new_shareable(&res->image, &res->share_handle, "virtio-gpu res", @@ -1514,9 +1528,9 @@ g->ctrl_vq = virtio_get_queue(vdev, 0); g->cursor_vq = virtio_get_queue(vdev, 1); - g->ctrl_bh = virtio_bh_new_guarded(qdev, virtio_gpu_ctrl_bh, g); - g->cursor_bh = virtio_bh_new_guarded(qdev, virtio_gpu_cursor_bh, g); - g->reset_bh = qemu_bh_new(virtio_gpu_reset_bh, g); + g->ctrl_bh = virtio_bh_io_new_guarded(qdev, virtio_gpu_ctrl_bh, g); + g->cursor_bh = virtio_bh_io_new_guarded(qdev, virtio_gpu_cursor_bh, g); + g->reset_bh = virtio_bh_io_new_guarded(qdev, virtio_gpu_reset_bh, g); qemu_cond_init(&g->reset_cond); QTAILQ_INIT(&g->reslist); QTAILQ_INIT(&g->cmdq); diff -Nru qemu-10.0.8+ds/hw/dma/pl080.c qemu-10.0.10+ds/hw/dma/pl080.c --- qemu-10.0.8+ds/hw/dma/pl080.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/dma/pl080.c 2026-05-25 22:03:52.000000000 +0000 @@ -102,6 +102,7 @@ int size; uint8_t buff[4]; uint32_t req; + uint32_t next_lli; s->tc_mask = 0; for (c = 0; c < s->nchannels; c++) { @@ -164,40 +165,61 @@ destination widths are different. */ swidth = 1 << ((ch->ctrl >> 18) & 7); dwidth = 1 << ((ch->ctrl >> 21) & 7); - for (n = 0; n < dwidth; n+= swidth) { + + /* Only widths of 1, 2 or 4 are valid */ + if (swidth > 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "pl080: channel %d: invalid SWidth %d\n", + c, extract32(ch->ctrl, 18, 3)); + continue; + } + if (dwidth > 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "pl080: channel %d: invalid DWidth %d\n", + c, extract32(ch->ctrl, 21, 3)); + continue; + } + if ((size * swidth) % dwidth) { + qemu_log_mask(LOG_GUEST_ERROR, + "pl080: channel %d: transfer size mismatch: size=%d swidth=%d dwidth=%d\n", + c, size, swidth, dwidth); + continue; + } + xsize = MAX(swidth, dwidth); + for (n = 0; n < xsize; n += swidth) { address_space_read(&s->downstream_as, ch->src, MEMTXATTRS_UNSPECIFIED, buff + n, swidth); if (ch->ctrl & PL080_CCTRL_SI) ch->src += swidth; } - xsize = (dwidth < swidth) ? swidth : dwidth; /* ??? This may pad the value incorrectly for dwidth < 32. */ for (n = 0; n < xsize; n += dwidth) { - address_space_write(&s->downstream_as, ch->dest + n, + address_space_write(&s->downstream_as, ch->dest, MEMTXATTRS_UNSPECIFIED, buff + n, dwidth); if (ch->ctrl & PL080_CCTRL_DI) - ch->dest += swidth; + ch->dest += dwidth; } - size--; + size -= xsize / swidth; ch->ctrl = (ch->ctrl & 0xfffff000) | size; if (size == 0) { /* Transfer complete. */ - if (ch->lli) { + next_lli = (ch->lli & ~3); + if (next_lli) { ch->src = address_space_ldl_le(&s->downstream_as, - ch->lli, + next_lli, MEMTXATTRS_UNSPECIFIED, NULL); ch->dest = address_space_ldl_le(&s->downstream_as, - ch->lli + 4, + next_lli + 4, MEMTXATTRS_UNSPECIFIED, NULL); ch->ctrl = address_space_ldl_le(&s->downstream_as, - ch->lli + 12, + next_lli + 12, MEMTXATTRS_UNSPECIFIED, NULL); ch->lli = address_space_ldl_le(&s->downstream_as, - ch->lli + 8, + next_lli + 8, MEMTXATTRS_UNSPECIFIED, NULL); } else { @@ -212,6 +234,7 @@ if (--s->running) s->running = 1; } + pl080_update(s); } static uint64_t pl080_read(void *opaque, hwaddr offset, diff -Nru qemu-10.0.8+ds/hw/hyperv/syndbg.c qemu-10.0.10+ds/hw/hyperv/syndbg.c --- qemu-10.0.8+ds/hw/hyperv/syndbg.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/hyperv/syndbg.c 2026-05-25 22:03:52.000000000 +0000 @@ -189,7 +189,7 @@ { uint16_t ret; uint8_t data_buf[TARGET_PAGE_SIZE - UDP_PKT_HEADER_SIZE]; - hwaddr out_len; + hwaddr out_len, out_requested_len; void *out_data; ssize_t recv_byte_count; @@ -218,29 +218,28 @@ if (is_raw) { out_len += UDP_PKT_HEADER_SIZE; } + out_requested_len = out_len; out_data = cpu_physical_memory_map(outgpa, &out_len, 1); - if (!out_data) { - return HV_STATUS_INSUFFICIENT_MEMORY; + ret = HV_STATUS_INSUFFICIENT_MEMORY; + if (!out_data || out_len < out_requested_len) { + goto cleanup_out_data; } if (is_raw && - !create_udp_pkt(syndbg, out_data, - recv_byte_count + UDP_PKT_HEADER_SIZE, + !create_udp_pkt(syndbg, out_data, out_len, data_buf, recv_byte_count)) { - ret = HV_STATUS_INSUFFICIENT_MEMORY; goto cleanup_out_data; } else if (!is_raw) { - memcpy(out_data, data_buf, recv_byte_count); + memcpy(out_data, data_buf, out_len); } - *retrieved_count = recv_byte_count; - if (is_raw) { - *retrieved_count += UDP_PKT_HEADER_SIZE; - } + *retrieved_count = out_len; ret = HV_STATUS_SUCCESS; cleanup_out_data: - cpu_physical_memory_unmap(out_data, out_len, 1, out_len); + if (out_data) { + cpu_physical_memory_unmap(out_data, out_len, 1, out_len); + } return ret; } diff -Nru qemu-10.0.8+ds/hw/i2c/aspeed_i2c.c qemu-10.0.10+ds/hw/i2c/aspeed_i2c.c --- qemu-10.0.8+ds/hw/i2c/aspeed_i2c.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/i2c/aspeed_i2c.c 2026-05-25 22:03:52.000000000 +0000 @@ -94,7 +94,7 @@ unsigned size) { AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); - uint64_t value = bus->regs[offset / sizeof(*bus->regs)]; + uint64_t value = -1; switch (offset) { case A_I2CD_FUN_CTRL: @@ -105,7 +105,7 @@ case A_I2CD_DEV_ADDR: case A_I2CD_POOL_CTRL: case A_I2CD_BYTE_BUF: - /* Value is already set, don't do anything. */ + value = bus->regs[offset / sizeof(*bus->regs)]; break; case A_I2CD_CMD: value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); @@ -113,21 +113,20 @@ case A_I2CD_DMA_ADDR: if (!aic->has_dma) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); - value = -1; break; } + value = bus->regs[offset / sizeof(*bus->regs)]; break; case A_I2CD_DMA_LEN: if (!aic->has_dma) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); - value = -1; + break; } + value = bus->regs[offset / sizeof(*bus->regs)]; break; - default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); - value = -1; break; } @@ -139,7 +138,7 @@ unsigned size) { AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); - uint64_t value = bus->regs[offset / sizeof(*bus->regs)]; + uint64_t value = -1; switch (offset) { case A_I2CC_FUN_CTRL: @@ -159,13 +158,12 @@ case A_I2CS_CMD: case A_I2CS_INTR_CTRL: case A_I2CS_DMA_LEN_STS: - /* Value is already set, don't do anything. */ + case A_I2CS_INTR_STS: + value = bus->regs[offset / sizeof(*bus->regs)]; break; case A_I2CC_DMA_ADDR: value = extract64(bus->dma_dram_offset, 0, 32); break; - case A_I2CS_INTR_STS: - break; case A_I2CM_CMD: value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); break; @@ -176,13 +174,13 @@ if (!aic->has_dma64) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA 64 bits support\n", __func__); - value = -1; + break; } + value = bus->regs[offset / sizeof(*bus->regs)]; break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); - value = -1; break; } diff -Nru qemu-10.0.8+ds/hw/i2c/microbit_i2c.c qemu-10.0.10+ds/hw/i2c/microbit_i2c.c --- qemu-10.0.8+ds/hw/i2c/microbit_i2c.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/i2c/microbit_i2c.c 2026-05-25 22:03:52.000000000 +0000 @@ -41,8 +41,13 @@ data = 0x01; break; case NRF51_TWI_REG_RXD: + /* + * Return the next byte from our fake data sequence. If + * the guest keeps reading the register after that, keep + * returning the same last byte value. + */ data = twi_read_sequence[s->read_idx]; - if (s->read_idx < G_N_ELEMENTS(twi_read_sequence)) { + if (s->read_idx + 1 < G_N_ELEMENTS(twi_read_sequence)) { s->read_idx++; } break; diff -Nru qemu-10.0.8+ds/hw/i386/vmmouse.c qemu-10.0.10+ds/hw/i386/vmmouse.c --- qemu-10.0.8+ds/hw/i386/vmmouse.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/i386/vmmouse.c 2026-05-25 22:03:52.000000000 +0000 @@ -72,7 +72,7 @@ ISAKBDState *i8042; }; -static void vmmouse_get_data(uint32_t *data) +static void vmmouse_get_data(uint64_t *data) { X86CPU *cpu = X86_CPU(current_cpu); CPUX86State *env = &cpu->env; @@ -82,7 +82,7 @@ data[4] = env->regs[R_ESI]; data[5] = env->regs[R_EDI]; } -static void vmmouse_set_data(const uint32_t *data) +static void vmmouse_set_data(const uint64_t *data) { X86CPU *cpu = X86_CPU(current_cpu); CPUX86State *env = &cpu->env; @@ -197,7 +197,7 @@ vmmouse_remove_handler(s); } -static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size) +static void vmmouse_data(VMMouseState *s, uint64_t *data, uint32_t size) { int i; @@ -221,7 +221,7 @@ static uint32_t vmmouse_ioport_read(void *opaque, uint32_t addr) { VMMouseState *s = opaque; - uint32_t data[6]; + uint64_t data[6]; uint16_t command; vmmouse_get_data(data); @@ -247,7 +247,7 @@ vmmouse_request_absolute(s); break; default: - printf("vmmouse: unknown command %x\n", data[1]); + printf("vmmouse: unknown command %" PRIx64 "\n", data[1]); break; } break; diff -Nru qemu-10.0.8+ds/hw/ide/core.c qemu-10.0.10+ds/hw/ide/core.c --- qemu-10.0.8+ds/hw/ide/core.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/ide/core.c 2026-05-25 22:03:52.000000000 +0000 @@ -420,24 +420,15 @@ QEMUBH *bh; int ret; QEMUIOVector *qiov; - BlockAIOCB *aiocb; - int i, j; + bool canceled; } TrimAIOCB; static void trim_aio_cancel(BlockAIOCB *acb) { TrimAIOCB *iocb = container_of(acb, TrimAIOCB, common); - /* Exit the loop so ide_issue_trim_cb will not continue */ - iocb->j = iocb->qiov->niov - 1; - iocb->i = (iocb->qiov->iov[iocb->j].iov_len / 8) - 1; - - iocb->ret = -ECANCELED; - - if (iocb->aiocb) { - blk_aio_cancel_async(iocb->aiocb); - iocb->aiocb = NULL; - } + /* Exit the loop so ide_trim_co_entry will not continue */ + iocb->canceled = true; } static const AIOCBInfo trim_aiocb_info = { @@ -456,65 +447,64 @@ iocb->bh = NULL; qemu_aio_unref(iocb); - /* Paired with an increment in ide_issue_trim() */ - blk_dec_in_flight(blk); + /* Paired with blk_co_start_request in ide_trim_co_entry() */ + blk_end_request(blk); } -static void ide_issue_trim_cb(void *opaque, int ret) +static void coroutine_fn ide_trim_co_entry(void *opaque) { TrimAIOCB *iocb = opaque; IDEState *s = iocb->s; + int i, j; + int ret; - if (iocb->i >= 0) { - if (ret >= 0) { - block_acct_done(blk_get_stats(s->blk), &s->acct); - } else { - block_acct_failed(blk_get_stats(s->blk), &s->acct); - } - } + /* Paired with blk_end_request in ide_trim_bh_cb() */ + blk_co_start_request(s->blk); + + for (j = 0; j < iocb->qiov->niov; j++) { + for (i = 0; i < iocb->qiov->iov[j].iov_len / 8; i++) { + uint64_t *buffer = iocb->qiov->iov[j].iov_base; + + /* 6-byte LBA + 2-byte range per entry */ + uint64_t entry = le64_to_cpu(buffer[i]); + uint64_t sector = entry & 0x0000ffffffffffffULL; + uint16_t count = entry >> 48; + + if (count == 0) { + continue; + } - if (ret >= 0) { - while (iocb->j < iocb->qiov->niov) { - int j = iocb->j; - while (++iocb->i < iocb->qiov->iov[j].iov_len / 8) { - int i = iocb->i; - uint64_t *buffer = iocb->qiov->iov[j].iov_base; - - /* 6-byte LBA + 2-byte range per entry */ - uint64_t entry = le64_to_cpu(buffer[i]); - uint64_t sector = entry & 0x0000ffffffffffffULL; - uint16_t count = entry >> 48; - - if (count == 0) { - continue; - } - - if (!ide_sect_range_ok(s, sector, count)) { - block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_UNMAP); - iocb->ret = -EINVAL; - goto done; - } - - block_acct_start(blk_get_stats(s->blk), &s->acct, - count << BDRV_SECTOR_BITS, BLOCK_ACCT_UNMAP); - - /* Got an entry! Submit and exit. */ - iocb->aiocb = blk_aio_pdiscard(s->blk, - sector << BDRV_SECTOR_BITS, - count << BDRV_SECTOR_BITS, - ide_issue_trim_cb, opaque); - return; + if (iocb->canceled) { + iocb->ret = -ECANCELED; + goto done; } - iocb->j++; - iocb->i = -1; + if (!ide_sect_range_ok(s, sector, count)) { + block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_UNMAP); + iocb->ret = -EINVAL; + goto done; + } + + block_acct_start(blk_get_stats(s->blk), &s->acct, + count << BDRV_SECTOR_BITS, BLOCK_ACCT_UNMAP); + + /* Got an entry! Submit and exit. */ + ret = blk_co_pdiscard(s->blk, + sector << BDRV_SECTOR_BITS, + count << BDRV_SECTOR_BITS, + BDRV_REQ_NO_QUEUE); + if (ret >= 0) { + block_acct_done(blk_get_stats(s->blk), &s->acct); + } else { + iocb->ret = ret; + block_acct_failed(blk_get_stats(s->blk), &s->acct); + goto done; + } } - } else { - iocb->ret = ret; } + iocb->ret = 0; done: - iocb->aiocb = NULL; if (iocb->bh) { replay_bh_schedule_event(iocb->bh); } @@ -527,9 +517,7 @@ IDEState *s = opaque; IDEDevice *dev = s->unit ? s->bus->slave : s->bus->master; TrimAIOCB *iocb; - - /* Paired with a decrement in ide_trim_bh_cb() */ - blk_inc_in_flight(s->blk); + Coroutine *co; iocb = blk_aio_get(&trim_aiocb_info, s->blk, cb, cb_opaque); iocb->s = s; @@ -537,9 +525,11 @@ &DEVICE(dev)->mem_reentrancy_guard); iocb->ret = 0; iocb->qiov = qiov; - iocb->i = -1; - iocb->j = 0; - ide_issue_trim_cb(iocb, 0); + iocb->canceled = false; + + co = qemu_coroutine_create(ide_trim_co_entry, iocb); + aio_co_enter(qemu_get_current_aio_context(), co); + return &iocb->common; } @@ -799,6 +789,7 @@ s->error = 0; /* not needed by IDE spec, but needed by Windows */ sector_num = ide_get_sector(s); n = s->nsector; + ide_set_retry(s); if (n == 0) { ide_transfer_stop(s); diff -Nru qemu-10.0.8+ds/hw/intc/arm_gicv3_cpuif.c qemu-10.0.10+ds/hw/intc/arm_gicv3_cpuif.c --- qemu-10.0.8+ds/hw/intc/arm_gicv3_cpuif.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/intc/arm_gicv3_cpuif.c 2026-05-25 22:03:52.000000000 +0000 @@ -1870,9 +1870,40 @@ * at a priority outside the Non-secure range (128..255), since this * would otherwise allow malicious NS code to block delivery of S interrupts * by writing a bad value to these registers. + * + * The NS priority range (128..255) maps to APR bits starting at + * aprbit = 0x80 >> (8 - prebits). Depending on prebits, this boundary + * may fall within AP1R0 or AP1R1, so we cannot simply WI the entire + * register. Instead we calculate which bits within each register + * correspond to the Secure range and preserve those, while allowing + * NS code to modify only the NS range bits. + * + * prebits=4: num_aprs=1, NS starts at AP1R0[8] + * prebits=5: num_aprs=1, NS starts at AP1R0[16] + * prebits=6: num_aprs=2, NS starts at AP1R1[0] + * prebits=7: num_aprs=4, NS starts at AP1R2[0] */ - if (grp == GICV3_G1NS && regno < 2 && arm_feature(env, ARM_FEATURE_EL3)) { - return; + if (grp == GICV3_G1NS && arm_feature(env, ARM_FEATURE_EL3)) { + int ns_start_bit = 0x80 >> (8 - cs->prebits); + int ns_start_regno = ns_start_bit / 32; + int ns_start_regbit = ns_start_bit % 32; + + if (regno < ns_start_regno) { + /* This entire register is in the Secure range: WI */ + return; + } else if (regno == ns_start_regno && ns_start_regbit > 0) { + /* + * This register is split: low bits are Secure, high bits are NS. + * Preserve the Secure bits (below ns_start_regbit) from the + * current value, and take the NS bits (at and above + * ns_start_regbit) from the written value. + */ + uint32_t secure_mask = MAKE_64BIT_MASK(0, ns_start_regbit); + + value = (cs->icc_apr[grp][regno] & secure_mask) | + (value & ~secure_mask); + } + /* else: regno > ns_start_regno, entire register is NS: allow write */ } if (cs->nmi_support) { diff -Nru qemu-10.0.8+ds/hw/intc/riscv_aclint.c qemu-10.0.10+ds/hw/intc/riscv_aclint.c --- qemu-10.0.8+ds/hw/intc/riscv_aclint.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/intc/riscv_aclint.c 2026-05-25 22:03:52.000000000 +0000 @@ -130,6 +130,7 @@ addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) { size_t hartid = mtimer->hartid_base + ((addr - mtimer->timecmp_base) >> 3); + size_t hartid_offset = hartid - mtimer->hartid_base; CPUState *cpu = cpu_by_arch_id(hartid); CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; if (!env) { @@ -137,11 +138,11 @@ "aclint-mtimer: invalid hartid: %zu", hartid); } else if ((addr & 0x7) == 0) { /* timecmp_lo for RV32/RV64 or timecmp for RV64 */ - uint64_t timecmp = mtimer->timecmp[hartid]; + uint64_t timecmp = mtimer->timecmp[hartid_offset]; return (size == 4) ? (timecmp & 0xFFFFFFFF) : timecmp; } else if ((addr & 0x7) == 4) { /* timecmp_hi */ - uint64_t timecmp = mtimer->timecmp[hartid]; + uint64_t timecmp = mtimer->timecmp[hartid_offset]; return (timecmp >> 32) & 0xFFFFFFFF; } else { qemu_log_mask(LOG_UNIMP, @@ -173,6 +174,7 @@ addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) { size_t hartid = mtimer->hartid_base + ((addr - mtimer->timecmp_base) >> 3); + size_t hartid_offset = hartid - mtimer->hartid_base; CPUState *cpu = cpu_by_arch_id(hartid); CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; if (!env) { @@ -181,7 +183,7 @@ } else if ((addr & 0x7) == 0) { if (size == 4) { /* timecmp_lo for RV32/RV64 */ - uint64_t timecmp_hi = mtimer->timecmp[hartid] >> 32; + uint64_t timecmp_hi = mtimer->timecmp[hartid_offset] >> 32; riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, timecmp_hi << 32 | (value & 0xFFFFFFFF)); } else { @@ -192,7 +194,7 @@ } else if ((addr & 0x7) == 4) { if (size == 4) { /* timecmp_hi for RV32/RV64 */ - uint64_t timecmp_lo = mtimer->timecmp[hartid]; + uint64_t timecmp_lo = mtimer->timecmp[hartid_offset]; riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, value << 32 | (timecmp_lo & 0xFFFFFFFF)); } else { diff -Nru qemu-10.0.8+ds/hw/intc/xics.c qemu-10.0.10+ds/hw/intc/xics.c --- qemu-10.0.8+ds/hw/intc/xics.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/intc/xics.c 2026-05-25 22:03:52.000000000 +0000 @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qapi/error.h" #include "trace.h" #include "qemu/timer.h" @@ -222,6 +223,13 @@ trace_xics_icp_irq(server, nr, priority); + if (!icp) { + qemu_log_mask(LOG_GUEST_ERROR, "XICS: invalid server %d for IRQ 0x%x\n", + server, nr); + ics_reject(ics, nr); + return; + } + if ((priority >= CPPR(icp)) || (XISR(icp) && (icp->pending_priority <= priority))) { ics_reject(ics, nr); diff -Nru qemu-10.0.8+ds/hw/misc/aspeed_hace.c qemu-10.0.10+ds/hw/misc/aspeed_hace.c --- qemu-10.0.8+ds/hw/misc/aspeed_hace.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/misc/aspeed_hace.c 2026-05-25 22:03:52.000000000 +0000 @@ -112,6 +112,14 @@ hwaddr req_len, uint32_t *total_msg_len, uint32_t *pad_offset) { + /* Need at least 8 bytes to read the total message length field */ + if (req_len < 8) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid request length=0x%" HWADDR_PRIx "\n", + __func__, req_len); + return false; + } + *total_msg_len = (uint32_t)(ldq_be_p(iov->iov_base + req_len - 8) / 8); /* * SG_LIST_LEN_LAST asserted in the request length doesn't mean it is the @@ -161,6 +169,19 @@ return iov_count; } +static bool hash_accumulate_len(AspeedHACEState *s, hwaddr plen) +{ + if (plen > UINT32_MAX - s->total_req_len) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: total_req_len overflow, current=0x%x, adding=0x%" + HWADDR_PRIx "\n", __func__, s->total_req_len, plen); + return false; + } + + s->total_req_len += plen; + return true; +} + static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, bool acc_mode) { @@ -216,7 +237,9 @@ } iov[i].iov_base = haddr; if (acc_mode) { - s->total_req_len += plen; + if (!hash_accumulate_len(s, plen)) { + return; + } if (has_padding(s, &iov[i], plen, &total_msg_len, &pad_offset)) { diff -Nru qemu-10.0.8+ds/hw/misc/avr_power.c qemu-10.0.10+ds/hw/misc/avr_power.c --- qemu-10.0.8+ds/hw/misc/avr_power.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/misc/avr_power.c 2026-05-25 22:03:52.000000000 +0000 @@ -73,6 +73,9 @@ .impl = { .max_access_size = 1, }, + .valid = { + .max_access_size = 1, + }, }; static void avr_mask_init(Object *dev) diff -Nru qemu-10.0.8+ds/hw/misc/bcm2835_rng.c qemu-10.0.10+ds/hw/misc/bcm2835_rng.c --- qemu-10.0.8+ds/hw/misc/bcm2835_rng.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/misc/bcm2835_rng.c 2026-05-25 22:03:52.000000000 +0000 @@ -93,6 +93,10 @@ .read = bcm2835_rng_read, .write = bcm2835_rng_write, .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 4, + .valid.max_access_size = 4, }; static const VMStateDescription vmstate_bcm2835_rng = { diff -Nru qemu-10.0.8+ds/hw/misc/virt_ctrl.c qemu-10.0.10+ds/hw/misc/virt_ctrl.c --- qemu-10.0.8+ds/hw/misc/virt_ctrl.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/misc/virt_ctrl.c 2026-05-25 22:03:52.000000000 +0000 @@ -43,7 +43,7 @@ break; } - trace_virt_ctrl_write(s, addr, size, value); + trace_virt_ctrl_read(s, addr, size, value); return value; } diff -Nru qemu-10.0.8+ds/hw/net/allwinner-sun8i-emac.c qemu-10.0.10+ds/hw/net/allwinner-sun8i-emac.c --- qemu-10.0.8+ds/hw/net/allwinner-sun8i-emac.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/net/allwinner-sun8i-emac.c 2026-05-25 22:03:52.000000000 +0000 @@ -727,6 +727,9 @@ break; case REG_RX_CTL_0: /* Receive Control 0 */ s->rx_ctl0 = value; + if (allwinner_sun8i_emac_can_receive(nc)) { + qemu_flush_queued_packets(nc); + } break; case REG_RX_CTL_1: /* Receive Control 1 */ s->rx_ctl1 = value | RX_CTL1_RX_MD; diff -Nru qemu-10.0.8+ds/hw/net/ftgmac100.c qemu-10.0.10+ds/hw/net/ftgmac100.c --- qemu-10.0.8+ds/hw/net/ftgmac100.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/net/ftgmac100.c 2026-05-25 22:03:52.000000000 +0000 @@ -624,7 +624,10 @@ bd.des0 &= ~FTGMAC100_TXDES0_TXDMA_OWN; /* Write back the modified descriptor. */ - ftgmac100_write_bd(&bd, addr); + if (ftgmac100_write_bd(&bd, addr)) { + s->isr |= FTGMAC100_INT_AHB_ERR; + break; + } /* Advance to the next descriptor. */ if (bd.des0 & s->txdes0_edotr) { addr = tx_ring; @@ -1134,7 +1137,10 @@ bd.des0 |= flags | FTGMAC100_RXDES0_LRS; s->isr |= FTGMAC100_INT_RPKT_BUF; } - ftgmac100_write_bd(&bd, addr); + if (ftgmac100_write_bd(&bd, addr)) { + s->isr |= FTGMAC100_INT_AHB_ERR; + break; + } if (bd.des0 & s->rxdes0_edorr) { addr = s->rx_ring; } else { diff -Nru qemu-10.0.8+ds/hw/net/npcm_gmac.c qemu-10.0.10+ds/hw/net/npcm_gmac.c --- qemu-10.0.8+ds/hw/net/npcm_gmac.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/net/npcm_gmac.c 2026-05-25 22:03:52.000000000 +0000 @@ -703,6 +703,13 @@ NPCMGMACState *gmac = opaque; uint32_t v = 0; + if (offset >= NPCM_GMAC_REG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid register offset: 0x%04" HWADDR_PRIx"\n", + DEVICE(gmac)->canonical_path, offset); + return v; + } + switch (offset) { /* Write only registers */ case A_NPCM_DMA_XMT_POLL_DEMAND: @@ -727,6 +734,13 @@ trace_npcm_gmac_reg_write(DEVICE(gmac)->canonical_path, offset, v); + if (offset >= NPCM_GMAC_REG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid register offset: 0x%04" HWADDR_PRIx"\n", + DEVICE(gmac)->canonical_path, offset); + return; + } + switch (offset) { /* Read only registers */ case A_NPCM_GMAC_VERSION: diff -Nru qemu-10.0.8+ds/hw/net/rocker/rocker_of_dpa.c qemu-10.0.10+ds/hw/net/rocker/rocker_of_dpa.c --- qemu-10.0.8+ds/hw/net/rocker/rocker_of_dpa.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/net/rocker/rocker_of_dpa.c 2026-05-25 22:03:52.000000000 +0000 @@ -2063,6 +2063,7 @@ err_out: group->l2_flood.group_count = 0; g_free(group->l2_flood.group_ids); + group->l2_flood.group_ids = NULL; g_free(tlvs); return err; diff -Nru qemu-10.0.8+ds/hw/net/rtl8139.c qemu-10.0.10+ds/hw/net/rtl8139.c --- qemu-10.0.8+ds/hw/net/rtl8139.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/net/rtl8139.c 2026-05-25 22:03:52.000000000 +0000 @@ -2131,6 +2131,26 @@ hlen, ip->ip_sum); } + /* + * The code in this function triggers a GCC bug where an + * interaction between -fsanitize=address and -Wstringop-overflow + * results in a false-positive stringop-overflow warning that is + * only emitted when the address sanitizer is enabled: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114494 + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99673 + * GCC incorrectly thinks that the eth_payload_data buffer has + * the type and size of the first field in 'struct ip_header', i.e. + * one byte, and then complains about all other attempts to access + * data in the buffer. + * + * Work around this by disabling the warning when building with + * GCC and the address sanitizer is enabled. + */ +#pragma GCC diagnostic push +#if !defined(__clang__) && defined(QEMU_SANITIZE_ADDRESS) +#pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif + if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP) { /* Large enough for the TCP header? */ @@ -2314,6 +2334,9 @@ /* restore IP header */ memcpy(eth_payload_data, saved_ip_header, hlen); } + +#pragma GCC diagnostic pop + } skip_offload: diff -Nru qemu-10.0.8+ds/hw/net/smc91c111.c qemu-10.0.10+ds/hw/net/smc91c111.c --- qemu-10.0.8+ds/hw/net/smc91c111.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/net/smc91c111.c 2026-05-25 22:03:52.000000000 +0000 @@ -30,6 +30,12 @@ * LAN91C111 datasheet). */ #define MAX_PACKET_SIZE 2048 +/* + * Size of the non-data fields in a data frame: status word, + * byte count, control byte, and last data byte; this defines + * the smallest value the byte count in the frame can validly be. + */ +#define MIN_PACKET_SIZE 6 #define TYPE_SMC91C111 "smc91c111" OBJECT_DECLARE_SIMPLE_TYPE(smc91c111_state, SMC91C111) @@ -289,7 +295,7 @@ *(p++) = 0x40; len = *(p++); len |= ((int)*(p++)) << 8; - if (len > MAX_PACKET_SIZE) { + if (len < MIN_PACKET_SIZE || len > MAX_PACKET_SIZE) { /* * Datasheet doesn't say what to do here, and there is no * relevant tx error condition listed. Log, and drop the packet. @@ -300,7 +306,13 @@ smc91c111_complete_tx_packet(s, packetnum); continue; } - len -= 6; + /* + * Convert from size of the data frame to number of bytes of + * actual packet data. Whether the "last data byte" field is + * included in the packet depends on the ODD bit in the control + * byte at the end of the frame. + */ + len -= MIN_PACKET_SIZE; control = p[len + 1]; if (control & 0x20) len++; diff -Nru qemu-10.0.8+ds/hw/net/trace-events qemu-10.0.10+ds/hw/net/trace-events --- qemu-10.0.8+ds/hw/net/trace-events 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/net/trace-events 2026-05-25 22:03:52.000000000 +0000 @@ -517,3 +517,4 @@ # xilinx_ethlite.c ethlite_pkt_lost(uint32_t rx_ctrl) "rx_ctrl:0x%" PRIx32 ethlite_pkt_size_too_big(uint64_t size) "size:0x%" PRIx64 +ethlite_pkt_tx_size_too_big(uint64_t size) "size:0x%" PRIx64 diff -Nru qemu-10.0.8+ds/hw/net/xilinx_ethlite.c qemu-10.0.10+ds/hw/net/xilinx_ethlite.c --- qemu-10.0.8+ds/hw/net/xilinx_ethlite.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/net/xilinx_ethlite.c 2026-05-25 22:03:52.000000000 +0000 @@ -162,9 +162,15 @@ break; case TX_CTRL: if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { - qemu_send_packet(qemu_get_queue(s->nic), - txbuf_ptr(s, port_index), - s->port[port_index].reg.tx_len); + uint32_t tx_size = s->port[port_index].reg.tx_len; + + if (tx_size >= BUFSZ_MAX) { + trace_ethlite_pkt_tx_size_too_big(tx_size); + } else { + qemu_send_packet(qemu_get_queue(s->nic), + txbuf_ptr(s, port_index), + tx_size); + } if (s->port[port_index].reg.tx_ctrl & CTRL_I) { eth_pulse_irq(s); } diff -Nru qemu-10.0.8+ds/hw/nvme/ctrl.c qemu-10.0.10+ds/hw/nvme/ctrl.c --- qemu-10.0.8+ds/hw/nvme/ctrl.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/nvme/ctrl.c 2026-05-25 22:03:52.000000000 +0000 @@ -5515,7 +5515,7 @@ event_notifier_set_handler(&cq->notifier, NULL); event_notifier_cleanup(&cq->notifier); } - if (msix_enabled(pci) && cq->irq_enabled) { + if (msix_present(pci) && cq->irq_enabled) { msix_vector_unuse(pci, cq->vector); } if (cq->cqid) { @@ -5556,7 +5556,7 @@ { PCIDevice *pci = PCI_DEVICE(n); - if (msix_enabled(pci) && irq_enabled) { + if (msix_present(pci) && irq_enabled) { msix_vector_use(pci, vector); } @@ -6109,7 +6109,7 @@ { uint16_t sqid = le32_to_cpu(req->cmd.cdw10) & 0xffff; uint16_t cid = (le32_to_cpu(req->cmd.cdw10) >> 16) & 0xffff; - NvmeSQueue *sq = n->sq[sqid]; + NvmeSQueue *sq; NvmeRequest *r, *next; int i; @@ -6118,6 +6118,8 @@ return NVME_INVALID_FIELD | NVME_DNR; } + sq = n->sq[sqid]; + if (sqid == 0) { for (i = 0; i < n->outstanding_aers; i++) { NvmeRequest *re = n->aer_reqs[i]; diff -Nru qemu-10.0.8+ds/hw/nvme/ns.c qemu-10.0.10+ds/hw/nvme/ns.c --- qemu-10.0.8+ds/hw/nvme/ns.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/nvme/ns.c 2026-05-25 22:03:52.000000000 +0000 @@ -73,7 +73,7 @@ ns->csi = NVME_CSI_NVM; ns->status = 0x0; - ns->id_ns.dlfeat = 0x1; + ns->id_ns.dlfeat = 0x9; /* support DULBE and I/O optimization fields */ id_ns->nsfeat |= (NVME_ID_NS_NSFEAT_DAE | NVME_ID_NS_NSFEAT_OPTPERF_ALL); diff -Nru qemu-10.0.8+ds/hw/ppc/e500.c qemu-10.0.10+ds/hw/ppc/e500.c --- qemu-10.0.8+ds/hw/ppc/e500.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/ppc/e500.c 2026-05-25 22:03:52.000000000 +0000 @@ -79,8 +79,6 @@ #define MPC85XX_ESDHC_IRQ 72 #define RTC_REGS_OFFSET 0x68 -#define PLATFORM_CLK_FREQ_HZ (400 * 1000 * 1000) - struct boot_info { uint32_t dt_base; @@ -120,7 +118,7 @@ } static void dt_serial_create(void *fdt, unsigned long long offset, - const char *soc, const char *mpic, + const char *soc, uint32_t freq, const char *mpic, const char *alias, int idx, bool defcon) { char *ser; @@ -131,7 +129,7 @@ qemu_fdt_setprop_string(fdt, ser, "compatible", "ns16550"); qemu_fdt_setprop_cells(fdt, ser, "reg", offset, 0x100); qemu_fdt_setprop_cell(fdt, ser, "cell-index", idx); - qemu_fdt_setprop_cell(fdt, ser, "clock-frequency", PLATFORM_CLK_FREQ_HZ); + qemu_fdt_setprop_cell(fdt, ser, "clock-frequency", freq); qemu_fdt_setprop_cells(fdt, ser, "interrupts", 42, 2); qemu_fdt_setprop_phandle(fdt, ser, "interrupt-parent", mpic); qemu_fdt_setprop_string(fdt, "/aliases", alias, ser); @@ -382,8 +380,7 @@ int fdt_size; void *fdt; uint8_t hypercall[16]; - uint32_t clock_freq = PLATFORM_CLK_FREQ_HZ; - uint32_t tb_freq = PLATFORM_CLK_FREQ_HZ; + uint32_t clock_freq, tb_freq; int i; char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; char *soc; @@ -484,6 +481,9 @@ if (kvmppc_get_hasidle(env)) { qemu_fdt_setprop(fdt, "/hypervisor", "has-idle", NULL, 0); } + } else { + clock_freq = pmc->clock_freq; + tb_freq = pmc->tb_freq; } /* Create CPU nodes */ @@ -516,7 +516,7 @@ env->icache_line_size); qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000); qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000); - qemu_fdt_setprop_cell(fdt, cpu_name, "bus-frequency", 0); + qemu_fdt_setprop_cell(fdt, cpu_name, "bus-frequency", pmc->bus_freq); if (cpu->cpu_index) { qemu_fdt_setprop_string(fdt, cpu_name, "status", "disabled"); qemu_fdt_setprop_string(fdt, cpu_name, "enable-method", @@ -564,12 +564,12 @@ */ if (serial_hd(1)) { dt_serial_create(fdt, MPC8544_SERIAL1_REGS_OFFSET, - soc, mpic, "serial1", 1, false); + soc, pmc->clock_freq, mpic, "serial1", 1, false); } if (serial_hd(0)) { dt_serial_create(fdt, MPC8544_SERIAL0_REGS_OFFSET, - soc, mpic, "serial0", 0, true); + soc, pmc->clock_freq, mpic, "serial0", 0, true); } /* i2c */ @@ -967,7 +967,7 @@ env->spr_cb[SPR_BOOKE_PIR].default_value = cs->cpu_index = i; env->mpic_iack = pmc->ccsrbar_base + MPC8544_MPIC_REGS_OFFSET + 0xa0; - ppc_booke_timers_init(cpu, PLATFORM_CLK_FREQ_HZ, PPC_TIMER_E500); + ppc_booke_timers_init(cpu, pmc->tb_freq, PPC_TIMER_E500); /* Register reset handler */ if (!i) { diff -Nru qemu-10.0.8+ds/hw/ppc/e500.h qemu-10.0.10+ds/hw/ppc/e500.h --- qemu-10.0.8+ds/hw/ppc/e500.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/ppc/e500.h 2026-05-25 22:03:52.000000000 +0000 @@ -5,6 +5,8 @@ #include "hw/platform-bus.h" #include "qom/object.h" +#define PLATFORM_CLK_FREQ_HZ (400 * 1000 * 1000) + struct PPCE500MachineState { /*< private >*/ MachineState parent_obj; @@ -37,6 +39,9 @@ hwaddr pci_mmio_base; hwaddr pci_mmio_bus_base; hwaddr spin_base; + uint32_t clock_freq; + uint32_t bus_freq; + uint32_t tb_freq; }; void ppce500_init(MachineState *machine); diff -Nru qemu-10.0.8+ds/hw/ppc/e500plat.c qemu-10.0.10+ds/hw/ppc/e500plat.c --- qemu-10.0.8+ds/hw/ppc/e500plat.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/ppc/e500plat.c 2026-05-25 22:03:52.000000000 +0000 @@ -93,6 +93,9 @@ pmc->pci_mmio_base = 0xC00000000ULL; pmc->pci_mmio_bus_base = 0xE0000000ULL; pmc->spin_base = 0xFEF000000ULL; + pmc->clock_freq = PLATFORM_CLK_FREQ_HZ; + pmc->bus_freq = PLATFORM_CLK_FREQ_HZ; + pmc->tb_freq = PLATFORM_CLK_FREQ_HZ; mc->desc = "generic paravirt e500 platform"; mc->init = e500plat_init; diff -Nru qemu-10.0.8+ds/hw/ppc/mpc8544ds.c qemu-10.0.10+ds/hw/ppc/mpc8544ds.c --- qemu-10.0.8+ds/hw/ppc/mpc8544ds.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/ppc/mpc8544ds.c 2026-05-25 22:03:52.000000000 +0000 @@ -55,6 +55,9 @@ pmc->pci_mmio_bus_base = 0xC0000000ULL; pmc->pci_pio_base = 0xE1000000ULL; pmc->spin_base = 0xEF000000ULL; + pmc->clock_freq = PLATFORM_CLK_FREQ_HZ; + pmc->bus_freq = PLATFORM_CLK_FREQ_HZ; + pmc->tb_freq = PLATFORM_CLK_FREQ_HZ; mc->desc = "mpc8544ds"; mc->init = mpc8544ds_init; diff -Nru qemu-10.0.8+ds/hw/ppc/pnv.c qemu-10.0.10+ds/hw/ppc/pnv.c --- qemu-10.0.8+ds/hw/ppc/pnv.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/ppc/pnv.c 2026-05-25 22:03:52.000000000 +0000 @@ -716,51 +716,12 @@ static void pnv_reset(MachineState *machine, ResetType type) { - PnvMachineState *pnv = PNV_MACHINE(machine); - IPMIBmc *bmc; void *fdt; qemu_devices_reset(type); - /* - * The machine should provide by default an internal BMC simulator. - * If not, try to use the BMC device that was provided on the command - * line. - */ - bmc = pnv_bmc_find(&error_fatal); - if (!pnv->bmc) { - if (!bmc) { - if (!qtest_enabled()) { - warn_report("machine has no BMC device. Use '-device " - "ipmi-bmc-sim,id=bmc0 -device isa-ipmi-bt,bmc=bmc0,irq=10' " - "to define one"); - } - } else { - pnv_bmc_set_pnor(bmc, pnv->pnor); - pnv->bmc = bmc; - } - } - - if (machine->fdt) { - fdt = machine->fdt; - } else { - fdt = pnv_dt_create(machine); - /* Pack resulting tree */ - _FDT((fdt_pack(fdt))); - } - + fdt = machine->fdt; cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt)); - - /* Update machine->fdt with latest fdt */ - if (machine->fdt != fdt) { - /* - * Set machine->fdt for 'dumpdtb' QMP/HMP command. Free - * the existing machine->fdt to avoid leaking it during - * a reset. - */ - g_free(machine->fdt); - machine->fdt = fdt; - } } static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp) @@ -934,6 +895,37 @@ return chip_id == 0 ? 1 * GiB : QEMU_ALIGN_DOWN(ram_per_chip, 1 * MiB); } +static void pnv_machine_init_done(Notifier *notifier, void *data) +{ + PnvMachineState *pnv = container_of(notifier, PnvMachineState, machine_init_done); + MachineState *machine = MACHINE(pnv); + IPMIBmc *bmc; + + /* + * The machine should provide by default an internal BMC simulator. + * If not, try to use the BMC device that was provided on the command + * line. + */ + bmc = pnv_bmc_find(&error_fatal); + if (!pnv->bmc) { + if (!bmc) { + if (!qtest_enabled()) { + warn_report("machine has no BMC device. Use '-device " + "ipmi-bmc-sim,id=bmc0 -device isa-ipmi-bt,bmc=bmc0,irq=10' " + "to define one"); + } + } else { + pnv_bmc_set_pnor(bmc, pnv->pnor); + pnv->bmc = bmc; + } + } + + if (!machine->fdt) { + machine->fdt = pnv_dt_create(machine); + _FDT((fdt_pack(machine->fdt))); + } +} + static void pnv_init(MachineState *machine) { const char *bios_name = machine->firmware ?: FW_FILE_NAME; @@ -1208,6 +1200,9 @@ if (pmc->i2c_init) { pmc->i2c_init(pnv); } + + pnv->machine_init_done.notify = pnv_machine_init_done; + qemu_add_machine_init_done_notifier(&pnv->machine_init_done); } /* diff -Nru qemu-10.0.8+ds/hw/riscv/virt-acpi-build.c qemu-10.0.10+ds/hw/riscv/virt-acpi-build.c --- qemu-10.0.8+ds/hw/riscv/virt-acpi-build.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/riscv/virt-acpi-build.c 2026-05-25 22:03:52.000000000 +0000 @@ -35,9 +35,11 @@ #include "hw/riscv/virt.h" #include "hw/riscv/numa.h" #include "hw/virtio/virtio-acpi.h" +#include "kvm/kvm_riscv.h" #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/error-report.h" +#include "system/kvm.h" #include "system/reset.h" #define ACPI_BUILD_TABLE_SIZE 0x20000 @@ -273,7 +275,10 @@ /* Time Base Frequency */ build_append_int_noprefix(table_data, - RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, 8); + kvm_enabled() ? + kvm_riscv_get_timebase_frequency(&s->soc->harts[0]) : + RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, + 8); /* ISA + N hart info */ num_rhct_nodes = 1 + ms->smp.cpus; diff -Nru qemu-10.0.8+ds/hw/s390x/s390-pci-inst.c qemu-10.0.10+ds/hw/s390x/s390-pci-inst.c --- qemu-10.0.8+ds/hw/s390x/s390-pci-inst.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/s390x/s390-pci-inst.c 2026-05-25 22:03:52.000000000 +0000 @@ -305,7 +305,7 @@ uint32_t data = pci_get_long(pbdev->pdev->config + PCI_BASE_ADDRESS_0 + (i * 4)); - stl_be_p(&resquery->bar[i], data); + stl_le_p(&resquery->bar[i], data); resquery->bar_size[i] = pbdev->pdev->io_regions[i].size ? ctz64(pbdev->pdev->io_regions[i].size) : 0; trace_s390_pci_bar(i, diff -Nru qemu-10.0.8+ds/hw/scsi/lsi53c895a.c qemu-10.0.10+ds/hw/scsi/lsi53c895a.c --- qemu-10.0.8+ds/hw/scsi/lsi53c895a.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/scsi/lsi53c895a.c 2026-05-25 22:03:52.000000000 +0000 @@ -197,6 +197,7 @@ uint8_t *dma_buf; uint32_t pending; int out; + bool orphan; QTAILQ_ENTRY(lsi_request) next; } lsi_request; @@ -626,6 +627,8 @@ uint32_t count; dma_addr_t addr; SCSIDevice *dev; + SCSIRequest *req; + lsi_request *p; if (!s->current || !s->current->dma_len) { /* Wait until data is available. */ @@ -633,12 +636,14 @@ return; } - dev = s->current->req->dev; + p = s->current; + req = scsi_req_ref(s->current->req); + dev = req->dev; assert(dev); count = s->dbc; - if (count > s->current->dma_len) - count = s->current->dma_len; + if (count > p->dma_len) + count = p->dma_len; addr = s->dnad; /* both 40 and Table Indirect 64-bit DMAs store upper bits in dnad64 */ @@ -653,21 +658,27 @@ s->csbc += count; s->dnad += count; s->dbc -= count; - if (s->current->dma_buf == NULL) { - s->current->dma_buf = scsi_req_get_buf(s->current->req); + if (p->dma_buf == NULL) { + p->dma_buf = scsi_req_get_buf(req); } /* ??? Set SFBR to first data byte. */ if (out) { - lsi_mem_read(s, addr, s->current->dma_buf, count); + lsi_mem_read(s, addr, p->dma_buf, count); } else { - lsi_mem_write(s, addr, s->current->dma_buf, count); + lsi_mem_write(s, addr, p->dma_buf, count); } - s->current->dma_len -= count; - if (s->current->dma_len == 0) { - s->current->dma_buf = NULL; - scsi_req_continue(s->current->req); + if (p->orphan) { + scsi_req_unref(req); + return; + } + scsi_req_unref(req); + + p->dma_len -= count; + if (p->dma_len == 0) { + p->dma_buf = NULL; + scsi_req_continue(req); } else { - s->current->dma_buf += count; + p->dma_buf += count; lsi_resume_script(s); } } @@ -743,14 +754,20 @@ return NULL; } -static void lsi_request_free(LSIState *s, lsi_request *p) +static void lsi_request_orphan(LSIState *s, lsi_request *p) { + p->orphan = true; if (p == s->current) { s->current = NULL; } else { QTAILQ_REMOVE(&s->queue, p, next); } - g_free(p); + scsi_req_unref(p->req); +} + +static void lsi_free_request(SCSIBus *bus, void *priv) +{ + g_free(priv); } static void lsi_request_cancelled(SCSIRequest *req) @@ -758,9 +775,7 @@ LSIState *s = LSI53C895A(req->bus->qbus.parent); lsi_request *p = req->hba_private; - req->hba_private = NULL; - lsi_request_free(s, p); - scsi_req_unref(req); + lsi_request_orphan(s, p); } /* Record that data is available for a queued command. Returns zero if @@ -812,9 +827,7 @@ } if (req->hba_private == s->current) { - req->hba_private = NULL; - lsi_request_free(s, s->current); - scsi_req_unref(req); + lsi_request_orphan(s, s->current); } if (!stop) { lsi_resume_script(s); @@ -825,10 +838,11 @@ static void lsi_transfer_data(SCSIRequest *req, uint32_t len) { LSIState *s = LSI53C895A(req->bus->qbus.parent); + lsi_request *p = req->hba_private; int out; - assert(req->hba_private); - if (s->waiting == LSI_WAIT_RESELECT || req->hba_private != s->current || + assert(!p->orphan); + if (s->waiting == LSI_WAIT_RESELECT || p != s->current || (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) { if (lsi_queue_req(s, req, len)) { return; @@ -1163,6 +1177,7 @@ s->waiting = LSI_NOWAIT; } + object_ref(s); reentrancy_level++; s->istat1 |= LSI_ISTAT1_SRUN; @@ -1182,6 +1197,7 @@ s->waiting = LSI_WAIT_SCRIPTS; lsi_scripts_timer_start(s); reentrancy_level--; + object_unref(s); return; } insn = read_dword(s, s->dsp); @@ -1630,6 +1646,7 @@ trace_lsi_execute_script_stop(); reentrancy_level--; + object_unref(s); } static uint8_t lsi_reg_readb(LSIState *s, int offset) @@ -1946,6 +1963,10 @@ CASE_SET_REG32(dsa, 0x10) case 0x14: /* ISTAT0 */ s->istat0 = (s->istat0 & 0x0f) | (val & 0xf0); + if (val & LSI_ISTAT0_SRST) { + device_cold_reset(DEVICE(s)); + return; + } if (val & LSI_ISTAT0_ABRT) { lsi_script_dma_interrupt(s, LSI_DSTAT_ABRT); } @@ -1959,9 +1980,6 @@ s->dsp = s->dnad; lsi_execute_script(s); } - if (val & LSI_ISTAT0_SRST) { - device_cold_reset(DEVICE(s)); - } break; case 0x16: /* MBOX0 */ s->mbox0 = val; @@ -2316,7 +2334,8 @@ .transfer_data = lsi_transfer_data, .complete = lsi_command_complete, - .cancel = lsi_request_cancelled + .cancel = lsi_request_cancelled, + .free_request = lsi_free_request, }; static void scripts_timer_cb(void *opaque) diff -Nru qemu-10.0.8+ds/hw/scsi/scsi-bus.c qemu-10.0.10+ds/hw/scsi/scsi-bus.c --- qemu-10.0.8+ds/hw/scsi/scsi-bus.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/scsi/scsi-bus.c 2026-05-25 22:03:52.000000000 +0000 @@ -482,12 +482,7 @@ Location loc; DriveInfo *dinfo; int unit; - BlockConf conf = { - .bootindex = -1, - .share_rw = false, - .rerror = BLOCKDEV_ON_ERROR_AUTO, - .werror = BLOCKDEV_ON_ERROR_AUTO, - }; + BlockConf conf = DEFAULT_BLOCK_CONF; loc_push_none(&loc); for (unit = 0; unit <= bus->info->max_target; unit++) { diff -Nru qemu-10.0.8+ds/hw/scsi/virtio-scsi.c qemu-10.0.10+ds/hw/scsi/virtio-scsi.c --- qemu-10.0.8+ds/hw/scsi/virtio-scsi.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/scsi/virtio-scsi.c 2026-05-25 22:03:52.000000000 +0000 @@ -231,16 +231,16 @@ return 0; } -static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq, QemuMutex *vq_lock) +static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq, size_t extra_req_size, + QemuMutex *vq_lock) { - VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s; VirtIOSCSIReq *req; if (vq_lock) { qemu_mutex_lock(vq_lock); } - req = virtqueue_pop(vq, sizeof(VirtIOSCSIReq) + vs->cdb_size); + req = virtqueue_pop(vq, sizeof(VirtIOSCSIReq) + extra_req_size); if (vq_lock) { qemu_mutex_unlock(vq_lock); @@ -686,7 +686,7 @@ { VirtIOSCSIReq *req; - while ((req = virtio_scsi_pop_req(s, vq, &s->ctrl_lock))) { + while ((req = virtio_scsi_pop_req(s, vq, 0, &s->ctrl_lock))) { virtio_scsi_handle_ctrl_req(s, req); } } @@ -854,13 +854,14 @@ virtio_scsi_complete_cmd_req(req); } -static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) +static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req, + size_t cdb_size) { VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); SCSIDevice *d; int rc; - rc = virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size, + rc = virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + cdb_size, sizeof(VirtIOSCSICmdResp) + vs->sense_size); if (rc < 0) { if (rc == -ENOTSUP) { @@ -882,7 +883,7 @@ } req->sreq = scsi_req_new(d, req->req.cmd.tag, virtio_scsi_get_lun(req->req.cmd.lun), - req->req.cmd.cdb, vs->cdb_size, req); + req->req.cmd.cdb, cdb_size, req); if (req->sreq->cmd.mode != SCSI_XFER_NONE && (req->sreq->cmd.mode != req->mode || @@ -917,12 +918,15 @@ QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); do { + VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s; + size_t cdb_size = qatomic_read(&vs->cdb_size); + if (suppress_notifications) { virtio_queue_set_notification(vq, 0); } - while ((req = virtio_scsi_pop_req(s, vq, NULL))) { - ret = virtio_scsi_handle_cmd_req_prepare(s, req); + while ((req = virtio_scsi_pop_req(s, vq, cdb_size, NULL))) { + ret = virtio_scsi_handle_cmd_req_prepare(s, req, cdb_size); if (!ret) { QTAILQ_INSERT_TAIL(&reqs, req, next); } else if (ret == -EINVAL) { @@ -993,7 +997,7 @@ } vs->sense_size = virtio_ldl_p(vdev, &scsiconf->sense_size); - vs->cdb_size = virtio_ldl_p(vdev, &scsiconf->cdb_size); + qatomic_set(&vs->cdb_size, virtio_ldl_p(vdev, &scsiconf->cdb_size)); } static uint64_t virtio_scsi_get_features(VirtIODevice *vdev, @@ -1054,7 +1058,7 @@ return; } - req = virtio_scsi_pop_req(s, vs->event_vq, &s->event_lock); + req = virtio_scsi_pop_req(s, vs->event_vq, 0, &s->event_lock); WITH_QEMU_LOCK_GUARD(&s->event_lock) { if (!req) { s->events_dropped = true; diff -Nru qemu-10.0.8+ds/hw/sh4/sh7750.c qemu-10.0.10+ds/hw/sh4/sh7750.c --- qemu-10.0.8+ds/hw/sh4/sh7750.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/sh4/sh7750.c 2026-05-25 22:03:52.000000000 +0000 @@ -687,7 +687,6 @@ break; case MM_ITLB_DATA: cpu_sh4_write_mmaped_itlb_data(&s->cpu->env, addr, mem_value); - abort(); break; case MM_OCACHE_ADDR: case MM_OCACHE_DATA: diff -Nru qemu-10.0.8+ds/hw/ssi/aspeed_smc.c qemu-10.0.10+ds/hw/ssi/aspeed_smc.c --- qemu-10.0.8+ds/hw/ssi/aspeed_smc.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/ssi/aspeed_smc.c 2026-05-25 22:03:52.000000000 +0000 @@ -493,17 +493,18 @@ } } -static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) +static MemTxResult aspeed_smc_flash_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, MemTxAttrs attrs) { AspeedSMCFlash *fl = opaque; AspeedSMCState *s = fl->controller; - uint64_t ret = 0; int i; + *data = 0; switch (aspeed_smc_flash_mode(fl)) { case CTRL_USERMODE: for (i = 0; i < size; i++) { - ret |= (uint64_t) ssi_transfer(s->spi, 0x0) << (8 * i); + *data |= (uint64_t) ssi_transfer(s->spi, 0x0) << (8 * i); } break; case CTRL_READMODE: @@ -512,18 +513,19 @@ aspeed_smc_flash_setup(fl, addr); for (i = 0; i < size; i++) { - ret |= (uint64_t) ssi_transfer(s->spi, 0x0) << (8 * i); + *data |= (uint64_t) ssi_transfer(s->spi, 0x0) << (8 * i); } aspeed_smc_flash_unselect(fl); break; default: aspeed_smc_error("invalid flash mode %d", aspeed_smc_flash_mode(fl)); + return MEMTX_ERROR; } - trace_aspeed_smc_flash_read(fl->cs, addr, size, ret, + trace_aspeed_smc_flash_read(fl->cs, addr, size, *data, aspeed_smc_flash_mode(fl)); - return ret; + return MEMTX_OK; } /* @@ -624,8 +626,8 @@ return false; } -static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, - unsigned size) +static MemTxResult aspeed_smc_flash_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size, MemTxAttrs attrs) { AspeedSMCFlash *fl = opaque; AspeedSMCState *s = fl->controller; @@ -636,7 +638,7 @@ if (!aspeed_smc_is_writable(fl)) { aspeed_smc_error("flash is not writable at 0x%" HWADDR_PRIx, addr); - return; + return MEMTX_ERROR; } switch (aspeed_smc_flash_mode(fl)) { @@ -661,12 +663,15 @@ break; default: aspeed_smc_error("invalid flash mode %d", aspeed_smc_flash_mode(fl)); + return MEMTX_ERROR; } + + return MEMTX_OK; } static const MemoryRegionOps aspeed_smc_flash_ops = { - .read = aspeed_smc_flash_read, - .write = aspeed_smc_flash_write, + .read_with_attrs = aspeed_smc_flash_read, + .write_with_attrs = aspeed_smc_flash_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, @@ -754,7 +759,8 @@ s->snoop_dummies = 0; } -static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) +static MemTxResult aspeed_smc_read(void *opaque, hwaddr addr, uint64_t *data, + unsigned int size, MemTxAttrs attrs) { AspeedSMCState *s = ASPEED_SMC(opaque); AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(opaque); @@ -782,12 +788,13 @@ trace_aspeed_smc_read(addr << 2, size, s->regs[addr]); - return s->regs[addr]; + *data = s->regs[addr]; } else { qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", __func__, addr); - return -1; + *data = -1; } + return MEMTX_OK; } static uint8_t aspeed_smc_hclk_divisor(uint8_t hclk_mask) @@ -1108,8 +1115,8 @@ s->regs[R_DMA_CTRL] &= ~(DMA_CTRL_REQUEST | DMA_CTRL_GRANT); } -static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) +static MemTxResult aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size, MemTxAttrs attrs) { AspeedSMCState *s = ASPEED_SMC(opaque); AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); @@ -1159,13 +1166,13 @@ } else { qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", __func__, addr); - return; } + return MEMTX_OK; } static const MemoryRegionOps aspeed_smc_ops = { - .read = aspeed_smc_read, - .write = aspeed_smc_write, + .read_with_attrs = aspeed_smc_read, + .write_with_attrs = aspeed_smc_write, .endianness = DEVICE_LITTLE_ENDIAN, }; @@ -2007,8 +2014,8 @@ }; static const MemoryRegionOps aspeed_2700_smc_flash_ops = { - .read = aspeed_smc_flash_read, - .write = aspeed_smc_flash_write, + .read_with_attrs = aspeed_smc_flash_read, + .write_with_attrs = aspeed_smc_flash_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, diff -Nru qemu-10.0.8+ds/hw/ssi/xilinx_spips.c qemu-10.0.10+ds/hw/ssi/xilinx_spips.c --- qemu-10.0.8+ds/hw/ssi/xilinx_spips.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/ssi/xilinx_spips.c 2026-05-25 22:03:52.000000000 +0000 @@ -369,7 +369,7 @@ memset(s->regs, 0, sizeof(s->regs)); fifo8_reset(&s->rx_fifo); - fifo8_reset(&s->rx_fifo); + fifo8_reset(&s->tx_fifo); /* non zero resets */ s->regs[R_CONFIG] |= MODEFAIL_GEN_EN; s->regs[R_SLAVE_IDLE_COUNT] = 0xFF; @@ -397,7 +397,7 @@ memset(s->regs, 0, sizeof(s->regs)); fifo8_reset(&s->rx_fifo_g); - fifo8_reset(&s->rx_fifo_g); + fifo8_reset(&s->tx_fifo_g); fifo32_reset(&s->fifo_g); s->regs[R_INTR_STATUS] = R_INTR_STATUS_RESET; s->regs[R_GPIO] = 1; diff -Nru qemu-10.0.8+ds/hw/uefi/var-service-auth.c qemu-10.0.10+ds/hw/uefi/var-service-auth.c --- qemu-10.0.8+ds/hw/uefi/var-service-auth.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/uefi/var-service-auth.c 2026-05-25 22:03:52.000000000 +0000 @@ -180,9 +180,10 @@ void *data, uint64_t data_offset) { - variable_auth_2 *auth = data; + variable_auth_2 auth; uefi_variable *siglist; + memcpy(&auth, data, sizeof(auth)); if (custom_mode_is_active(uv)) { /* no authentication in custom mode */ return EFI_SUCCESS; @@ -193,7 +194,7 @@ return EFI_SUCCESS; } - if (auth->hdr_length == 24) { + if (auth.hdr_length == (sizeof(auth) - sizeof(auth.timestamp))) { /* no signature (auth->cert_data is empty) */ return EFI_SECURITY_VIOLATION; } @@ -218,23 +219,28 @@ efi_status uefi_vars_check_auth_2(uefi_vars_state *uv, uefi_variable *var, mm_variable_access *va, void *data) { - variable_auth_2 *auth = data; + variable_auth_2 auth; uint64_t data_offset; efi_status status; - if (va->data_size < sizeof(*auth)) { + if (va->data_size < sizeof(auth)) { return EFI_SECURITY_VIOLATION; } - if (uadd64_overflow(sizeof(efi_time), auth->hdr_length, &data_offset)) { + memcpy(&auth, data, sizeof(auth)); + + if (auth.hdr_length < (sizeof(auth) - sizeof(auth.timestamp))) { + return EFI_SECURITY_VIOLATION; + } + if (uadd64_overflow(sizeof(efi_time), auth.hdr_length, &data_offset)) { return EFI_SECURITY_VIOLATION; } if (va->data_size < data_offset) { return EFI_SECURITY_VIOLATION; } - if (auth->hdr_revision != 0x0200 || - auth->hdr_cert_type != WIN_CERT_TYPE_EFI_GUID || - !qemu_uuid_is_equal(&auth->guid_cert_type, &EfiCertTypePkcs7Guid)) { + if (auth.hdr_revision != 0x0200 || + auth.hdr_cert_type != WIN_CERT_TYPE_EFI_GUID || + !qemu_uuid_is_equal(&auth.guid_cert_type, &EfiCertTypePkcs7Guid)) { return EFI_UNSUPPORTED; } @@ -255,7 +261,7 @@ } /* checks passed, set variable data */ - var->time = auth->timestamp; + var->time = auth.timestamp; if (va->data_size - data_offset > 0) { var->data = g_malloc(va->data_size - data_offset); memcpy(var->data, data + data_offset, va->data_size - data_offset); diff -Nru qemu-10.0.8+ds/hw/uefi/var-service-core.c qemu-10.0.10+ds/hw/uefi/var-service-core.c --- qemu-10.0.8+ds/hw/uefi/var-service-core.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/uefi/var-service-core.c 2026-05-25 22:03:52.000000000 +0000 @@ -133,9 +133,8 @@ uv->buffer, sizeof(*mhdr) + mhdr->length, MEMTXATTRS_UNSPECIFIED); } else { - memcpy(uv->pio_xfer_buffer + sizeof(*mhdr), - uv->buffer + sizeof(*mhdr), - sizeof(*mhdr) + mhdr->length); + memcpy(uv->pio_xfer_buffer, + uv->buffer, sizeof(*mhdr) + mhdr->length); } return retval; @@ -230,6 +229,10 @@ uv->pio_xfer_offset += size; break; case UEFI_VARS_REG_PIO_BUFFER_CRC32C: + if (uv->pio_xfer_offset > uv->buf_size) { + retval = 0; + break; + } retval = crc32c(0xffffffff, uv->pio_xfer_buffer, uv->pio_xfer_offset); break; case UEFI_VARS_REG_FLAGS: diff -Nru qemu-10.0.8+ds/hw/uefi/var-service-pkcs7.c qemu-10.0.10+ds/hw/uefi/var-service-pkcs7.c --- qemu-10.0.8+ds/hw/uefi/var-service-pkcs7.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/uefi/var-service-pkcs7.c 2026-05-25 22:03:52.000000000 +0000 @@ -21,17 +21,20 @@ */ static gnutls_datum_t *build_signed_data(mm_variable_access *va, void *data) { - variable_auth_2 *auth = data; - uint64_t data_offset = sizeof(efi_time) + auth->hdr_length; + variable_auth_2 auth; + uint64_t data_offset; uint16_t *name = (void *)va + sizeof(mm_variable_access); gnutls_datum_t *sdata; uint64_t pos = 0; + memcpy(&auth, data, sizeof(auth)); + data_offset = sizeof(efi_time) + auth.hdr_length; + sdata = g_new(gnutls_datum_t, 1); sdata->size = (va->name_size - 2 + sizeof(QemuUUID) + sizeof(va->attributes) - + sizeof(auth->timestamp) + + sizeof(auth.timestamp) + va->data_size - data_offset); sdata->data = g_malloc(sdata->size); @@ -48,8 +51,8 @@ pos += sizeof(va->attributes); /* TimeStamp */ - memcpy(sdata->data + pos, &auth->timestamp, sizeof(auth->timestamp)); - pos += sizeof(auth->timestamp); + memcpy(sdata->data + pos, &auth.timestamp, sizeof(auth.timestamp)); + pos += sizeof(auth.timestamp); /* Variable Content */ memcpy(sdata->data + pos, data + data_offset, va->data_size - data_offset); @@ -73,7 +76,8 @@ }; gnutls_datum_t wrap; - if (pkcs7->data[4] == 0x06 && + if (pkcs7->size > 16 && + pkcs7->data[4] == 0x06 && pkcs7->data[5] == 0x09 && memcmp(pkcs7->data + 6, signed_data_oid, sizeof(signed_data_oid)) == 0 && pkcs7->data[15] == 0x0a && @@ -104,13 +108,14 @@ static gnutls_datum_t *build_pkcs7(void *data) { - variable_auth_2 *auth = data; + variable_auth_2 auth; gnutls_datum_t *pkcs7; + memcpy(&auth, data, sizeof(auth)); pkcs7 = g_new(gnutls_datum_t, 1); - pkcs7->size = auth->hdr_length - 24; + pkcs7->size = auth.hdr_length - (sizeof(auth) - sizeof(auth.timestamp)); pkcs7->data = g_malloc(pkcs7->size); - memcpy(pkcs7->data, data + 16 + 24, pkcs7->size); + memcpy(pkcs7->data, data + sizeof(auth), pkcs7->size); wrap_pkcs7(pkcs7); diff -Nru qemu-10.0.8+ds/hw/uefi/var-service-utils.c qemu-10.0.10+ds/hw/uefi/var-service-utils.c --- qemu-10.0.8+ds/hw/uefi/var-service-utils.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/uefi/var-service-utils.c 2026-05-25 22:03:52.000000000 +0000 @@ -19,13 +19,18 @@ * sometimes when they are not (for example in variable policies). */ -gboolean uefi_str_is_valid(const uint16_t *str, size_t len, +gboolean uefi_str_is_valid(const uint16_t *str, size_t bytes, gboolean must_be_null_terminated) { + size_t chars = bytes / 2; size_t pos = 0; + if ((bytes % 2) != 0) { + return false; + } + for (;;) { - if (pos == len) { + if (pos == chars) { if (must_be_null_terminated) { return false; } else { @@ -47,12 +52,13 @@ } } -size_t uefi_strlen(const uint16_t *str, size_t len) +size_t uefi_strlen(const uint16_t *str, size_t bytes) { + size_t chars = bytes / 2; size_t pos = 0; for (;;) { - if (pos == len) { + if (pos == chars) { return pos; } if (str[pos] == 0) { @@ -62,25 +68,25 @@ } } -gboolean uefi_str_equal_ex(const uint16_t *a, size_t alen, - const uint16_t *b, size_t blen, +gboolean uefi_str_equal_ex(const uint16_t *a, size_t a_bytes, + const uint16_t *b, size_t b_bytes, gboolean wildcards_in_a) { + size_t a_chars = a_bytes / 2; + size_t b_chars = b_bytes / 2; size_t pos = 0; - alen = alen / 2; - blen = blen / 2; for (;;) { - if (pos == alen && pos == blen) { + if (pos == a_chars && pos == b_chars) { return true; } - if (pos == alen && b[pos] == 0) { + if (pos == a_chars && b[pos] == 0) { return true; } - if (pos == blen && a[pos] == 0) { + if (pos == b_chars && a[pos] == 0) { return true; } - if (pos == alen || pos == blen) { + if (pos == a_chars || pos == b_chars) { return false; } if (a[pos] == 0 && b[pos] == 0) { @@ -100,18 +106,18 @@ } } -gboolean uefi_str_equal(const uint16_t *a, size_t alen, - const uint16_t *b, size_t blen) +gboolean uefi_str_equal(const uint16_t *a, size_t a_bytes, + const uint16_t *b, size_t b_bytes) { - return uefi_str_equal_ex(a, alen, b, blen, false); + return uefi_str_equal_ex(a, a_bytes, b, b_bytes, false); } -char *uefi_ucs2_to_ascii(const uint16_t *ucs2, uint64_t ucs2_size) +char *uefi_ucs2_to_ascii(const uint16_t *ucs2, uint64_t ucs2_bytes) { - char *str = g_malloc0(ucs2_size / 2 + 1); + char *str = g_malloc0(ucs2_bytes / 2 + 1); int i; - for (i = 0; i * 2 < ucs2_size; i++) { + for (i = 0; i * 2 < ucs2_bytes; i++) { if (ucs2[i] == 0) { break; } diff -Nru qemu-10.0.8+ds/hw/uefi/var-service-vars.c qemu-10.0.10+ds/hw/uefi/var-service-vars.c --- qemu-10.0.8+ds/hw/uefi/var-service-vars.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/uefi/var-service-vars.c 2026-05-25 22:03:52.000000000 +0000 @@ -260,6 +260,17 @@ return sizeof(*mvar); } +static bool check_buffer_size(uefi_vars_state *uv, uint64_t length) +{ + /* uefi_vars_cmd_mm() checks that */ + g_assert(uv->buf_size >= sizeof(mm_header)); + + if (uv->buf_size - sizeof(mm_header) < length) { + return false; + } + return true; +} + static size_t uefi_vars_mm_get_variable(uefi_vars_state *uv, mm_header *mhdr, mm_variable *mvar, void *func) { @@ -307,7 +318,7 @@ if (uadd64_overflow(length, va->data_size, &length)) { return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); } - if (uv->buf_size < length) { + if (!check_buffer_size(uv, length)) { return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); } @@ -377,7 +388,7 @@ } length = sizeof(*mvar) + sizeof(*nv) + var->name_size; - if (uv->buf_size < length) { + if (!check_buffer_size(uv, length)) { return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); } @@ -567,7 +578,7 @@ uint64_t length; length = sizeof(*mvar) + sizeof(*vi); - if (uv->buf_size < length) { + if (!check_buffer_size(uv, length)) { return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); } @@ -588,7 +599,7 @@ uint64_t length; length = sizeof(*mvar) + sizeof(*ps); - if (uv->buf_size < length) { + if (!check_buffer_size(uv, length)) { return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); } @@ -618,6 +629,9 @@ if (mhdr->length < length) { return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); } + if (sizeof(*pe) + lv->name_size > UINT16_MAX) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } uefi_trace_variable(__func__, lv->guid, name, lv->name_size); diff -Nru qemu-10.0.8+ds/hw/ufs/trace-events qemu-10.0.10+ds/hw/ufs/trace-events --- qemu-10.0.8+ds/hw/ufs/trace-events 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/ufs/trace-events 2026-05-25 22:03:52.000000000 +0000 @@ -40,10 +40,13 @@ ufs_err_mcq_db_wr_invalid_db(uint8_t qid, uint32_t db) "invalid mcq doorbell sqid %"PRIu8", db %"PRIu32"" ufs_err_mcq_create_sq_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" ufs_err_mcq_create_sq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" +ufs_err_mcq_create_sq_invalid_size(uint8_t qid) "invalid mcq sq size for sqid %"PRIu8"" ufs_err_mcq_create_sq_already_exists(uint8_t qid) "mcq sqid %"PRIu8 "already exists" ufs_err_mcq_delete_sq_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" ufs_err_mcq_delete_sq_not_exists(uint8_t qid) "mcq sqid %"PRIu8 "not exists" +ufs_err_mcq_delete_sq_busy(uint8_t qid) "mcq sqid %"PRIu8" has outstanding requests" ufs_err_mcq_create_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" +ufs_err_mcq_create_cq_invalid_size(uint8_t qid) "invalid mcq cq size for cqid %"PRIu8"" ufs_err_mcq_create_cq_already_exists(uint8_t qid) "mcq cqid %"PRIu8 "already exists" ufs_err_mcq_delete_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" ufs_err_mcq_delete_cq_not_exists(uint8_t qid) "mcq cqid %"PRIu8 "not exists" diff -Nru qemu-10.0.8+ds/hw/ufs/ufs.c qemu-10.0.10+ds/hw/ufs/ufs.c --- qemu-10.0.8+ds/hw/ufs/ufs.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/ufs/ufs.c 2026-05-25 22:03:52.000000000 +0000 @@ -506,6 +506,8 @@ UfsMcqReg *reg = &u->mcq_reg[qid]; UfsSq *sq; uint8_t cqid = FIELD_EX32(attr, SQATTR, CQID); + uint16_t qsize = + ((FIELD_EX32(attr, SQATTR, SIZE) + 1) << 2) / sizeof(UfsSqEntry); if (qid >= u->params.mcq_maxq) { trace_ufs_err_mcq_create_sq_invalid_sqid(qid); @@ -517,8 +519,18 @@ return false; } + if (cqid >= u->params.mcq_maxq) { + trace_ufs_err_mcq_create_sq_invalid_cqid(cqid); + return false; + } + if (!u->cq[cqid]) { - trace_ufs_err_mcq_create_sq_invalid_cqid(qid); + trace_ufs_err_mcq_create_sq_invalid_cqid(cqid); + return false; + } + + if (!qsize) { + trace_ufs_err_mcq_create_sq_invalid_size(qid); return false; } @@ -527,7 +539,7 @@ sq->sqid = qid; sq->cq = u->cq[cqid]; sq->addr = ((uint64_t)reg->squba << 32) | reg->sqlba; - sq->size = ((FIELD_EX32(attr, SQATTR, SIZE) + 1) << 2) / sizeof(UfsSqEntry); + sq->size = qsize; sq->bh = qemu_bh_new_guarded(ufs_mcq_process_sq, sq, &DEVICE(u)->mem_reentrancy_guard); @@ -544,6 +556,31 @@ return true; } +static bool ufs_mcq_sq_has_outstanding_req(UfsSq *sq) +{ + UfsRequest *req; + uint16_t free_reqs = 0; + + QTAILQ_FOREACH(req, &sq->req_list, entry) + { + free_reqs++; + } + + return free_reqs != sq->size; +} + +static void ufs_mcq_free_sq(UfsSq *sq) +{ + qemu_bh_delete(sq->bh); + + for (int i = 0; i < sq->size; i++) { + ufs_clear_req(&sq->req[i]); + } + + g_free(sq->req); + g_free(sq); +} + static bool ufs_mcq_delete_sq(UfsHc *u, uint8_t qid) { UfsSq *sq; @@ -560,9 +597,12 @@ sq = u->sq[qid]; - qemu_bh_delete(sq->bh); - g_free(sq->req); - g_free(sq); + if (ufs_mcq_sq_has_outstanding_req(sq)) { + trace_ufs_err_mcq_delete_sq_busy(qid); + return false; + } + + ufs_mcq_free_sq(sq); u->sq[qid] = NULL; return true; } @@ -571,6 +611,8 @@ { UfsMcqReg *reg = &u->mcq_reg[qid]; UfsCq *cq; + uint16_t qsize = + ((FIELD_EX32(attr, CQATTR, SIZE) + 1) << 2) / sizeof(UfsCqEntry); if (qid >= u->params.mcq_maxq) { trace_ufs_err_mcq_create_cq_invalid_cqid(qid); @@ -582,11 +624,16 @@ return false; } + if (!qsize) { + trace_ufs_err_mcq_create_cq_invalid_size(qid); + return false; + } + cq = g_malloc0(sizeof(*cq)); cq->u = u; cq->cqid = qid; cq->addr = ((uint64_t)reg->cquba << 32) | reg->cqlba; - cq->size = ((FIELD_EX32(attr, CQATTR, SIZE) + 1) << 2) / sizeof(UfsCqEntry); + cq->size = qsize; cq->bh = qemu_bh_new_guarded(ufs_mcq_process_cq, cq, &DEVICE(u)->mem_reentrancy_guard); @@ -598,6 +645,12 @@ return true; } +static void ufs_mcq_free_cq(UfsCq *cq) +{ + qemu_bh_delete(cq->bh); + g_free(cq); +} + static bool ufs_mcq_delete_cq(UfsHc *u, uint8_t qid) { UfsCq *cq; @@ -621,8 +674,7 @@ cq = u->cq[qid]; - qemu_bh_delete(cq->bh); - g_free(cq); + ufs_mcq_free_cq(cq); u->cq[qid] = NULL; return true; } @@ -775,6 +827,11 @@ } sq = u->sq[qid]; + if (!sq) { + trace_ufs_err_mcq_db_wr_invalid_sqid(qid); + return; + } + if (sq->size * sizeof(UfsSqEntry) <= db) { trace_ufs_err_mcq_db_wr_invalid_db(qid, db); return; @@ -788,7 +845,14 @@ unsigned size) { int qid = offset / sizeof(UfsMcqOpReg); - UfsMcqOpReg *opr = &u->mcq_op_reg[qid]; + UfsMcqOpReg *opr; + + if (qid >= u->params.mcq_maxq) { + trace_ufs_err_invalid_register_offset(offset); + return; + } + + opr = &u->mcq_op_reg[qid]; switch (offset % sizeof(UfsMcqOpReg)) { case offsetof(UfsMcqOpReg, sq.tp): @@ -800,6 +864,10 @@ case offsetof(UfsMcqOpReg, cq.hp): { UfsCq *cq = u->cq[qid]; + if (!cq) { + break; + } + if (ufs_mcq_cq_full(u, qid) && !QTAILQ_EMPTY(&cq->req_list)) { /* Enqueueing to CQ was blocked because it was full */ qemu_bh_schedule(cq->bh); @@ -1849,12 +1917,14 @@ for (int i = 0; i < ARRAY_SIZE(u->sq); i++) { if (u->sq[i]) { - ufs_mcq_delete_sq(u, i); + ufs_mcq_free_sq(u->sq[i]); + u->sq[i] = NULL; } } for (int i = 0; i < ARRAY_SIZE(u->cq); i++) { if (u->cq[i]) { - ufs_mcq_delete_cq(u, i); + ufs_mcq_free_cq(u->cq[i]); + u->cq[i] = NULL; } } } diff -Nru qemu-10.0.8+ds/hw/ufs/ufs.h qemu-10.0.10+ds/hw/ufs/ufs.h --- qemu-10.0.8+ds/hw/ufs/ufs.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/ufs/ufs.h 2026-05-25 22:03:52.000000000 +0000 @@ -203,7 +203,14 @@ static inline bool ufs_mcq_cq_full(UfsHc *u, uint32_t qid) { uint32_t tail = ufs_mcq_cq_tail(u, qid); - uint16_t cq_size = u->cq[qid]->size; + UfsCq *cq = u->cq[qid]; + uint16_t cq_size; + + if (!cq) { + return false; + } + + cq_size = cq->size; tail = (tail + sizeof(UfsCqEntry)) % (sizeof(UfsCqEntry) * cq_size); return tail == ufs_mcq_cq_head(u, qid); diff -Nru qemu-10.0.8+ds/hw/usb/hcd-ohci.c qemu-10.0.10+ds/hw/usb/hcd-ohci.c --- qemu-10.0.8+ds/hw/usb/hcd-ohci.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/usb/hcd-ohci.c 2026-05-25 22:03:52.000000000 +0000 @@ -956,6 +956,17 @@ if (len && dir != OHCI_TD_DIR_IN) { /* The endpoint may not allow us to transfer it all now */ pktlen = (ed->flags & OHCI_ED_MPS_MASK) >> OHCI_ED_MPS_SHIFT; + /* + * The OHCI spec does not say what to do if the guest hands us + * an endpoint descriptor which specifies a MaximumPacketSize + * of zero, which would mean we can never actually make forward + * progress transferring data to it. We choose to treat it as + * an error. + */ + if (pktlen == 0) { + ohci_die(ohci); + return 1; + } if (pktlen > len) { pktlen = len; } diff -Nru qemu-10.0.8+ds/hw/virtio/virtio.c qemu-10.0.10+ds/hw/virtio/virtio.c --- qemu-10.0.8+ds/hw/virtio/virtio.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/hw/virtio/virtio.c 2026-05-25 22:03:52.000000000 +0000 @@ -4403,3 +4403,13 @@ return qemu_bh_new_full(cb, opaque, name, &transport->mem_reentrancy_guard); } + +QEMUBH *virtio_bh_io_new_guarded_full(DeviceState *dev, + QEMUBHFunc *cb, void *opaque, + const char *name) +{ + DeviceState *transport = qdev_get_parent_bus(dev)->parent; + + return aio_bh_new_full(iohandler_get_aio_context(), cb, opaque, name, + &transport->mem_reentrancy_guard); +} diff -Nru qemu-10.0.8+ds/include/block/block-common.h qemu-10.0.10+ds/include/block/block-common.h --- qemu-10.0.8+ds/include/block/block-common.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/include/block/block-common.h 2026-05-25 22:03:52.000000000 +0000 @@ -215,8 +215,17 @@ */ BDRV_REQ_NO_WAIT = 0x400, + /* + * Used between blk_co_start_request() and blk_end_request() to avoid + * that the request waits in a drained BlockBackend until the drained + * section ends. Waiting would cause a deadlock because drain waits for + * blk_end_request() to be called, but the request never completes + * because it waits for the drain to end. + */ + BDRV_REQ_NO_QUEUE = 0x800, + /* Mask of valid flags */ - BDRV_REQ_MASK = 0x7ff, + BDRV_REQ_MASK = 0xfff, } BdrvRequestFlags; #define BDRV_O_NO_SHARE 0x0001 /* don't share permissions */ diff -Nru qemu-10.0.8+ds/include/block/throttle-groups.h qemu-10.0.10+ds/include/block/throttle-groups.h --- qemu-10.0.8+ds/include/block/throttle-groups.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/include/block/throttle-groups.h 2026-05-25 22:03:52.000000000 +0000 @@ -35,8 +35,7 @@ typedef struct ThrottleGroupMember { AioContext *aio_context; - /* throttled_reqs_lock protects the CoQueues for throttled requests. */ - CoMutex throttled_reqs_lock; + /* Protected by ThrottleGroup.lock */ CoQueue throttled_reqs[THROTTLE_MAX]; /* Nonzero if the I/O limits are currently being ignored; generally diff -Nru qemu-10.0.8+ds/include/hw/block/block.h qemu-10.0.10+ds/include/hw/block/block.h --- qemu-10.0.8+ds/include/hw/block/block.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/include/hw/block/block.h 2026-05-25 22:03:52.000000000 +0000 @@ -49,6 +49,18 @@ return exp; } +#define DEFAULT_BLOCK_CONF (BlockConf) { \ + .bootindex = -1, \ + .backend_defaults = ON_OFF_AUTO_AUTO, \ + .discard_granularity = -1, \ + .wce = ON_OFF_AUTO_AUTO, \ + .share_rw = false, \ + .account_invalid = ON_OFF_AUTO_AUTO, \ + .account_failed = ON_OFF_AUTO_AUTO, \ + .rerror = BLOCKDEV_ON_ERROR_AUTO, \ + .werror = BLOCKDEV_ON_ERROR_AUTO, \ +} + #define DEFINE_BLOCK_PROPERTIES_BASE(_state, _conf) \ DEFINE_PROP_ON_OFF_AUTO("backend_defaults", _state, \ _conf.backend_defaults, ON_OFF_AUTO_AUTO), \ diff -Nru qemu-10.0.8+ds/include/hw/i2c/aspeed_i2c.h qemu-10.0.10+ds/include/hw/i2c/aspeed_i2c.h --- qemu-10.0.8+ds/include/hw/i2c/aspeed_i2c.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/include/hw/i2c/aspeed_i2c.h 2026-05-25 22:03:52.000000000 +0000 @@ -37,8 +37,7 @@ #define ASPEED_I2C_NR_BUSSES 16 #define ASPEED_I2C_SHARE_POOL_SIZE 0x800 #define ASPEED_I2C_BUS_POOL_SIZE 0x20 -#define ASPEED_I2C_OLD_NUM_REG 11 -#define ASPEED_I2C_NEW_NUM_REG 28 +#define ASPEED_I2C_NEW_NUM_REG (0x80 >> 2) #define A_I2CD_M_STOP_CMD BIT(5) #define A_I2CD_M_RX_CMD BIT(3) diff -Nru qemu-10.0.8+ds/include/hw/net/npcm_gmac.h qemu-10.0.10+ds/include/hw/net/npcm_gmac.h --- qemu-10.0.8+ds/include/hw/net/npcm_gmac.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/include/hw/net/npcm_gmac.h 2026-05-25 22:03:52.000000000 +0000 @@ -24,7 +24,8 @@ #include "hw/sysbus.h" #include "net/net.h" -#define NPCM_GMAC_NR_REGS (0x1060 / sizeof(uint32_t)) +#define NPCM_GMAC_REG_SIZE 0x1060 +#define NPCM_GMAC_NR_REGS (NPCM_GMAC_REG_SIZE / sizeof(uint32_t)) #define NPCM_GMAC_MAX_PHYS 32 #define NPCM_GMAC_MAX_PHY_REGS 32 diff -Nru qemu-10.0.8+ds/include/hw/ppc/pnv.h qemu-10.0.10+ds/include/hw/ppc/pnv.h --- qemu-10.0.8+ds/include/hw/ppc/pnv.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/include/hw/ppc/pnv.h 2026-05-25 22:03:52.000000000 +0000 @@ -106,6 +106,8 @@ bool big_core; bool lpar_per_core; + + Notifier machine_init_done; }; PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id); diff -Nru qemu-10.0.8+ds/include/hw/virtio/virtio.h qemu-10.0.10+ds/include/hw/virtio/virtio.h --- qemu-10.0.8+ds/include/hw/virtio/virtio.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/include/hw/virtio/virtio.h 2026-05-25 22:03:52.000000000 +0000 @@ -543,4 +543,14 @@ #define virtio_bh_new_guarded(dev, cb, opaque) \ virtio_bh_new_guarded_full((dev), (cb), (opaque), (stringify(cb))) +/* + * The "_io" variant runs BH only on a main-loop thread, while generic BH + * may run on a vCPU thread. + */ +QEMUBH *virtio_bh_io_new_guarded_full(DeviceState *dev, + QEMUBHFunc *cb, void *opaque, + const char *name); +#define virtio_bh_io_new_guarded(dev, cb, opaque) \ + virtio_bh_io_new_guarded_full((dev), (cb), (opaque), (stringify(cb))) + #endif diff -Nru qemu-10.0.8+ds/include/io/task.h qemu-10.0.10+ds/include/io/task.h --- qemu-10.0.8+ds/include/io/task.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/include/io/task.h 2026-05-25 22:03:52.000000000 +0000 @@ -96,7 +96,7 @@ * 1000, * myobject_operation_timer, * task, - * NULL); + * qio_task_free); * } * * @@ -138,9 +138,8 @@ * the callback func 'myobject_operation_notify' shown * earlier to deal with the results. * - * Once this function returns false, object_unref will be called - * automatically on the task causing it to be released and the - * ref on QMyObject dropped too. + * Once this function returns FALSE, the task will be freed, + * causing it release the ref on QMyObject too. * * The QIOTask module can also be used to perform operations * in a background thread context, while still reporting the @@ -208,8 +207,8 @@ * 'err' attribute in the task object to determine if * the operation was successful or not. * - * The returned task will be released when qio_task_complete() - * is invoked. + * The returned task must be released by calling + * qio_task_free() when no longer required. * * Returns: the task struct */ @@ -219,6 +218,19 @@ GDestroyNotify destroy); /** + * qio_task_free: + * task: the task object to free + * + * Free the resources associated with the task. Typically + * the qio_task_complete() method will be called immediately + * before this to trigger the task callback, however, it is + * permissible to free the task in the case of cancellation. + * The destroy callback will be used to release the opaque + * data provided to qio_task_new(). + */ +void qio_task_free(QIOTask *task); + +/** * qio_task_run_in_thread: * @task: the task struct * @worker: the function to invoke in a thread @@ -268,8 +280,9 @@ * qio_task_complete: * @task: the task struct * - * Invoke the completion callback for @task and - * then free its memory. + * Invoke the completion callback for @task. This should typically + * only be invoked once on a task, and then qio_task_free() used + * to free it. */ void qio_task_complete(QIOTask *task); diff -Nru qemu-10.0.8+ds/include/system/block-backend-io.h qemu-10.0.10+ds/include/system/block-backend-io.h --- qemu-10.0.8+ds/include/system/block-backend-io.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/include/system/block-backend-io.h 2026-05-25 22:03:52.000000000 +0000 @@ -71,6 +71,8 @@ void blk_inc_in_flight(BlockBackend *blk); void blk_dec_in_flight(BlockBackend *blk); +void coroutine_fn blk_co_start_request(BlockBackend *blk); +void blk_end_request(BlockBackend *blk); bool coroutine_fn GRAPH_RDLOCK blk_co_is_inserted(BlockBackend *blk); bool co_wrapper_mixed_bdrv_rdlock blk_is_inserted(BlockBackend *blk); @@ -215,9 +217,9 @@ BdrvRequestFlags flags); int co_wrapper_mixed blk_pdiscard(BlockBackend *blk, int64_t offset, - int64_t bytes); + int64_t bytes, BdrvRequestFlags flags); int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, - int64_t bytes); + int64_t bytes, BdrvRequestFlags flags); int co_wrapper_mixed blk_flush(BlockBackend *blk); int coroutine_fn blk_co_flush(BlockBackend *blk); diff -Nru qemu-10.0.8+ds/io/channel-tls.c qemu-10.0.10+ds/io/channel-tls.c --- qemu-10.0.8+ds/io/channel-tls.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/io/channel-tls.c 2026-05-25 22:03:52.000000000 +0000 @@ -153,13 +153,32 @@ }; typedef struct QIOChannelTLSData QIOChannelTLSData; +static void qio_channel_tls_io_data_free(gpointer user_data) +{ + QIOChannelTLSData *data = user_data; + /* + * Usually 'task' will be NULL since the GSource + * callback will either complete the task or pass + * it on to a new GSource. We'll see a non-NULL + * task here only if the GSource was released before + * its callback triggers + */ + if (data->task) { + qio_task_free(data->task); + } + if (data->context) { + g_main_context_unref(data->context); + } + g_free(data); +} + static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc, GIOCondition condition, gpointer user_data); -static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, - QIOTask *task, - GMainContext *context) +static gboolean qio_channel_tls_handshake_task(QIOChannelTLS *ioc, + QIOTask *task, + GMainContext *context) { Error *err = NULL; int status; @@ -170,7 +189,7 @@ trace_qio_channel_tls_handshake_fail(ioc); qio_task_set_error(task, err); qio_task_complete(task); - return; + return TRUE; } if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { @@ -183,6 +202,7 @@ trace_qio_channel_tls_credentials_allow(ioc); } qio_task_complete(task); + return TRUE; } else { GIOCondition condition; QIOChannelTLSData *data = g_new0(typeof(*data), 1); @@ -206,8 +226,9 @@ condition, qio_channel_tls_handshake_io, data, - NULL, + qio_channel_tls_io_data_free, context); + return FALSE; } } @@ -223,11 +244,9 @@ qio_task_get_source(task)); tioc->hs_ioc_tag = 0; - g_free(data); - qio_channel_tls_handshake_task(tioc, task, context); - - if (context) { - g_main_context_unref(context); + if (!qio_channel_tls_handshake_task(tioc, task, context)) { + /* task is kept by new GSource so must not be released yet */ + data->task = NULL; } return FALSE; @@ -245,14 +264,16 @@ func, opaque, destroy); trace_qio_channel_tls_handshake_start(ioc); - qio_channel_tls_handshake_task(ioc, task, context); + if (qio_channel_tls_handshake_task(ioc, task, context)) { + qio_task_free(task); + } } static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition, gpointer user_data); -static void qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task, - GMainContext *context) +static gboolean qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task, + GMainContext *context) { GIOCondition condition; QIOChannelTLSData *data; @@ -265,12 +286,12 @@ trace_qio_channel_tls_bye_fail(ioc); qio_task_set_error(task, err); qio_task_complete(task); - return; + return TRUE; } if (status == QCRYPTO_TLS_BYE_COMPLETE) { qio_task_complete(task); - return; + return TRUE; } data = g_new0(typeof(*data), 1); @@ -290,7 +311,10 @@ trace_qio_channel_tls_bye_pending(ioc, status); ioc->bye_ioc_tag = qio_channel_add_watch_full(ioc->master, condition, qio_channel_tls_bye_io, - data, NULL, context); + data, + qio_channel_tls_io_data_free, + context); + return FALSE; } @@ -303,11 +327,9 @@ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(qio_task_get_source(task)); tioc->bye_ioc_tag = 0; - g_free(data); - qio_channel_tls_bye_task(tioc, task, context); - - if (context) { - g_main_context_unref(context); + if (!qio_channel_tls_bye_task(tioc, task, context)) { + /* task is kept by new GSource so must not be released yet */ + data->task = NULL; } return FALSE; @@ -325,7 +347,9 @@ task = qio_task_new(OBJECT(ioc), propagate_error, errp, NULL); trace_qio_channel_tls_bye_start(ioc); - qio_channel_tls_bye_task(ioc, task, NULL); + if (qio_channel_tls_bye_task(ioc, task, NULL)) { + qio_task_free(task); + } } static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED) diff -Nru qemu-10.0.8+ds/io/channel-websock.c qemu-10.0.10+ds/io/channel-websock.c --- qemu-10.0.8+ds/io/channel-websock.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/io/channel-websock.c 2026-05-25 22:03:52.000000000 +0000 @@ -526,11 +526,32 @@ return 1; } +typedef struct QIOChannelWebsockData { + QIOTask *task; +} QIOChannelWebsockData; + +static void qio_channel_websock_data_free(gpointer user_data) +{ + QIOChannelWebsockData *data = user_data; + /* + * Usually 'task' will be NULL since the GSource + * callback will either complete the task or pass + * it on to a new GSource. We'll see a non-NULL + * task here only if the GSource was released before + * its callback triggers + */ + if (data->task) { + qio_task_free(data->task); + } + g_free(data); +} + static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc, GIOCondition condition, gpointer user_data) { - QIOTask *task = user_data; + QIOChannelWebsockData *data = user_data; + QIOTask *task = data->task; QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK( qio_task_get_source(task)); Error *err = NULL; @@ -572,7 +593,8 @@ GIOCondition condition, gpointer user_data) { - QIOTask *task = user_data; + QIOChannelWebsockData *data = user_data, *newdata = NULL; + QIOTask *task = data->task; QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK( qio_task_get_source(task)); Error *err = NULL; @@ -600,12 +622,14 @@ error_propagate(&wioc->io_err, err); trace_qio_channel_websock_handshake_reply(ioc); + newdata = g_new0(QIOChannelWebsockData, 1); + newdata->task = g_steal_pointer(&data->task); wioc->hs_io_tag = qio_channel_add_watch( wioc->master, G_IO_OUT, qio_channel_websock_handshake_send, - task, - NULL); + newdata, + qio_channel_websock_data_free); return FALSE; } @@ -901,12 +925,12 @@ gpointer opaque, GDestroyNotify destroy) { - QIOTask *task; + QIOChannelWebsockData *data = g_new0(QIOChannelWebsockData, 1); - task = qio_task_new(OBJECT(ioc), - func, - opaque, - destroy); + data->task = qio_task_new(OBJECT(ioc), + func, + opaque, + destroy); trace_qio_channel_websock_handshake_start(ioc); trace_qio_channel_websock_handshake_pending(ioc, G_IO_IN); @@ -914,8 +938,8 @@ ioc->master, G_IO_IN, qio_channel_websock_handshake_io, - task, - NULL); + data, + qio_channel_websock_data_free); } diff -Nru qemu-10.0.8+ds/io/task.c qemu-10.0.10+ds/io/task.c --- qemu-10.0.8+ds/io/task.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/io/task.c 2026-05-25 22:03:52.000000000 +0000 @@ -70,8 +70,12 @@ return task; } -static void qio_task_free(QIOTask *task) +void qio_task_free(QIOTask *task) { + if (!task) { + return; + } + qemu_mutex_lock(&task->thread_lock); if (task->thread) { if (task->thread->destroy) { @@ -110,6 +114,7 @@ trace_qio_task_thread_result(task); qio_task_complete(task); + qio_task_free(task); return FALSE; } @@ -196,7 +201,6 @@ { task->func(task, task->opaque); trace_qio_task_complete(task); - qio_task_free(task); } diff -Nru qemu-10.0.8+ds/linux-user/alpha/sockbits.h qemu-10.0.10+ds/linux-user/alpha/sockbits.h --- qemu-10.0.8+ds/linux-user/alpha/sockbits.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/alpha/sockbits.h 2026-05-25 22:03:52.000000000 +0000 @@ -75,6 +75,13 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define TARGET_SO_NOFCS 43 +#define TARGET_SO_TIMESTAMP_NEW 63 +#define TARGET_SO_TIMESTAMPNS_NEW 64 +#define TARGET_SO_TIMESTAMPING_NEW 65 + +#define TARGET_SO_RCVTIMEO_NEW 66 +#define TARGET_SO_SNDTIMEO_NEW 67 + /* TARGET_O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. */ diff -Nru qemu-10.0.8+ds/linux-user/arm/cpu_loop.c qemu-10.0.10+ds/linux-user/arm/cpu_loop.c --- qemu-10.0.8+ds/linux-user/arm/cpu_loop.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/arm/cpu_loop.c 2026-05-25 22:03:52.000000000 +0000 @@ -231,7 +231,7 @@ static bool emulate_arm_fpa11(CPUARMState *env, uint32_t opcode) { TaskState *ts = get_task_state(env_cpu(env)); - int rc = EmulateAll(opcode, &ts->fpa, env); + int rc = EmulateAll(opcode, &ts->fpa); int raise, enabled; if (rc == 0) { diff -Nru qemu-10.0.8+ds/linux-user/arm/nwfpe/fpa11.c qemu-10.0.10+ds/linux-user/arm/nwfpe/fpa11.c --- qemu-10.0.8+ds/linux-user/arm/nwfpe/fpa11.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/arm/nwfpe/fpa11.c 2026-05-25 22:03:52.000000000 +0000 @@ -29,8 +29,7 @@ //#include -FPA11* qemufpa = NULL; -CPUARMState* user_registers; +__thread FPA11* qemufpa = NULL; /* Reset the FPA11 chip. Called to initialize and reset the emulator. */ void resetFPA11(void) @@ -155,8 +154,7 @@ } /* Emulate the instruction in the opcode. */ -/* ??? This is not thread safe. */ -unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa, CPUARMState* qregs) +unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa) { unsigned int nRc = 0; // unsigned long flags; @@ -173,12 +171,6 @@ } qemufpa=qfpa; - user_registers=qregs; - -#if 0 - fprintf(stderr,"emulating FP insn 0x%08x, PC=0x%08x\n", - opcode, qregs[ARM_REG_PC]); -#endif fpa11 = GET_FPA11(); if (fpa11->initflag == 0) /* good place for __builtin_expect */ diff -Nru qemu-10.0.8+ds/linux-user/arm/nwfpe/fpa11.h qemu-10.0.10+ds/linux-user/arm/nwfpe/fpa11.h --- qemu-10.0.8+ds/linux-user/arm/nwfpe/fpa11.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/arm/nwfpe/fpa11.h 2026-05-25 22:03:52.000000000 +0000 @@ -25,15 +25,6 @@ #define GET_FPA11() (qemufpa) -/* - * The processes registers are always at the very top of the 8K - * stack+task struct. Use the same method as 'current' uses to - * reach them. - */ -extern CPUARMState *user_registers; - -#define GET_USERREG() (user_registers) - /* Need task_struct */ //#include @@ -83,7 +74,7 @@ float_status fp_status; /* QEMU float emulator status */ } FPA11; -extern FPA11* qemufpa; +extern __thread FPA11* qemufpa; void resetFPA11(void); void SetRoundingMode(const unsigned int); @@ -91,25 +82,25 @@ static inline unsigned int readRegister(unsigned int reg) { - return (user_registers->regs[(reg)]); + CPUARMState *env = cpu_env(current_cpu); + return env->regs[reg]; } static inline void writeRegister(unsigned int x, unsigned int y) { -#if 0 - printf("writing %d to r%d\n",y,x); -#endif - user_registers->regs[(x)]=(y); + CPUARMState *env = cpu_env(current_cpu); + env->regs[x] = y; } static inline void writeConditionCodes(unsigned int x) { - cpsr_write(user_registers, x, CPSR_NZCV, CPSRWriteByInstr); + CPUARMState *env = cpu_env(current_cpu); + cpsr_write(env, x, CPSR_NZCV, CPSRWriteByInstr); } #define ARM_REG_PC 15 -unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa, CPUARMState* qregs); +unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa); unsigned int EmulateCPDO(const unsigned int); unsigned int EmulateCPDT(const unsigned int); diff -Nru qemu-10.0.8+ds/linux-user/elfload.c qemu-10.0.10+ds/linux-user/elfload.c --- qemu-10.0.8+ds/linux-user/elfload.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/elfload.c 2026-05-25 22:03:52.000000000 +0000 @@ -2377,12 +2377,6 @@ { abi_ulong align_bss; - /* We only expect writable bss; the code segment shouldn't need this. */ - if (!(prot & PROT_WRITE)) { - error_setg(errp, "PT_LOAD with non-writable bss"); - return false; - } - align_bss = TARGET_PAGE_ALIGN(start_bss); end_bss = TARGET_PAGE_ALIGN(end_bss); @@ -2400,20 +2394,35 @@ */ align_bss -= TARGET_PAGE_SIZE; } else { + abi_ulong start_page_aligned = start_bss & TARGET_PAGE_MASK; /* - * The start of the bss shares a page with something. - * The only thing that we expect is the data section, - * which would already be marked writable. - * Overlapping the RX code segment seems malformed. + * The logical OR between flags and PAGE_WRITE works because + * in include/exec/page-protection.h they are defined as PROT_* + * values, matching mprotect(). + * Temporarily enable write access to zero the fractional bss. + * target_mprotect() handles TB invalidation if needed. */ if (!(flags & PAGE_WRITE)) { - error_setg(errp, "PT_LOAD with bss overlapping " - "non-writable page"); - return false; + if (target_mprotect(start_page_aligned, + TARGET_PAGE_SIZE, + prot | PAGE_WRITE) == -1) { + error_setg_errno(errp, errno, + "Error enabling write access for bss"); + return false; + } } - /* The page is already mapped and writable. */ + /* The page is already mapped and now guaranteed writable. */ memset(g2h_untagged(start_bss), 0, align_bss - start_bss); + + if (!(flags & PAGE_WRITE)) { + if (target_mprotect(start_page_aligned, + TARGET_PAGE_SIZE, prot) == -1) { + error_setg_errno(errp, errno, + "Error restoring bss first permissions"); + return false; + } + } } } @@ -3396,6 +3405,9 @@ /* Usual start for brk is after all sections of the main executable. */ info->brk = TARGET_PAGE_ALIGN(hiaddr + load_bias); info->elf_flags = ehdr->e_flags; +#ifdef TARGET_MIPS + info->use_k0_tls = (ehdr->e_flags & EF_MIPS_MACH) == EF_MIPS_MACH_OCTEON; +#endif prot_exec = PROT_EXEC; #ifdef TARGET_AARCH64 diff -Nru qemu-10.0.8+ds/linux-user/fd-trans.c qemu-10.0.10+ds/linux-user/fd-trans.c --- qemu-10.0.8+ds/linux-user/fd-trans.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/fd-trans.c 2026-05-25 22:03:52.000000000 +0000 @@ -482,7 +482,7 @@ unsigned short aligned_rta_len; abi_long ret; - while (len > sizeof(struct rtattr)) { + while (len >= sizeof(struct rtattr)) { rta_len = rtattr->rta_len; if (rta_len < sizeof(struct rtattr) || rta_len > len) { diff -Nru qemu-10.0.8+ds/linux-user/generic/sockbits.h qemu-10.0.10+ds/linux-user/generic/sockbits.h --- qemu-10.0.8+ds/linux-user/generic/sockbits.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/generic/sockbits.h 2026-05-25 22:03:52.000000000 +0000 @@ -58,4 +58,12 @@ #define TARGET_SO_PROTOCOL 38 #define TARGET_SO_DOMAIN 39 + +#define TARGET_SO_TIMESTAMP_NEW 63 +#define TARGET_SO_TIMESTAMPNS_NEW 64 +#define TARGET_SO_TIMESTAMPING_NEW 65 + +#define TARGET_SO_RCVTIMEO_NEW 66 +#define TARGET_SO_SNDTIMEO_NEW 67 + #endif diff -Nru qemu-10.0.8+ds/linux-user/hppa/sockbits.h qemu-10.0.10+ds/linux-user/hppa/sockbits.h --- qemu-10.0.8+ds/linux-user/hppa/sockbits.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/hppa/sockbits.h 2026-05-25 22:03:52.000000000 +0000 @@ -67,6 +67,13 @@ #define TARGET_SO_CNX_ADVICE 0x402E +#define TARGET_SO_TIMESTAMP_NEW 0x4038 +#define TARGET_SO_TIMESTAMPNS_NEW 0x4039 +#define TARGET_SO_TIMESTAMPING_NEW 0x403A + +#define TARGET_SO_RCVTIMEO_NEW 0x4040 +#define TARGET_SO_SNDTIMEO_NEW 0x4041 + /* TARGET_O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. */ diff -Nru qemu-10.0.8+ds/linux-user/i386/signal.c qemu-10.0.10+ds/linux-user/i386/signal.c --- qemu-10.0.8+ds/linux-user/i386/signal.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/i386/signal.c 2026-05-25 22:03:52.000000000 +0000 @@ -60,10 +60,33 @@ }; QEMU_BUILD_BUG_ON(sizeof(struct target_fpx_sw_bytes) != 12*4); +struct fpxreg { + uint16_t significand[4]; + uint16_t exponent; + uint16_t padding[3]; +}; + +struct xmmreg { + uint32_t element[4]; +}; + +/* + * This corresponds to the kernel's _fpstate_32. Since we + * only use it for the fpstate_unused padding section in + * the target sigcontext, it doesn't actually matter what fields + * we define here as long as we get the size right. + */ struct target_fpstate_32 { struct target_fregs_state fpstate; - X86LegacyXSaveArea fxstate; + uint32_t fxsr_env[6]; + uint32_t mxcsr; + uint32_t reserved; + struct fpxreg fxsr_st[8]; + struct xmmreg xmm[8]; + uint32_t padding1[44]; + uint32_t padding2[12]; /* aka sw_reserved */ }; +QEMU_BUILD_BUG_ON(sizeof(struct target_fpstate_32) != 32 + 80 + 512); struct target_sigcontext_32 { uint16_t gs, __gsh; diff -Nru qemu-10.0.8+ds/linux-user/ioctls.h qemu-10.0.10+ds/linux-user/ioctls.h --- qemu-10.0.8+ds/linux-user/ioctls.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/ioctls.h 2026-05-25 22:03:52.000000000 +0000 @@ -26,7 +26,7 @@ IOCTL(TIOCSCTTY, 0, TYPE_INT) IOCTL(TIOCGPGRP, IOC_R, MK_PTR(TYPE_INT)) IOCTL(TIOCSPGRP, IOC_W, MK_PTR(TYPE_INT)) - IOCTL(TIOCGSID, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(TIOCGSID, IOC_R, MK_PTR(TYPE_INT)) IOCTL(TIOCOUTQ, IOC_R, MK_PTR(TYPE_INT)) IOCTL(TIOCSTI, IOC_W, MK_PTR(TYPE_INT)) IOCTL(TIOCMGET, IOC_R, MK_PTR(TYPE_INT)) @@ -416,19 +416,18 @@ #endif IOCTL(CDROMPAUSE, 0, TYPE_NULL) - IOCTL(CDROMSTART, 0, TYPE_NULL) - IOCTL(CDROMSTOP, 0, TYPE_NULL) IOCTL(CDROMRESUME, 0, TYPE_NULL) - IOCTL(CDROMEJECT, 0, TYPE_NULL) - IOCTL(CDROMEJECT_SW, 0, TYPE_INT) - IOCTL(CDROMCLOSETRAY, 0, TYPE_NULL) - IOCTL(CDROMRESET, 0, TYPE_NULL) IOCTL(CDROMPLAYMSF, IOC_W, MK_PTR(TYPE_INT)) IOCTL(CDROMPLAYTRKIND, IOC_W, MK_PTR(TYPE_INT)) IOCTL(CDROMREADTOCHDR, IOC_R, MK_PTR(TYPE_INT)) IOCTL(CDROMREADTOCENTRY, IOC_RW, MK_PTR(TYPE_INT)) + IOCTL(CDROMSTOP, 0, TYPE_NULL) + IOCTL(CDROMSTART, 0, TYPE_NULL) + IOCTL(CDROMEJECT, 0, TYPE_NULL) IOCTL(CDROMVOLCTRL, IOC_W, MK_PTR(TYPE_INT)) IOCTL(CDROMSUBCHNL, IOC_RW, MK_PTR(TYPE_INT)) + IOCTL(CDROMEJECT_SW, IOC_W, TYPE_INT) + IOCTL(CDROMRESET, 0, TYPE_NULL) /* XXX: incorrect (need specific handling) */ IOCTL(CDROMREADAUDIO, IOC_W, MK_PTR(MK_STRUCT(STRUCT_cdrom_read_audio))) IOCTL(CDROMREADCOOKED, IOC_RW, MK_PTR(TYPE_INT)) @@ -438,16 +437,22 @@ IOCTL(CDROMREADALL, IOC_RW, MK_PTR(TYPE_INT)) IOCTL(CDROMMULTISESSION, IOC_RW, MK_PTR(TYPE_INT)) IOCTL(CDROM_GET_UPC, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(CDROM_LAST_WRITTEN, IOC_R, MK_PTR(TYPE_LONG)) IOCTL(CDROMVOLREAD, IOC_R, MK_PTR(TYPE_INT)) IOCTL(CDROMSEEK, IOC_W, MK_PTR(TYPE_INT)) IOCTL(CDROMPLAYBLK, IOC_W, MK_PTR(TYPE_INT)) - IOCTL(CDROM_MEDIA_CHANGED, 0, TYPE_NULL) - IOCTL(CDROM_SET_OPTIONS, 0, TYPE_INT) - IOCTL(CDROM_CLEAR_OPTIONS, 0, TYPE_INT) - IOCTL(CDROM_SELECT_SPEED, 0, TYPE_INT) - IOCTL(CDROM_SELECT_DISC, 0, TYPE_INT) - IOCTL(CDROM_DRIVE_STATUS, 0, TYPE_NULL) + IOCTL(CDROMCLOSETRAY, 0, TYPE_NULL) + IOCTL(CDROM_SET_OPTIONS, IOC_W, TYPE_INT) + IOCTL(CDROM_CLEAR_OPTIONS, IOC_W, TYPE_INT) + IOCTL(CDROM_SELECT_SPEED, IOC_W, TYPE_INT) + IOCTL(CDROM_SELECT_DISC, IOC_W, TYPE_INT) + IOCTL(CDROM_MEDIA_CHANGED, IOC_W, TYPE_INT) + IOCTL(CDROM_DRIVE_STATUS, IOC_W, TYPE_INT) IOCTL(CDROM_DISC_STATUS, 0, TYPE_NULL) + IOCTL(CDROM_CHANGER_NSLOTS, 0, TYPE_NULL) + IOCTL(CDROM_LOCKDOOR, IOC_W, TYPE_INT) + IOCTL(CDROM_DEBUG, IOC_W, TYPE_INT) + IOCTL(CDROM_GET_CAPABILITY, 0, TYPE_NULL) IOCTL(CDROMAUDIOBUFSIZ, 0, TYPE_INT) #if 0 diff -Nru qemu-10.0.8+ds/linux-user/main.c qemu-10.0.10+ds/linux-user/main.c --- qemu-10.0.8+ds/linux-user/main.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/main.c 2026-05-25 22:03:52.000000000 +0000 @@ -786,13 +786,15 @@ execfd = open(exec_path, O_RDONLY); if (execfd < 0) { printf("Error while loading %s: %s\n", exec_path, strerror(errno)); - _exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } } /* Resolve executable file name to full path name */ - if (realpath(exec_path, real_exec_path)) { - exec_path = real_exec_path; + /* Keep how we started the program in exec_path, e.g. "./my_program" */ + /* Store real path in real_exec_path, e.g. "/usr/local/bin/my_program" */ + if (!realpath(exec_path, real_exec_path)) { + printf("Could not resolve %s\n", exec_path); } /* @@ -987,7 +989,7 @@ info, &bprm); if (ret != 0) { printf("Error while loading %s: %s\n", exec_path, strerror(-ret)); - _exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } for (wrk = target_environ; *wrk; wrk++) { diff -Nru qemu-10.0.8+ds/linux-user/mips/sockbits.h qemu-10.0.10+ds/linux-user/mips/sockbits.h --- qemu-10.0.8+ds/linux-user/mips/sockbits.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/mips/sockbits.h 2026-05-25 22:03:52.000000000 +0000 @@ -71,6 +71,13 @@ #define TARGET_SO_RCVBUFFORCE 33 #define TARGET_SO_PASSSEC 34 +#define TARGET_SO_TIMESTAMP_NEW 63 +#define TARGET_SO_TIMESTAMPNS_NEW 64 +#define TARGET_SO_TIMESTAMPING_NEW 65 + +#define TARGET_SO_RCVTIMEO_NEW 66 +#define TARGET_SO_SNDTIMEO_NEW 67 + /** sock_type - Socket types * * Please notice that for binary compat reasons MIPS has to diff -Nru qemu-10.0.8+ds/linux-user/mips/target_cpu.h qemu-10.0.10+ds/linux-user/mips/target_cpu.h --- qemu-10.0.8+ds/linux-user/mips/target_cpu.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/mips/target_cpu.h 2026-05-25 22:03:52.000000000 +0000 @@ -35,7 +35,12 @@ static inline void cpu_set_tls(CPUMIPSState *env, target_ulong newtls) { + TaskState *ts = get_task_state(env_cpu(env)); + env->active_tc.CP0_UserLocal = newtls; + if (ts->info->use_k0_tls) { + env->active_tc.gpr[26] = newtls; + } } static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) diff -Nru qemu-10.0.8+ds/linux-user/mmap.c qemu-10.0.10+ds/linux-user/mmap.c --- qemu-10.0.8+ds/linux-user/mmap.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/mmap.c 2026-05-25 22:03:52.000000000 +0000 @@ -417,12 +417,15 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size, abi_ulong align) { - target_ulong ret; + target_ulong ret = -1; - ret = page_find_range_empty(start, reserved_va, size, align); + if (start <= reserved_va) { + ret = page_find_range_empty(start, reserved_va, size, align); + } if (ret == -1 && start > mmap_min_addr) { /* Restart at the beginning of the address space. */ - ret = page_find_range_empty(mmap_min_addr, start - 1, size, align); + ret = page_find_range_empty(mmap_min_addr, MIN(start - 1, reserved_va), + size, align); } return ret; @@ -1115,6 +1118,58 @@ errno = EINVAL; return -1; } + + if (!old_size) { + if (!(flags & MREMAP_MAYMOVE)) { + errno = EINVAL; + return -1; + } + mmap_lock(); + if (flags & MREMAP_FIXED) { + host_addr = mremap(g2h_untagged(old_addr), old_size, new_size, + flags, g2h_untagged(new_addr)); + } else { + /* + * We ensure that the new mapping stands in the + * region of guest mappable addresses. + */ + abi_ulong mmap_start; + + mmap_start = mmap_find_vma(0, new_size, TARGET_PAGE_SIZE); + + if (mmap_start == -1) { + errno = ENOMEM; + mmap_unlock(); + return -1; + } + + host_addr = mremap(g2h_untagged(old_addr), old_size, new_size, + flags | MREMAP_FIXED, g2h_untagged(mmap_start)); + + new_addr = mmap_start; + } + + if (host_addr == MAP_FAILED) { + mmap_unlock(); + return -1; + } + + if (flags & MREMAP_FIXED) { + new_addr = h2g(host_addr); + } + + prot = page_get_flags(old_addr); + /* + * For old_size zero, there is nothing to clear at old_addr. + * Only set the flags for the new mapping. They both are valid. + */ + page_set_flags(new_addr, new_addr + new_size - 1, + prot | PAGE_VALID | PAGE_RESET); + shm_region_rm_complete(new_addr, new_addr + new_size - 1); + mmap_unlock(); + return new_addr; + } + if (!guest_range_valid_untagged(old_addr, old_size)) { errno = EFAULT; return -1; diff -Nru qemu-10.0.8+ds/linux-user/ppc/signal.c qemu-10.0.10+ds/linux-user/ppc/signal.c --- qemu-10.0.8+ds/linux-user/ppc/signal.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/ppc/signal.c 2026-05-25 22:03:52.000000000 +0000 @@ -210,6 +210,18 @@ #endif +#ifdef TARGET_PPC64 +#define RT_SIGFRAME_ADJUST 0 +#else +/* + * For 32-bit rt sigframes we have an extra 16 bytes of gap + * on top of __SIGNAL_FRAMESIZE; this is to get the siginfo + * and ucontext in the same positions as in older kernels. + * See Linux's arch/powerpc/kernel/signal_32.c. + */ +#define RT_SIGFRAME_ADJUST 16 +#endif + #if defined(TARGET_PPC64) struct target_func_ptr { @@ -525,7 +537,7 @@ env->fpscr = 0; /* Create a stack frame for the caller of the handler. */ - newsp = rt_sf_addr - (SIGNAL_FRAMESIZE + 16); + newsp = rt_sf_addr - (SIGNAL_FRAMESIZE + RT_SIGFRAME_ADJUST); err |= put_user(env->gpr[1], newsp, target_ulong); if (err) @@ -641,7 +653,7 @@ struct target_rt_sigframe *rt_sf = NULL; target_ulong rt_sf_addr; - rt_sf_addr = env->gpr[1] + SIGNAL_FRAMESIZE + 16; + rt_sf_addr = env->gpr[1] + SIGNAL_FRAMESIZE + RT_SIGFRAME_ADJUST; if (!lock_user_struct(VERIFY_READ, rt_sf, rt_sf_addr, 1)) goto sigsegv; diff -Nru qemu-10.0.8+ds/linux-user/qemu.h qemu-10.0.10+ds/linux-user/qemu.h --- qemu-10.0.8+ds/linux-user/qemu.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/qemu.h 2026-05-25 22:03:52.000000000 +0000 @@ -64,6 +64,7 @@ uint32_t note_flags; #ifdef TARGET_MIPS + bool use_k0_tls; int fp_abi; int interp_fp_abi; #endif diff -Nru qemu-10.0.8+ds/linux-user/sh4/signal.c qemu-10.0.10+ds/linux-user/sh4/signal.c --- qemu-10.0.8+ds/linux-user/sh4/signal.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/sh4/signal.c 2026-05-25 22:03:52.000000000 +0000 @@ -57,7 +57,7 @@ struct target_ucontext { target_ulong tuc_flags; - struct target_ucontext *tuc_link; + abi_ulong tuc_link; target_stack_t tuc_stack; struct target_sigcontext tuc_mcontext; target_sigset_t tuc_sigmask; /* mask last for extensibility */ @@ -237,7 +237,7 @@ /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); - __put_user(0, (unsigned long *)&frame->uc.tuc_link); + __put_user(0, &frame->uc.tuc_link); target_save_altstack(&frame->uc.tuc_stack, regs); setup_sigcontext(&frame->uc.tuc_mcontext, regs, set->sig[0]); @@ -329,20 +329,42 @@ return -QEMU_ESIGRETURN; } +/* + * "or r0,r0" nop used by the Linux kernel inline sigreturn trampolines to + * avoid a hardware bug (OR_R0_R0 in arch/sh/kernel/signal_32.c). Five of + * these nops follow TRAP_NOARG, placing the syscall number word 14 bytes + * past the MOVW(7) instruction (at MOVW(7)'s load offset). This yields the + * fixed 16-byte layout that libunwind's unw_is_signal_frame detects: + * [MOVW(7), TRAP_NOARG, 5x NOP_OR, .word syscall_nr] + */ +#define NOP_OR 0x200b + void setup_sigtramp(abi_ulong sigtramp_page) { - uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 6, 0); + uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 16, 0); assert(tramp != NULL); + /* sigreturn trampoline (non-RT) at offset 0 */ default_sigreturn = sigtramp_page; - __put_user(MOVW(2), &tramp[0]); + __put_user(MOVW(7), &tramp[0]); __put_user(TRAP_NOARG, &tramp[1]); - __put_user(TARGET_NR_sigreturn, &tramp[2]); - - default_rt_sigreturn = sigtramp_page + 6; - __put_user(MOVW(2), &tramp[3]); - __put_user(TRAP_NOARG, &tramp[4]); - __put_user(TARGET_NR_rt_sigreturn, &tramp[5]); + __put_user(NOP_OR, &tramp[2]); + __put_user(NOP_OR, &tramp[3]); + __put_user(NOP_OR, &tramp[4]); + __put_user(NOP_OR, &tramp[5]); + __put_user(NOP_OR, &tramp[6]); + __put_user(TARGET_NR_sigreturn, &tramp[7]); + + /* rt_sigreturn trampoline at offset 16 */ + default_rt_sigreturn = sigtramp_page + 16; + __put_user(MOVW(7), &tramp[8]); + __put_user(TRAP_NOARG, &tramp[9]); + __put_user(NOP_OR, &tramp[10]); + __put_user(NOP_OR, &tramp[11]); + __put_user(NOP_OR, &tramp[12]); + __put_user(NOP_OR, &tramp[13]); + __put_user(NOP_OR, &tramp[14]); + __put_user(TARGET_NR_rt_sigreturn, &tramp[15]); - unlock_user(tramp, sigtramp_page, 2 * 6); + unlock_user(tramp, sigtramp_page, 2 * 16); } diff -Nru qemu-10.0.8+ds/linux-user/signal.c qemu-10.0.10+ds/linux-user/signal.c --- qemu-10.0.8+ds/linux-user/signal.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/signal.c 2026-05-25 22:03:52.000000000 +0000 @@ -1382,6 +1382,11 @@ } handle_pending_signal(cpu_env, sig, &ts->sync_signal); + /* + * Restart scan from the beginning, as handle_pending_signal + * might have resulted in a new synchronous signal (eg SIGSEGV). + */ + goto restart_scan; } for (sig = 1; sig <= TARGET_NSIG; sig++) { @@ -1392,9 +1397,7 @@ (!sigismember(blocked_set, target_to_host_signal_table[sig]))) { handle_pending_signal(cpu_env, sig, &ts->sigtab[sig - 1]); - /* Restart scan from the beginning, as handle_pending_signal - * might have resulted in a new synchronous signal (eg SIGSEGV). - */ + /* Restart scan, explained above. */ goto restart_scan; } } diff -Nru qemu-10.0.8+ds/linux-user/sparc/sockbits.h qemu-10.0.10+ds/linux-user/sparc/sockbits.h --- qemu-10.0.8+ds/linux-user/sparc/sockbits.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/sparc/sockbits.h 2026-05-25 22:03:52.000000000 +0000 @@ -61,6 +61,13 @@ #define TARGET_SO_TIMESTAMPING 0x0023 #define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING +#define TARGET_SO_TIMESTAMP_NEW 0x0046 +#define TARGET_SO_TIMESTAMPNS_NEW 0x0042 +#define TARGET_SO_TIMESTAMPING_NEW 0x0043 + +#define TARGET_SO_RCVTIMEO_NEW 0x0044 +#define TARGET_SO_SNDTIMEO_NEW 0x0045 + #define TARGET_SO_RXQ_OVFL 0x0024 #define TARGET_SO_WIFI_STATUS 0x0025 diff -Nru qemu-10.0.8+ds/linux-user/strace.list qemu-10.0.10+ds/linux-user/strace.list --- qemu-10.0.8+ds/linux-user/strace.list 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/strace.list 2026-05-25 22:03:52.000000000 +0000 @@ -1114,7 +1114,7 @@ { TARGET_NR_quotactl, "quotactl" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_read -{ TARGET_NR_read, "read" , "%s(%d,%#x,%d)", NULL, NULL }, +{ TARGET_NR_read, "read" , "%s(%d,%p,%d)", NULL, NULL }, #endif #ifdef TARGET_NR_readahead { TARGET_NR_readahead, "readahead" , NULL, NULL, NULL }, @@ -1674,7 +1674,7 @@ print_syscall_ret_waitpid }, #endif #ifdef TARGET_NR_write -{ TARGET_NR_write, "write" , "%s(%d,%#x,%d)", NULL, NULL }, +{ TARGET_NR_write, "write" , "%s(%d,%p,%d)", NULL, NULL }, #endif #ifdef TARGET_NR_writev { TARGET_NR_writev, "writev" , "%s(%d,%p,%#x)", NULL, NULL }, diff -Nru qemu-10.0.8+ds/linux-user/syscall.c qemu-10.0.10+ds/linux-user/syscall.c --- qemu-10.0.8+ds/linux-user/syscall.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/syscall.c 2026-05-25 22:03:52.000000000 +0000 @@ -1147,7 +1147,6 @@ return 0; } -#if defined(TARGET_NR_clock_adjtime64) && defined(CONFIG_CLOCK_ADJTIME) static inline abi_long copy_from_user_timeval64(struct timeval *tv, abi_ulong target_tv_addr) { @@ -1164,7 +1163,6 @@ return 0; } -#endif static inline abi_long copy_to_user_timeval64(abi_ulong target_tv_addr, const struct timeval *tv) @@ -1388,14 +1386,15 @@ return -TARGET_EFAULT; if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n)) return -TARGET_EFAULT; - - if (target_tv_addr) { - tv.tv_sec = ts.tv_sec; - tv.tv_usec = ts.tv_nsec / 1000; - if (copy_to_user_timeval(target_tv_addr, &tv)) { - return -TARGET_EFAULT; - } - } + } + if (target_tv_addr) { + tv.tv_sec = ts.tv_sec; + tv.tv_usec = ts.tv_nsec / 1000; + /* + * Like the kernel, we deliberately ignore possible + * failures writing back to the timeout struct. + */ + copy_to_user_timeval(target_tv_addr, &tv); } return ret; @@ -1523,14 +1522,16 @@ if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n)) { return -TARGET_EFAULT; } + } + if (ts_addr) { + /* + * Like the kernel, we deliberately ignore possible + * failures writing back to the timeout struct. + */ if (time64) { - if (ts_addr && host_to_target_timespec64(ts_addr, &ts)) { - return -TARGET_EFAULT; - } + host_to_target_timespec64(ts_addr, &ts); } else { - if (ts_addr && host_to_target_timespec(ts_addr, &ts)) { - return -TARGET_EFAULT; - } + host_to_target_timespec(ts_addr, &ts); } } return ret; @@ -1600,15 +1601,15 @@ if (set) { finish_sigsuspend_mask(ret); } - if (!is_error(ret) && arg3) { + if (arg3) { + /* + * Like the kernel, we deliberately ignore possible + * failures writing back to the timeout struct. + */ if (time64) { - if (host_to_target_timespec64(arg3, timeout_ts)) { - return -TARGET_EFAULT; - } + host_to_target_timespec64(arg3, timeout_ts); } else { - if (host_to_target_timespec(arg3, timeout_ts)) { - return -TARGET_EFAULT; - } + host_to_target_timespec(arg3, timeout_ts); } } } else { @@ -2011,7 +2012,8 @@ tgt_len != sizeof(struct errhdr_t)) { goto unimplemented; } - __put_user(errh->ee.ee_errno, &target_errh->ee.ee_errno); + __put_user(host_to_target_errno(errh->ee.ee_errno), + &target_errh->ee.ee_errno); __put_user(errh->ee.ee_origin, &target_errh->ee.ee_origin); __put_user(errh->ee.ee_type, &target_errh->ee.ee_type); __put_user(errh->ee.ee_code, &target_errh->ee.ee_code); @@ -2065,7 +2067,8 @@ tgt_len != sizeof(struct errhdr6_t)) { goto unimplemented; } - __put_user(errh->ee.ee_errno, &target_errh->ee.ee_errno); + __put_user(host_to_target_errno(errh->ee.ee_errno), + &target_errh->ee.ee_errno); __put_user(errh->ee.ee_origin, &target_errh->ee.ee_origin); __put_user(errh->ee.ee_type, &target_errh->ee.ee_type); __put_user(errh->ee.ee_code, &target_errh->ee.ee_code); @@ -2164,6 +2167,8 @@ QEMU_BUILD_BUG_ON(sizeof(struct ip_mreq) != sizeof(struct target_ip_mreq)); + QEMU_BUILD_BUG_ON(sizeof(struct ip_mreqn) != + sizeof(struct target_ip_mreqn)); if (optname == IP_MULTICAST_IF) { min_size = sizeof(struct in_addr); @@ -2392,6 +2397,25 @@ &tv, sizeof(tv))); return ret; } + case TARGET_SO_RCVTIMEO_NEW: + case TARGET_SO_SNDTIMEO_NEW: + { + struct timeval tv; + + if (optlen != sizeof(struct target__kernel_sock_timeval)) { + return -TARGET_EINVAL; + } + + if (copy_from_user_timeval64(&tv, optval_addr)) { + return -TARGET_EFAULT; + } + + ret = get_errno(setsockopt(sockfd, SOL_SOCKET, + optname == TARGET_SO_RCVTIMEO_NEW ? + SO_RCVTIMEO : SO_SNDTIMEO, + &tv, sizeof(tv))); + return ret; + } case TARGET_SO_ATTACH_FILTER: { struct target_sock_fprog *tfprog; @@ -2605,7 +2629,8 @@ /* These don't just return a single integer */ case TARGET_SO_PEERNAME: goto unimplemented; - case TARGET_SO_RCVTIMEO: { + case TARGET_SO_RCVTIMEO: + case TARGET_SO_RCVTIMEO_NEW: { struct timeval tv; socklen_t tvlen; @@ -2625,11 +2650,21 @@ if (ret < 0) { return ret; } - if (len > sizeof(struct target_timeval)) { - len = sizeof(struct target_timeval); + /* special case: destination address is NULL, return 0 */ + if (optval_addr) { + len = 0; } - if (copy_to_user_timeval(optval_addr, &tv)) { - return -TARGET_EFAULT; + if (len == sizeof(struct target__kernel_sock_timeval)) { + if (copy_to_user_timeval64(optval_addr, &tv)) { + return -TARGET_EFAULT; + } + } else { + if (len >= sizeof(struct target_timeval)) { + len = sizeof(struct target_timeval); + if (copy_to_user_timeval(optval_addr, &tv)) { + return -TARGET_EFAULT; + } + } } if (put_user_u32(len, optlen)) { return -TARGET_EFAULT; @@ -2637,6 +2672,7 @@ break; } case TARGET_SO_SNDTIMEO: + case TARGET_SO_SNDTIMEO_NEW: optname = SO_SNDTIMEO; goto get_timeout; case TARGET_SO_PEERCRED: { @@ -2818,7 +2854,10 @@ } if (len > lv) len = lv; - if (len == 4) { + if (!optval_addr) { + /* writing to NULL does not give error */ + len = 0; + } else if (len == 4) { if (put_user_u32(val, optval_addr)) return -TARGET_EFAULT; } else { @@ -2851,18 +2890,24 @@ return -TARGET_EINVAL; lv = sizeof(lv); ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); +write_ret: if (ret < 0) return ret; - if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) { + if (!optval_addr) { + len = 0; + } else if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) { len = 1; - if (put_user_u32(len, optlen) - || put_user_u8(val, optval_addr)) + if (put_user_u8(val, optval_addr)) { return -TARGET_EFAULT; + } } else { if (len > sizeof(int)) len = sizeof(int); - if (put_user_u32(len, optlen) - || put_user_u32(val, optval_addr)) + if (put_user_u32(val, optval_addr)) { + return -TARGET_EFAULT; + } + } + if (put_user_u32(len, optlen)) { return -TARGET_EFAULT; } break; @@ -2913,20 +2958,7 @@ return -TARGET_EINVAL; lv = sizeof(lv); ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); - if (ret < 0) - return ret; - if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) { - len = 1; - if (put_user_u32(len, optlen) - || put_user_u8(val, optval_addr)) - return -TARGET_EFAULT; - } else { - if (len > sizeof(int)) - len = sizeof(int); - if (put_user_u32(len, optlen) - || put_user_u32(val, optval_addr)) - return -TARGET_EFAULT; - } + goto write_ret; break; default: ret = -TARGET_ENOPROTOOPT; @@ -2960,8 +2992,14 @@ if (ret < 0) { return ret; } - if (put_user_u32(lv, optlen) - || put_user_u32(val, optval_addr)) { + if (optval_addr) { + if (put_user_u32(val, optval_addr)) { + return -TARGET_EFAULT; + } + } else { + lv = 0; + } + if (put_user_u32(lv, optlen)) { return -TARGET_EFAULT; } break; @@ -6890,8 +6928,6 @@ the child process gets its own copy of the lock. */ if (flags & CLONE_CHILD_SETTID) put_user_u32(sys_gettid(), child_tidptr); - if (flags & CLONE_PARENT_SETTID) - put_user_u32(sys_gettid(), parent_tidptr); ts = get_task_state(cpu); if (flags & CLONE_SETTLS) cpu_set_tls (env, newtls); @@ -6899,6 +6935,8 @@ ts->child_tidptr = child_tidptr; } else { cpu_clone_regs_parent(env, flags); + if (flags & CLONE_PARENT_SETTID) + put_user_u32(ret, parent_tidptr); if (flags & CLONE_PIDFD) { int pid_fd = 0; #if defined(__NR_pidfd_open) && defined(TARGET_NR_pidfd_open) @@ -8040,6 +8078,9 @@ #endif #if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) +#ifndef AT_HANDLE_MNT_ID_UNIQUE +#define AT_HANDLE_MNT_ID_UNIQUE 0x001 +#endif static abi_long do_name_to_handle_at(abi_long dirfd, abi_long pathname, abi_long handle, abi_long mount_id, abi_long flags) @@ -8047,6 +8088,7 @@ struct file_handle *target_fh; struct file_handle *fh; int mid = 0; + uint64_t mid64 = 0; abi_long ret; char *name; unsigned int size, total_size; @@ -8070,7 +8112,12 @@ fh = g_malloc0(total_size); fh->handle_bytes = size; - ret = get_errno(name_to_handle_at(dirfd, path(name), fh, &mid, flags)); + if (flags & AT_HANDLE_MNT_ID_UNIQUE) { + ret = get_errno(name_to_handle_at(dirfd, path(name), fh, + (int *)&mid64, flags)); + } else { + ret = get_errno(name_to_handle_at(dirfd, path(name), fh, &mid, flags)); + } unlock_user(name, pathname, 0); /* man name_to_handle_at(2): @@ -8084,8 +8131,14 @@ g_free(fh); unlock_user(target_fh, handle, total_size); - if (put_user_s32(mid, mount_id)) { - return -TARGET_EFAULT; + if (flags & AT_HANDLE_MNT_ID_UNIQUE) { + if (put_user_u64(mid64, mount_id)) { + return -TARGET_EFAULT; + } + } else { + if (put_user_s32(mid, mount_id)) { + return -TARGET_EFAULT; + } } return ret; @@ -8608,9 +8661,9 @@ return -1; } if (safe) { - return safe_openat(dirfd, exec_path, flags, mode); + return safe_openat(dirfd, real_exec_path, flags, mode); } else { - return openat(dirfd, exec_path, flags, mode); + return openat(dirfd, real_exec_path, flags, mode); } } @@ -8705,7 +8758,16 @@ if (fd > -2) { ret = get_errno(fd); } else { - ret = get_errno(safe_openat2(dirfd, pathname, &how, + const char *host_pathname = pathname; + if (pathname[0] == '/' && + !(how.resolve & (RESOLVE_IN_ROOT | RESOLVE_BENEATH))) { + /* + * RESOLVE_BENEATH rejects absolute paths; RESOLVE_IN_ROOT + * resolves them relative to dirfd. + */ + host_pathname = path(pathname); + } + ret = get_errno(safe_openat2(dirfd, host_pathname, &how, sizeof(struct open_how_ver0))); } @@ -8734,9 +8796,9 @@ * Don't worry about sign mismatch as earlier mapping * logic would have thrown a bad address error. */ - ret = MIN(strlen(exec_path), bufsiz); + ret = MIN(strlen(real_exec_path), bufsiz); /* We cannot NUL terminate the string. */ - memcpy(buf, exec_path, ret); + memcpy(buf, real_exec_path, ret); } else { ret = readlink(path(pathname), buf, bufsiz); } @@ -8827,7 +8889,7 @@ const char *exe = p; if (is_proc_myself(p, "exe")) { - exe = exec_path; + exe = real_exec_path; } ret = is_execveat ? safe_execveat(dirfd, exe, argp, envp, flags) @@ -10743,9 +10805,9 @@ * Don't worry about sign mismatch as earlier mapping * logic would have thrown a bad address error. */ - ret = MIN(strlen(exec_path), arg4); + ret = MIN(strlen(real_exec_path), arg4); /* We cannot NUL terminate the string. */ - memcpy(p2, exec_path, ret); + memcpy(p2, real_exec_path, ret); } else { ret = get_errno(readlinkat(arg1, path(p), p2, arg4)); } @@ -12945,7 +13007,7 @@ #ifdef TARGET_NR_set_thread_area case TARGET_NR_set_thread_area: #if defined(TARGET_MIPS) - cpu_env->active_tc.CP0_UserLocal = arg1; + cpu_set_tls(cpu_env, arg1); return 0; #elif defined(TARGET_I386) && defined(TARGET_ABI32) return do_set_thread_area(cpu_env, arg1); diff -Nru qemu-10.0.8+ds/linux-user/syscall_defs.h qemu-10.0.10+ds/linux-user/syscall_defs.h --- qemu-10.0.8+ds/linux-user/syscall_defs.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/syscall_defs.h 2026-05-25 22:03:52.000000000 +0000 @@ -210,7 +210,7 @@ struct target_ip_mreqn { struct target_in_addr imr_multiaddr; struct target_in_addr imr_address; - abi_long imr_ifindex; + abi_int imr_ifindex; }; struct target_ip_mreq_source { @@ -2003,7 +2003,7 @@ abi_uint __unused5; }; -#if !defined(TARGET_RISCV64) +#if !defined(TARGET_RISCV64) && !defined(TARGET_LOONGARCH64) #define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { abi_ullong st_dev; @@ -2771,7 +2771,12 @@ #ifndef RESOLVE_NO_SYMLINKS #define RESOLVE_NO_SYMLINKS 0x04 #endif - +#ifndef RESOLVE_BENEATH +#define RESOLVE_BENEATH 0x08 +#endif +#ifndef RESOLVE_IN_ROOT +#define RESOLVE_IN_ROOT 0x10 +#endif #if (defined(TARGET_I386) && defined(TARGET_ABI32)) || \ (defined(TARGET_ARM) && defined(TARGET_ABI32)) || \ defined(TARGET_M68K) || defined(TARGET_MICROBLAZE) || \ diff -Nru qemu-10.0.8+ds/linux-user/user-internals.h qemu-10.0.10+ds/linux-user/user-internals.h --- qemu-10.0.8+ds/linux-user/user-internals.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/linux-user/user-internals.h 2026-05-25 22:03:52.000000000 +0000 @@ -23,6 +23,7 @@ #include "qemu/log.h" extern char *exec_path; +extern char real_exec_path[PATH_MAX]; void init_task_state(TaskState *ts); void task_settid(TaskState *); void stop_all_tasks(void); diff -Nru qemu-10.0.8+ds/meson.build qemu-10.0.10+ds/meson.build --- qemu-10.0.8+ds/meson.build 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/meson.build 2026-05-25 22:03:52.000000000 +0000 @@ -109,7 +109,7 @@ bindgen = find_program('bindgen', required: get_option('rust')) if not bindgen.found() or bindgen.version().version_compare('<0.60.0') if get_option('rust').enabled() - error('bindgen version ' + bindgen.version() + ' is unsupported. You can install a new version with "cargo install bindgen-cli"') + error('bindgen version ' + bindgen.version() + ' is unsupported. You can install a new version with "cargo install --locked bindgen-cli"') else if bindgen.found() warning('bindgen version ' + bindgen.version() + ' is unsupported, disabling Rust compilation.') @@ -691,6 +691,12 @@ # it harder to take advantage of uninitialized stack # data to drive exploits '-ftrivial-auto-var-init=zero', + # Ensure GCC zero-initializes padding bits and trailing fields in + # unions. This avoids potentially leaking host data into the guest + # when we init a struct and copy it into guest memory. GCC prior + # to GCC 15 and clang don't have this, but they zero the padding + # and trailing portions of a union by default. + '-fzero-init-padding-bits=all', ] # Zero out registers used during a function call @@ -2845,7 +2851,7 @@ int main(void) { - pthread_condattr_t attr + pthread_condattr_t attr; pthread_condattr_init(&attr); pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); return 0; diff -Nru qemu-10.0.8+ds/migration/vmstate.c qemu-10.0.10+ds/migration/vmstate.c --- qemu-10.0.8+ds/migration/vmstate.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/migration/vmstate.c 2026-05-25 22:03:52.000000000 +0000 @@ -499,6 +499,9 @@ } else { ret = inner_field->info->put(f, curr_elem, size, inner_field, vmdesc_loop); + if (ret < 0) { + error_setg(errp, "put failed"); + } } written_bytes = qemu_file_transferred(f) - old_offset; @@ -511,8 +514,8 @@ } if (ret) { - error_setg(errp, "Save of field %s/%s failed", - vmsd->name, field->name); + error_prepend(errp, "Save of field %s/%s failed: ", + vmsd->name, field->name); if (vmsd->post_save) { vmsd->post_save(opaque); } diff -Nru qemu-10.0.8+ds/monitor/monitor.c qemu-10.0.10+ds/monitor/monitor.c --- qemu-10.0.8+ds/monitor/monitor.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/monitor/monitor.c 2026-05-25 22:03:52.000000000 +0000 @@ -363,14 +363,33 @@ { MonitorQAPIEventConf *evconf; MonitorQAPIEventState *evstate; + bool throttled; assert(event < QAPI_EVENT__MAX); evconf = &monitor_qapi_event_conf[event]; trace_monitor_protocol_event_queue(event, qdict, evconf->rate); + throttled = evconf->rate; + + /* + * Rate limit BLOCK_IO_ERROR only for action != "stop". + * + * If the VM is stopped after an I/O error, this is important information + * for the management tool to keep track of the state of QEMU and we can't + * merge any events. At the same time, stopping the VM means that the guest + * can't send additional requests and the number of events is already + * limited, so we can do without rate limiting. + */ + if (event == QAPI_EVENT_BLOCK_IO_ERROR) { + QDict *data = qobject_to(QDict, qdict_get(qdict, "data")); + const char *action = qdict_get_str(data, "action"); + if (!strcmp(action, "stop")) { + throttled = false; + } + } QEMU_LOCK_GUARD(&monitor_lock); - if (!evconf->rate) { + if (!throttled) { /* Unthrottled event */ monitor_qapi_event_emit(event, qdict); } else { diff -Nru qemu-10.0.8+ds/nbd/server.c qemu-10.0.10+ds/nbd/server.c --- qemu-10.0.8+ds/nbd/server.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/nbd/server.c 2026-05-25 22:03:52.000000000 +0000 @@ -2982,7 +2982,7 @@ "flush failed", errp); case NBD_CMD_TRIM: - ret = blk_co_pdiscard(exp->common.blk, request->from, request->len); + ret = blk_co_pdiscard(exp->common.blk, request->from, request->len, 0); if (ret >= 0 && request->flags & NBD_CMD_FLAG_FUA) { ret = blk_co_flush(exp->common.blk); } diff -Nru qemu-10.0.8+ds/plugins/meson.build qemu-10.0.10+ds/plugins/meson.build --- qemu-10.0.8+ds/plugins/meson.build 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/plugins/meson.build 2026-05-25 22:03:52.000000000 +0000 @@ -41,9 +41,16 @@ # to find missing symbols in current program. win32_qemu_plugin_api_link_flags = ['-Lplugins', '-lqemu_plugin_api'] if meson.get_compiler('c').get_id() == 'clang' + if host_machine.cpu() == 'x86_64' + dlltool_target = 'i386:x86-64' + elif host_machine.cpu() == 'aarch64' + dlltool_target = 'arm64' + else + error('Unknown machine') + endif # With LLVM/lld, delaylib is specified at link time (-delayload) dlltool = find_program('llvm-dlltool', required: true) - dlltool_cmd = [dlltool, '-d', '@INPUT@', '-l', '@OUTPUT@', '-D', 'qemu.exe'] + dlltool_cmd = [dlltool, '-m', dlltool_target,'-d', '@INPUT@', '-l', '@OUTPUT@', '-D', 'qemu.exe'] win32_qemu_plugin_api_link_flags += ['-Wl,-delayload=qemu.exe'] else # With gcc/ld, delay lib is built with a specific delay parameter. diff -Nru qemu-10.0.8+ds/python/setup.py qemu-10.0.10+ds/python/setup.py --- qemu-10.0.8+ds/python/setup.py 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/python/setup.py 2026-05-25 22:03:52.000000000 +0000 @@ -7,7 +7,6 @@ import setuptools from setuptools.command import bdist_egg import sys -import pkg_resources class bdist_egg_guard(bdist_egg.bdist_egg): @@ -30,9 +29,6 @@ QEMU tooling installer """ - # https://medium.com/@daveshawley/safely-using-setup-cfg-for-metadata-1babbe54c108 - pkg_resources.require('setuptools>=39.2') - setuptools.setup(cmdclass={'bdist_egg': bdist_egg_guard}) diff -Nru qemu-10.0.8+ds/qapi/block-core.json qemu-10.0.10+ds/qapi/block-core.json --- qemu-10.0.8+ds/qapi/block-core.json 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/qapi/block-core.json 2026-05-25 22:03:52.000000000 +0000 @@ -3786,6 +3786,9 @@ # # @errno: error identifier (errno) to be returned; defaults to EIO # +# @delay-ns: request delay before completion in nanoseconds +# (default: 0, since: 11.1) +# # @sector: specifies the sector index which has to be affected in # order to actually trigger the event; defaults to "any sector" # @@ -3801,6 +3804,7 @@ '*state': 'int', '*iotype': 'BlkdebugIOType', '*errno': 'int', + '*delay-ns': 'int', '*sector': 'int', '*once': 'bool', '*immediately': 'bool' } } @@ -5660,7 +5664,7 @@ # .. note:: If action is "stop", a STOP event will eventually follow # the BLOCK_IO_ERROR event. # -# .. note:: This event is rate-limited. +# .. note:: This event is rate-limited, except if action is "stop". # # Since: 0.13 # diff -Nru qemu-10.0.8+ds/qapi/qmp-dispatch.c qemu-10.0.10+ds/qapi/qmp-dispatch.c --- qemu-10.0.8+ds/qapi/qmp-dispatch.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/qapi/qmp-dispatch.c 2026-05-25 22:03:53.000000000 +0000 @@ -128,6 +128,16 @@ data->cmd->fn(data->args, data->ret, data->errp); monitor_set_cur(qemu_coroutine_self(), NULL); aio_co_wake(data->co); + + /* + * If the QMP dispatcher coroutine is waiting to be scheduled + * in iohandler_ctx, we must kick the main loop. This ensures + * that AIO_WAIT_WHILE_UNLOCKED() in monitor_cleanup() doesn't + * block indefinitely waiting for an event in qemu_aio_context, + * but actually gets the chance to poll iohandler_ctx and resume + * the coroutine. + */ + aio_wait_kick(); } /* diff -Nru qemu-10.0.8+ds/qemu-io-cmds.c qemu-10.0.10+ds/qemu-io-cmds.c --- qemu-10.0.8+ds/qemu-io-cmds.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/qemu-io-cmds.c 2026-05-25 22:03:53.000000000 +0000 @@ -2201,7 +2201,7 @@ } clock_gettime(CLOCK_MONOTONIC, &t1); - ret = blk_pdiscard(blk, offset, bytes); + ret = blk_pdiscard(blk, offset, bytes, 0); clock_gettime(CLOCK_MONOTONIC, &t2); if (ret < 0) { diff -Nru qemu-10.0.8+ds/qemu-keymap.c qemu-10.0.10+ds/qemu-keymap.c --- qemu-10.0.8+ds/qemu-keymap.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/qemu-keymap.c 2026-05-25 22:03:53.000000000 +0000 @@ -231,6 +231,9 @@ shift = get_mod(map, "Shift"); ctrl = get_mod(map, "Control"); altgr = get_mod(map, "AltGr"); + if (!altgr) { + altgr = get_mod(map, "Mod5"); + } numlock = get_mod(map, "NumLock"); state = xkb_state_new(map); diff -Nru qemu-10.0.8+ds/scripts/qemu-guest-agent/fsfreeze-hook qemu-10.0.10+ds/scripts/qemu-guest-agent/fsfreeze-hook --- qemu-10.0.8+ds/scripts/qemu-guest-agent/fsfreeze-hook 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/scripts/qemu-guest-agent/fsfreeze-hook 2026-05-25 22:03:53.000000000 +0000 @@ -20,18 +20,17 @@ } USE_SYSLOG=0 -# if log file is not writable, fallback to syslog -[ ! -w "$LOGFILE" ] && USE_SYSLOG=1 +# if log file exists but is not writable, fallback to syslog +[ -e "$LOGFILE" ] && [ ! -w "$LOGFILE" ] && USE_SYSLOG=1 # try to update log file and fallback to syslog if it fails -touch "$LOGFILE" &>/dev/null || USE_SYSLOG=1 +touch "$LOGFILE" >/dev/null 2>&1 || USE_SYSLOG=1 # Ensure the log file is writable, fallback to syslog if not log_message() { - local message="$1" if [ "$USE_SYSLOG" -eq 0 ]; then - printf "%s: %s\n" "$(date)" "$message" >>"$LOGFILE" + printf "%s: %s\n" "$(date)" "$1" >>"$LOGFILE" else - logger -t qemu-ga-freeze-hook "$message" + logger -t qemu-ga-freeze-hook "$1" fi } @@ -42,16 +41,31 @@ is_ignored_file "$file" && continue [ -x "$file" ] || continue - log_message "Executing $file $@" + log_message "Executing $file $*" if [ "$USE_SYSLOG" -eq 0 ]; then "$file" "$@" >>"$LOGFILE" 2>&1 STATUS=$? else - "$file" "$@" 2>&1 | logger -t qemu-ga-freeze-hook - STATUS=${PIPESTATUS[0]} + # We want to pipe the output of $file through 'logger' and also + # capture its exit status. Since we are a POSIX script we can't + # use PIPESTATUS, so instead this is a trick borrowed from + # https://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another/70675#70675 + # which uses command-groups and redirection to get the exit status. + # This is equivalent to + # "$file" "$@" 2>&1 | logger -t qemu-ga-freeze-hook + # plus setting the exit status of the pipe to the exit + # status of the first command rather than the last one. + { { { { + "$file" "$@" 2>&1 3>&- 4>&- + echo $? >&3 + } | logger -t qemu-ga-freeze-hook >&4 + } 3>&1 + } | { read -r xs ; exit "$xs"; } + } 4>&1 + STATUS=$? fi - if [ $STATUS -ne 0 ]; then + if [ "$STATUS" -ne 0 ]; then log_message "Error: $file finished with status=$STATUS" else log_message "$file finished successfully" diff -Nru qemu-10.0.8+ds/scsi/utils.c qemu-10.0.10+ds/scsi/utils.c --- qemu-10.0.8+ds/scsi/utils.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/scsi/utils.c 2026-05-25 22:03:53.000000000 +0000 @@ -373,7 +373,6 @@ case 0x1a00: /* PARAMETER LIST LENGTH ERROR */ case 0x2000: /* INVALID OPERATION CODE */ case 0x2400: /* INVALID FIELD IN CDB */ - case 0x2500: /* LOGICAL UNIT NOT SUPPORTED */ case 0x2600: /* INVALID FIELD IN PARAMETER LIST */ case 0x2104: /* UNALIGNED WRITE COMMAND */ diff -Nru qemu-10.0.8+ds/subprojects/berkeley-softfloat-3/.meson-subproject-wrap-hash.txt qemu-10.0.10+ds/subprojects/berkeley-softfloat-3/.meson-subproject-wrap-hash.txt --- qemu-10.0.8+ds/subprojects/berkeley-softfloat-3/.meson-subproject-wrap-hash.txt 1970-01-01 00:00:00.000000000 +0000 +++ qemu-10.0.10+ds/subprojects/berkeley-softfloat-3/.meson-subproject-wrap-hash.txt 2026-05-25 22:04:50.000000000 +0000 @@ -0,0 +1 @@ +f5b245e02a2f9400d7cfcc4a10860ed8c6d088577cf753ce6fe161e475a7d9ff diff -Nru qemu-10.0.8+ds/subprojects/berkeley-testfloat-3/.meson-subproject-wrap-hash.txt qemu-10.0.10+ds/subprojects/berkeley-testfloat-3/.meson-subproject-wrap-hash.txt --- qemu-10.0.8+ds/subprojects/berkeley-testfloat-3/.meson-subproject-wrap-hash.txt 1970-01-01 00:00:00.000000000 +0000 +++ qemu-10.0.10+ds/subprojects/berkeley-testfloat-3/.meson-subproject-wrap-hash.txt 2026-05-25 22:04:50.000000000 +0000 @@ -0,0 +1 @@ +b4d204e9f59e671c1c636259d8521b9c7f29116cc4b9ff6598e8e635f0359bef diff -Nru qemu-10.0.8+ds/subprojects/keycodemapdb/.meson-subproject-wrap-hash.txt qemu-10.0.10+ds/subprojects/keycodemapdb/.meson-subproject-wrap-hash.txt --- qemu-10.0.8+ds/subprojects/keycodemapdb/.meson-subproject-wrap-hash.txt 1970-01-01 00:00:00.000000000 +0000 +++ qemu-10.0.10+ds/subprojects/keycodemapdb/.meson-subproject-wrap-hash.txt 2026-05-25 22:04:50.000000000 +0000 @@ -0,0 +1 @@ +306b37c10ce735432220c08c6832dc67c0a306b88a0023c65937c90c638dba97 diff -Nru qemu-10.0.8+ds/subprojects/libvfio-user/.meson-subproject-wrap-hash.txt qemu-10.0.10+ds/subprojects/libvfio-user/.meson-subproject-wrap-hash.txt --- qemu-10.0.8+ds/subprojects/libvfio-user/.meson-subproject-wrap-hash.txt 1970-01-01 00:00:00.000000000 +0000 +++ qemu-10.0.10+ds/subprojects/libvfio-user/.meson-subproject-wrap-hash.txt 2026-05-25 22:04:50.000000000 +0000 @@ -0,0 +1 @@ +29b96e29021a38aac3544a1baacf367f905801439af61fe7a112a60894f4f2b4 diff -Nru qemu-10.0.8+ds/target/arm/cpu.h qemu-10.0.10+ds/target/arm/cpu.h --- qemu-10.0.8+ds/target/arm/cpu.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/arm/cpu.h 2026-05-25 22:03:53.000000000 +0000 @@ -1125,6 +1125,7 @@ /* Used to set the maximum vector length the cpu will support. */ uint32_t sve_max_vq; + uint32_t sme_max_vq; #ifdef CONFIG_USER_ONLY /* Used to set the default vector length at process start. */ diff -Nru qemu-10.0.8+ds/target/arm/cpu64.c qemu-10.0.10+ds/target/arm/cpu64.c --- qemu-10.0.8+ds/target/arm/cpu64.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/arm/cpu64.c 2026-05-25 22:03:53.000000000 +0000 @@ -337,6 +337,7 @@ } cpu->sme_vq.map = vq_map; + cpu->sme_max_vq = 32 - clz32(vq_map); } static bool cpu_arm_get_sme(Object *obj, Error **errp) diff -Nru qemu-10.0.8+ds/target/arm/helper.c qemu-10.0.10+ds/target/arm/helper.c --- qemu-10.0.8+ds/target/arm/helper.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/arm/helper.c 2026-05-25 22:03:53.000000000 +0000 @@ -3507,8 +3507,9 @@ /* * ATS operations only do S1 or S1+S2 translations, so we never * have to deal with the ARMCacheAttrs format for S2 only. + * (Note that res fields are only valid on ptw success.) */ - assert(!res.cacheattrs.is_s2_format); + assert(ret || !res.cacheattrs.is_s2_format); if (ret) { /* @@ -11590,7 +11591,7 @@ uint64_t pmask; assert(vq >= 1 && vq <= ARM_MAX_VQ); - assert(vq <= env_archcpu(env)->sve_max_vq); + assert(vq <= arm_max_vq(env_archcpu(env))); /* Zap the high bits of the zregs. */ for (i = 0; i < 32; i++) { diff -Nru qemu-10.0.8+ds/target/arm/internals.h qemu-10.0.10+ds/target/arm/internals.h --- qemu-10.0.8+ds/target/arm/internals.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/arm/internals.h 2026-05-25 22:03:53.000000000 +0000 @@ -717,7 +717,10 @@ * @paddr_space: physical address space that caused a fault for gpc * @stage2: True if we faulted at stage 2 * @s1ptw: True if we faulted at stage 2 while doing a stage 1 page-table walk - * @s1ns: True if we faulted on a non-secure IPA while in secure state + * @s1ns: True if we faulted on a non-secure IPA. Note that (unlike the + * HPFAR_EL2.NS bit) this is set for any stage 2 fault for an NS IPA, so + * code must check that this is for a fault taken to Secure EL2 before + * propagating s1ns to HPFAR_EL2.NS. * @ea: True if we should set the EA (external abort type) bit in syndrome */ typedef struct ARMMMUFaultInfo ARMMMUFaultInfo; @@ -1879,6 +1882,15 @@ (1 << (4 - 1)) | (1 << (8 - 1)) | (1 << (16 - 1))) /* + * Return the maximum SVE/SME VQ for this CPU. This defines + * the maximum possible size of the Zn vector registers. + */ +static inline int arm_max_vq(ARMCPU *cpu) +{ + return MAX(cpu->sve_max_vq, cpu->sme_max_vq); +} + +/* * Return true if it is possible to take a fine-grained-trap to EL2. */ static inline bool arm_fgt_active(CPUARMState *env, int el) diff -Nru qemu-10.0.8+ds/target/arm/ptw.c qemu-10.0.10+ds/target/arm/ptw.c --- qemu-10.0.8+ds/target/arm/ptw.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/arm/ptw.c 2026-05-25 22:03:53.000000000 +0000 @@ -550,12 +550,14 @@ static bool fault_s1ns(ARMSecuritySpace space, ARMMMUIdx s2_mmu_idx) { /* - * For stage 2 faults in Secure EL22, S1NS indicates - * whether the faulting IPA is in the Secure or NonSecure - * IPA space. For all other kinds of fault, it is false. + * For stage 2 faults, S1NS indicates whether the faulting IPA is + * in the Non-Secure (true) or Secure (false) IPA space. For all + * other kinds of fault, it is false. Note that we do not + * distinguish "s2 fault on NS IPA taken to Secure EL2" from + * "s2 fault on NS IPA taken to NS EL2 or Realm EL2" here, but + * instead do that when setting HPFAR_EL2.NS. */ - return space == ARMSS_Secure && regime_is_stage2(s2_mmu_idx) - && s2_mmu_idx == ARMMMUIdx_Stage2_S; + return space == ARMSS_NonSecure && regime_is_stage2(s2_mmu_idx); } /* Translate a S1 pagetable walk through S2 if needed. */ diff -Nru qemu-10.0.8+ds/target/arm/tcg/op_helper.c qemu-10.0.10+ds/target/arm/tcg/op_helper.c --- qemu-10.0.8+ds/target/arm/tcg/op_helper.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/arm/tcg/op_helper.c 2026-05-25 22:03:53.000000000 +0000 @@ -447,7 +447,7 @@ if (target_el) { env->pc -= 4; - raise_exception(env, excp, syn_wfx(1, 0xe, 0, false), target_el); + raise_exception(env, excp, syn_wfx(1, 0xe, 2, false), target_el); } if (uadd64_overflow(timeout, offset, &nexttick)) { diff -Nru qemu-10.0.8+ds/target/arm/tcg/translate-sve.c qemu-10.0.10+ds/target/arm/tcg/translate-sve.c --- qemu-10.0.8+ds/target/arm/tcg/translate-sve.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/arm/tcg/translate-sve.c 2026-05-25 22:03:53.000000000 +0000 @@ -3427,7 +3427,7 @@ gen_helper_gvec_usdot_idx_b, a) #define DO_SVE2_RRX(NAME, FUNC) \ - TRANS_FEAT(NAME, aa64_sve, gen_gvec_ool_zzz, FUNC, \ + TRANS_FEAT(NAME, aa64_sve2, gen_gvec_ool_zzz, FUNC, \ a->rd, a->rn, a->rm, a->index) DO_SVE2_RRX(MUL_zzx_h, gen_helper_gvec_mul_idx_h) @@ -3445,7 +3445,7 @@ #undef DO_SVE2_RRX #define DO_SVE2_RRX_TB(NAME, FUNC, TOP) \ - TRANS_FEAT(NAME, aa64_sve, gen_gvec_ool_zzz, FUNC, \ + TRANS_FEAT(NAME, aa64_sve2, gen_gvec_ool_zzz, FUNC, \ a->rd, a->rn, a->rm, (a->index << 1) | TOP) DO_SVE2_RRX_TB(SQDMULLB_zzx_s, gen_helper_sve2_sqdmull_idx_s, false) diff -Nru qemu-10.0.8+ds/target/arm/tcg/translate.c qemu-10.0.10+ds/target/arm/tcg/translate.c --- qemu-10.0.8+ds/target/arm/tcg/translate.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/arm/tcg/translate.c 2026-05-25 22:03:53.000000000 +0000 @@ -4815,7 +4815,7 @@ (a->imm == 0xab)) { gen_exception_internal_insn(s, EXCP_SEMIHOST); } else { - gen_exception_bkpt_insn(s, syn_aa32_bkpt(a->imm, false)); + gen_exception_bkpt_insn(s, syn_aa32_bkpt(a->imm, curr_insn_len(s) == 2)); } return true; } @@ -7602,7 +7602,6 @@ if (arm_feature(env, ARM_FEATURE_M)) { dc->vfp_enabled = 1; - dc->be_data = MO_TE; dc->v7m_handler_mode = EX_TBFLAG_M32(tb_flags, HANDLER); dc->v8m_secure = EX_TBFLAG_M32(tb_flags, SECURE); dc->v8m_stackcheck = EX_TBFLAG_M32(tb_flags, STACKCHECK); diff -Nru qemu-10.0.8+ds/target/i386/cpu.c qemu-10.0.10+ds/target/i386/cpu.c --- qemu-10.0.8+ds/target/i386/cpu.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/i386/cpu.c 2026-05-25 22:03:53.000000000 +0000 @@ -6221,6 +6221,7 @@ x86_cpu_list_feature_names(xc->filtered_features, &result); visit_type_strList(v, "unavailable-features", &result, errp); + qapi_free_strList(result); } /* Print all cpuid feature names in featureset @@ -8332,10 +8333,11 @@ /* Cache information initialization */ if (!cpu->legacy_cache) { - const CPUCaches *cache_info = - x86_cpu_get_versioned_cache_info(cpu, xcc->model); + const CPUCaches *cache_info = xcc->model + ? x86_cpu_get_versioned_cache_info(cpu, xcc->model) + : NULL; - if (!xcc->model || !cache_info) { + if (!cache_info) { g_autofree char *name = x86_cpu_class_get_model_name(xcc); error_setg(errp, "CPU model '%s' doesn't support legacy-cache=off", name); diff -Nru qemu-10.0.8+ds/target/i386/hvf/x86_mmu.c qemu-10.0.10+ds/target/i386/hvf/x86_mmu.c --- qemu-10.0.8+ds/target/i386/hvf/x86_mmu.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/i386/hvf/x86_mmu.c 2026-05-25 22:03:53.000000000 +0000 @@ -244,7 +244,8 @@ int copy = MIN(bytes, 0x1000 - (gva & 0xfff)); if (!mmu_gva_to_gpa(cpu, gva, &gpa)) { - VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva); + VM_PANIC_EX("%s: mmu_gva_to_gpa " TARGET_FMT_lx " failed\n", + __func__, gva); } else { address_space_write(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED, data, copy); @@ -265,7 +266,8 @@ int copy = MIN(bytes, 0x1000 - (gva & 0xfff)); if (!mmu_gva_to_gpa(cpu, gva, &gpa)) { - VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva); + VM_PANIC_EX("%s: mmu_gva_to_gpa " TARGET_FMT_lx " failed\n", + __func__, gva); } address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED, data, copy); diff -Nru qemu-10.0.8+ds/target/i386/tcg/decode-new.c.inc qemu-10.0.10+ds/target/i386/tcg/decode-new.c.inc --- qemu-10.0.8+ds/target/i386/tcg/decode-new.c.inc 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/i386/tcg/decode-new.c.inc 2026-05-25 22:03:53.000000000 +0000 @@ -751,19 +751,23 @@ /* five rows for no prefix, 66, F3, F2, 66+F2 */ static const X86OpEntry opcodes_0F38_F0toFF[16][5] = { + /* + * MOVBE and CRC32 are incorrectly listed as always doing 32-bit operation + * without prefix and 16-bit operation with 0x66. + */ [0] = { - X86_OP_ENTRYwr(MOVBE, G,y, M,y, cpuid(MOVBE)), - X86_OP_ENTRYwr(MOVBE, G,w, M,w, cpuid(MOVBE)), + X86_OP_ENTRYwr(MOVBE, G,v, M,v, cpuid(MOVBE)), + X86_OP_ENTRYwr(MOVBE, G,v, M,v, cpuid(MOVBE)), {}, X86_OP_ENTRY2(CRC32, G,d, E,b, cpuid(SSE42)), X86_OP_ENTRY2(CRC32, G,d, E,b, cpuid(SSE42)), }, [1] = { - X86_OP_ENTRYwr(MOVBE, M,y, G,y, cpuid(MOVBE)), - X86_OP_ENTRYwr(MOVBE, M,w, G,w, cpuid(MOVBE)), + X86_OP_ENTRYwr(MOVBE, M,v, G,v, cpuid(MOVBE)), + X86_OP_ENTRYwr(MOVBE, M,v, G,v, cpuid(MOVBE)), {}, - X86_OP_ENTRY2(CRC32, G,d, E,y, cpuid(SSE42)), - X86_OP_ENTRY2(CRC32, G,d, E,w, cpuid(SSE42)), + X86_OP_ENTRY2(CRC32, G,d, E,v, cpuid(SSE42)), + X86_OP_ENTRY2(CRC32, G,d, E,v, cpuid(SSE42)), }, [2] = { X86_OP_ENTRY3(ANDN, G,y, B,y, E,y, vex13 cpuid(BMI1)), diff -Nru qemu-10.0.8+ds/target/i386/tcg/user/excp_helper.c qemu-10.0.10+ds/target/i386/tcg/user/excp_helper.c --- qemu-10.0.8+ds/target/i386/tcg/user/excp_helper.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/i386/tcg/user/excp_helper.c 2026-05-25 22:03:53.000000000 +0000 @@ -37,9 +37,10 @@ * signal and set exception_index to EXCP_INTERRUPT. */ env->cr[2] = addr; - env->error_code = ((access_type == MMU_DATA_STORE) << PG_ERROR_W_BIT) - | (maperr ? 0 : PG_ERROR_P_MASK) - | PG_ERROR_U_MASK; + env->error_code = (maperr ? 0 : PG_ERROR_P_MASK) + | ((access_type == MMU_DATA_STORE) << PG_ERROR_W_BIT) + | PG_ERROR_U_MASK + | ((access_type == MMU_INST_FETCH) ? PG_ERROR_I_D_MASK : 0); cs->exception_index = EXCP0E_PAGE; /* Disable do_interrupt_user. */ diff -Nru qemu-10.0.8+ds/target/loongarch/cpu.c qemu-10.0.10+ds/target/loongarch/cpu.c --- qemu-10.0.8+ds/target/loongarch/cpu.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/loongarch/cpu.c 2026-05-25 22:03:53.000000000 +0000 @@ -197,6 +197,7 @@ } QEMU_FALLTHROUGH; case EXCCODE_PIF: + case EXCCODE_PNX: case EXCCODE_ADEF: cause = cs->exception_index; update_badinstr = 0; @@ -217,7 +218,6 @@ case EXCCODE_PIS: case EXCCODE_PME: case EXCCODE_PNR: - case EXCCODE_PNX: case EXCCODE_PPI: cause = cs->exception_index; break; @@ -554,6 +554,17 @@ #ifdef CONFIG_TCG env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3; + + if (is_la64(env)) { + env->hw_pte_mask = MAKE_64BIT_MASK(0, 9) | + R_TLBENTRY_64_PPN_MASK | + R_TLBENTRY_64_NR_MASK | + R_TLBENTRY_64_NX_MASK | + R_TLBENTRY_64_RPLV_MASK; + } else { + env->hw_pte_mask = MAKE_64BIT_MASK(0, 9) | + R_TLBENTRY_32_PPN_MASK; + } #endif env->fcsr0 = 0x0; diff -Nru qemu-10.0.8+ds/target/loongarch/cpu.h qemu-10.0.10+ds/target/loongarch/cpu.h --- qemu-10.0.8+ds/target/loongarch/cpu.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/loongarch/cpu.h 2026-05-25 22:03:53.000000000 +0000 @@ -378,6 +378,7 @@ uint32_t fcsr0_mask; uint64_t lladdr; /* LL virtual address compared against SC */ uint64_t llval; + uint64_t hw_pte_mask; /* Mask of architecturally-defined (hardware) PTE bits. */ #endif #ifndef CONFIG_USER_ONLY #ifdef CONFIG_TCG diff -Nru qemu-10.0.8+ds/target/loongarch/tcg/tlb_helper.c qemu-10.0.10+ds/target/loongarch/tcg/tlb_helper.c --- qemu-10.0.8+ds/target/loongarch/tcg/tlb_helper.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/loongarch/tcg/tlb_helper.c 2026-05-25 22:03:53.000000000 +0000 @@ -539,6 +539,20 @@ cpu_loop_exit_restore(cs, retaddr); } +static inline uint64_t loongarch_sanitize_hw_pte(CPULoongArchState *env, + uint64_t pte) +{ + uint64_t ppn_mask = is_la64(env) ? R_TLBENTRY_64_PPN_MASK : R_TLBENTRY_32_PPN_MASK; + + /* + * Keep only architecturally-defined PTE bits. Guests may use some + * otherwise-unused bits for software purposes. + */ + pte &= env->hw_pte_mask; + + return (pte & ~ppn_mask) | ((pte & ppn_mask) & TARGET_PHYS_MASK); +} + target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, target_ulong level, uint32_t mem_idx) { @@ -579,6 +593,7 @@ { CPUState *cs = env_cpu(env); target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv; + uint64_t pte_raw; uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); uint64_t dir_base, dir_width; @@ -590,7 +605,6 @@ * and the other is the huge page entry, * whose bit 6 should be 1. */ - base = base & TARGET_PHYS_MASK; if (FIELD_EX64(base, TLBENTRY, HUGE)) { /* * Gets the huge page level and Gets huge page size. @@ -614,19 +628,22 @@ * when loaded into the tlb, * so the tlb page size needs to be divided by 2. */ - tmp0 = base; + tmp0 = loongarch_sanitize_hw_pte(env, base); if (odd) { tmp0 += MAKE_64BIT_MASK(ps, 1); } } else { badv = env->CSR_TLBRBADV; + base = base & TARGET_PHYS_MASK; + ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1); ptindex = ptindex & ~0x1; /* clear bit 0 */ ptoffset0 = ptindex << 3; ptoffset1 = (ptindex + 1) << 3; phys = base | (odd ? ptoffset1 : ptoffset0); - tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; + pte_raw = ldq_le_phys(cs->as, phys); + tmp0 = loongarch_sanitize_hw_pte(env, pte_raw); ps = ptbase; } diff -Nru qemu-10.0.8+ds/target/microblaze/cpu.c qemu-10.0.10+ds/target/microblaze/cpu.c --- qemu-10.0.8+ds/target/microblaze/cpu.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/microblaze/cpu.c 2026-05-25 22:03:53.000000000 +0000 @@ -226,8 +226,8 @@ { info->mach = bfd_arch_microblaze; info->print_insn = print_insn_microblaze; - info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG - : BFD_ENDIAN_LITTLE; + info->endian = MICROBLAZE_CPU(cpu)->cfg.endi ? BFD_ENDIAN_LITTLE + : BFD_ENDIAN_BIG; } static void mb_cpu_realizefn(DeviceState *dev, Error **errp) diff -Nru qemu-10.0.8+ds/target/riscv/cpu_helper.c qemu-10.0.10+ds/target/riscv/cpu_helper.c --- qemu-10.0.8+ds/target/riscv/cpu_helper.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/riscv/cpu_helper.c 2026-05-25 22:03:53.000000000 +0000 @@ -1410,12 +1410,15 @@ adue = adue && (env->henvcfg & HENVCFG_ADUE); } - int ptshift = (levels - 1) * ptidxbits; + int ptshift; target_ulong pte; hwaddr pte_addr; + const hwaddr base_root = base; int i; restart: + ptshift = (levels - 1) * ptidxbits; + base = base_root; for (i = 0; i < levels; i++, ptshift -= ptidxbits) { target_ulong idx; if (i == 0) { diff -Nru qemu-10.0.8+ds/target/riscv/csr.c qemu-10.0.10+ds/target/riscv/csr.c --- qemu-10.0.8+ds/target/riscv/csr.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/riscv/csr.c 2026-05-25 22:03:53.000000000 +0000 @@ -3633,6 +3633,14 @@ uint64_t old_mip, mask = wr_mask & delegable_ints; uint32_t gin; + /* + * When mvien[9]=1, mip.SEIP is read-only and reflects only + * the external interrupt signal from the interrupt controller. + */ + if (env->mvien & MIP_SEIP) { + mask &= ~MIP_SEIP; + } + if (mask & MIP_SEIP) { env->software_seip = new_val & MIP_SEIP; new_val |= env->external_seip * MIP_SEIP; diff -Nru qemu-10.0.8+ds/target/riscv/helper.h qemu-10.0.10+ds/target/riscv/helper.h --- qemu-10.0.8+ds/target/riscv/helper.h 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/riscv/helper.h 2026-05-25 22:03:53.000000000 +0000 @@ -1289,3 +1289,6 @@ #ifndef CONFIG_USER_ONLY DEF_HELPER_1(ssamoswap_disabled, void, env) #endif + +/* Zalrsc SC write probe */ +DEF_HELPER_FLAGS_3(sc_probe_write, TCG_CALL_NO_WG, void, env, tl, tl) diff -Nru qemu-10.0.8+ds/target/riscv/insn_trans/trans_rva.c.inc qemu-10.0.10+ds/target/riscv/insn_trans/trans_rva.c.inc --- qemu-10.0.8+ds/target/riscv/insn_trans/trans_rva.c.inc 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/riscv/insn_trans/trans_rva.c.inc 2026-05-25 22:03:53.000000000 +0000 @@ -84,6 +84,12 @@ */ TCGBar bar_strl = (ctx->ztso || a->rl) ? TCG_BAR_STRL : 0; tcg_gen_mb(TCG_MO_ALL + a->aq * TCG_BAR_LDAQ + bar_strl); + /* + * "For the purposes of memory protection, a failed SC.W may be treated + * like a store." so let's check the write access permissions + */ + gen_helper_sc_probe_write(tcg_env, src1, + tcg_constant_tl(memop_size(mop))); gen_set_gpr(ctx, a->rd, tcg_constant_tl(1)); gen_set_label(l2); diff -Nru qemu-10.0.8+ds/target/riscv/op_helper.c qemu-10.0.10+ds/target/riscv/op_helper.c --- qemu-10.0.8+ds/target/riscv/op_helper.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/riscv/op_helper.c 2026-05-25 22:03:53.000000000 +0000 @@ -265,6 +265,20 @@ /* We don't emulate the cache-hierarchy, so we're done. */ } +void helper_sc_probe_write(CPURISCVState *env, target_ulong addr, + target_ulong size) +{ + uintptr_t ra = GETPC(); + int mmu_idx = riscv_env_mmu_index(env, false); + + if (addr & (size - 1)) { + env->badaddr = addr; + riscv_raise_exception(env, RISCV_EXCP_STORE_AMO_ADDR_MIS, ra); + } + + probe_write(env, addr, size, mmu_idx, ra); +} + #ifndef CONFIG_USER_ONLY target_ulong helper_sret(CPURISCVState *env) diff -Nru qemu-10.0.8+ds/target/riscv/vector_helper.c qemu-10.0.10+ds/target/riscv/vector_helper.c --- qemu-10.0.8+ds/target/riscv/vector_helper.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/target/riscv/vector_helper.c 2026-05-25 22:03:53.000000000 +0000 @@ -45,18 +45,17 @@ target_ulong reserved = s2 & MAKE_64BIT_MASK(R_VTYPE_RESERVED_SHIFT, xlen - 1 - R_VTYPE_RESERVED_SHIFT); - uint16_t vlen = cpu->cfg.vlenb << 3; int8_t lmul; if (vlmul & 4) { /* * Fractional LMUL, check: * - * VLEN * LMUL >= SEW - * VLEN >> (8 - lmul) >= sew - * (vlenb << 3) >> (8 - lmul) >= sew + * ELEN * LMUL >= SEW + * ELEN >> (8 - vlmul) >= sew */ - if (vlmul == 4 || (vlen >> (8 - vlmul)) < sew) { + if (vlmul == 4 || + (cpu->cfg.elen >> (8 - vlmul)) < sew) { vill = true; } } @@ -114,22 +113,38 @@ * It will trigger an exception if there is no mapping in TLB * and page table walk can't fill the TLB entry. Then the guest * software can return here after process the exception or never return. + * + * This function can also be used when direct access to probe_access_flags is + * needed in order to access the flags. If a pointer to a flags operand is + * provided the function will call probe_access_flags instead, use nonfault + * and update host and flags. */ -static void probe_pages(CPURISCVState *env, target_ulong addr, - target_ulong len, uintptr_t ra, - MMUAccessType access_type) +static void probe_pages(CPURISCVState *env, target_ulong addr, target_ulong len, + uintptr_t ra, MMUAccessType access_type, int mmu_index, + void **host, int *flags, bool nonfault) { target_ulong pagelen = -(addr | TARGET_PAGE_MASK); target_ulong curlen = MIN(pagelen, len); - int mmu_index = riscv_env_mmu_index(env, false); - probe_access(env, adjust_addr(env, addr), curlen, access_type, - mmu_index, ra); + if (flags != NULL) { + *flags = probe_access_flags(env, adjust_addr(env, addr), curlen, + access_type, mmu_index, nonfault, host, ra); + } else { + probe_access(env, adjust_addr(env, addr), curlen, access_type, + mmu_index, ra); + } + if (len > curlen) { addr += curlen; curlen = len - curlen; - probe_access(env, adjust_addr(env, addr), curlen, access_type, - mmu_index, ra); + if (flags != NULL) { + *flags |= probe_access_flags(env, adjust_addr(env, addr), curlen, + access_type, mmu_index, nonfault, + host, ra); + } else { + probe_access(env, adjust_addr(env, addr), curlen, access_type, + mmu_index, ra); + } } } @@ -332,8 +347,8 @@ MMUAccessType access_type = is_load ? MMU_DATA_LOAD : MMU_DATA_STORE; /* Check page permission/pmp/watchpoint/etc. */ - flags = probe_access_flags(env, adjust_addr(env, addr), size, access_type, - mmu_index, true, &host, ra); + probe_pages(env, addr, size, ra, access_type, mmu_index, &host, &flags, + true); if (flags == 0) { if (nf == 1) { @@ -630,7 +645,7 @@ uint32_t esz = 1 << log2_esz; uint32_t msize = nf * esz; uint32_t vma = vext_vma(desc); - target_ulong addr, addr_probe, addr_i, offset, remain, page_split, elems; + target_ulong addr, addr_i, offset, remain, page_split, elems; int mmu_index = riscv_env_mmu_index(env, false); int flags; void *host; @@ -646,16 +661,8 @@ } /* Check page permission/pmp/watchpoint/etc. */ - flags = probe_access_flags(env, adjust_addr(env, addr), elems * msize, - MMU_DATA_LOAD, mmu_index, true, &host, ra); - - /* If we are crossing a page check also the second page. */ - if (env->vl > elems) { - addr_probe = addr + (elems << log2_esz); - flags |= probe_access_flags(env, adjust_addr(env, addr_probe), - elems * msize, MMU_DATA_LOAD, mmu_index, - true, &host, ra); - } + probe_pages(env, addr, (env->vl - env->vstart) * msize, ra, MMU_DATA_LOAD, + mmu_index, &host, &flags, true); if (flags & ~TLB_WATCHPOINT) { /* probe every access */ @@ -666,16 +673,16 @@ addr_i = adjust_addr(env, base + i * (nf << log2_esz)); if (i == 0) { /* Allow fault on first element. */ - probe_pages(env, addr_i, nf << log2_esz, ra, MMU_DATA_LOAD); + probe_pages(env, addr_i, nf << log2_esz, ra, MMU_DATA_LOAD, + mmu_index, &host, NULL, false); } else { remain = nf << log2_esz; while (remain > 0) { offset = -(addr_i | TARGET_PAGE_MASK); /* Probe nonfault on subsequent elements. */ - flags = probe_access_flags(env, addr_i, offset, - MMU_DATA_LOAD, mmu_index, true, - &host, 0); + probe_pages(env, addr_i, offset, 0, MMU_DATA_LOAD, + mmu_index, &host, &flags, true); /* * Stop if invalid (unmapped) or mmio (transaction may diff -Nru qemu-10.0.8+ds/tcg/tcg-op-ldst.c qemu-10.0.10+ds/tcg/tcg-op-ldst.c --- qemu-10.0.8+ds/tcg/tcg-op-ldst.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tcg/tcg-op-ldst.c 2026-05-25 22:03:53.000000000 +0000 @@ -260,9 +260,6 @@ copy_addr = plugin_maybe_preserve_addr(addr); gen_ldst(INDEX_op_qemu_ld_i32, TCG_TYPE_I32, tcgv_i32_temp(val), NULL, addr_new, oi); - plugin_gen_mem_callbacks_i32(val, copy_addr, addr, orig_oi, - QEMU_PLUGIN_MEM_R); - maybe_free_addr(addr, addr_new); if ((orig_memop ^ memop) & MO_BSWAP) { switch (orig_memop & MO_SIZE) { @@ -278,6 +275,10 @@ g_assert_not_reached(); } } + + plugin_gen_mem_callbacks_i32(val, copy_addr, addr, orig_oi, + QEMU_PLUGIN_MEM_R); + maybe_free_addr(addr, addr_new); } void tcg_gen_qemu_ld_i32_chk(TCGv_i32 val, TCGTemp *addr, TCGArg idx, @@ -288,10 +289,10 @@ tcg_gen_qemu_ld_i32_int(val, addr, idx, memop); } -static void tcg_gen_qemu_st_i32_int(TCGv_i32 val, TCGTemp *addr, +static void tcg_gen_qemu_st_i32_int(TCGv_i32 orig_val, TCGTemp *addr, TCGArg idx, MemOp memop) { - TCGv_i32 swap = NULL; + TCGv_i32 val = orig_val; MemOpIdx orig_oi, oi; TCGOpcode opc; TCGTemp *addr_new; @@ -301,18 +302,17 @@ orig_oi = oi = make_memop_idx(memop, idx); if ((memop & MO_BSWAP) && !tcg_target_has_memory_bswap(memop)) { - swap = tcg_temp_ebb_new_i32(); + val = tcg_temp_ebb_new_i32(); switch (memop & MO_SIZE) { case MO_16: - tcg_gen_bswap16_i32(swap, val, 0); + tcg_gen_bswap16_i32(val, orig_val, 0); break; case MO_32: - tcg_gen_bswap32_i32(swap, val); + tcg_gen_bswap32_i32(val, orig_val); break; default: g_assert_not_reached(); } - val = swap; memop &= ~MO_BSWAP; oi = make_memop_idx(memop, idx); } @@ -324,11 +324,12 @@ } addr_new = tci_extend_addr(addr); gen_ldst(opc, TCG_TYPE_I32, tcgv_i32_temp(val), NULL, addr_new, oi); - plugin_gen_mem_callbacks_i32(val, NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); + plugin_gen_mem_callbacks_i32(orig_val, NULL, addr, orig_oi, + QEMU_PLUGIN_MEM_W); maybe_free_addr(addr, addr_new); - if (swap) { - tcg_temp_free_i32(swap); + if (val != orig_val) { + tcg_temp_free_i32(val); } } @@ -374,9 +375,6 @@ addr_new = tci_extend_addr(addr); copy_addr = plugin_maybe_preserve_addr(addr); gen_ldst_i64(INDEX_op_qemu_ld_i64, val, addr_new, oi); - plugin_gen_mem_callbacks_i64(val, copy_addr, addr, orig_oi, - QEMU_PLUGIN_MEM_R); - maybe_free_addr(addr, addr_new); if ((orig_memop ^ memop) & MO_BSWAP) { int flags = (orig_memop & MO_SIGN @@ -396,6 +394,10 @@ g_assert_not_reached(); } } + + plugin_gen_mem_callbacks_i64(val, copy_addr, addr, orig_oi, + QEMU_PLUGIN_MEM_R); + maybe_free_addr(addr, addr_new); } void tcg_gen_qemu_ld_i64_chk(TCGv_i64 val, TCGTemp *addr, TCGArg idx, @@ -406,10 +408,10 @@ tcg_gen_qemu_ld_i64_int(val, addr, idx, memop); } -static void tcg_gen_qemu_st_i64_int(TCGv_i64 val, TCGTemp *addr, +static void tcg_gen_qemu_st_i64_int(TCGv_i64 orig_val, TCGTemp *addr, TCGArg idx, MemOp memop) { - TCGv_i64 swap = NULL; + TCGv_i64 val = orig_val; MemOpIdx orig_oi, oi; TCGTemp *addr_new; @@ -423,32 +425,32 @@ orig_oi = oi = make_memop_idx(memop, idx); if ((memop & MO_BSWAP) && !tcg_target_has_memory_bswap(memop)) { - swap = tcg_temp_ebb_new_i64(); + val = tcg_temp_ebb_new_i64(); switch (memop & MO_SIZE) { case MO_16: - tcg_gen_bswap16_i64(swap, val, 0); + tcg_gen_bswap16_i64(val, orig_val, 0); break; case MO_32: - tcg_gen_bswap32_i64(swap, val, 0); + tcg_gen_bswap32_i64(val, orig_val, 0); break; case MO_64: - tcg_gen_bswap64_i64(swap, val); + tcg_gen_bswap64_i64(val, orig_val); break; default: g_assert_not_reached(); } - val = swap; memop &= ~MO_BSWAP; oi = make_memop_idx(memop, idx); } addr_new = tci_extend_addr(addr); gen_ldst_i64(INDEX_op_qemu_st_i64, val, addr_new, oi); - plugin_gen_mem_callbacks_i64(val, NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); + plugin_gen_mem_callbacks_i64(orig_val, NULL, addr, orig_oi, + QEMU_PLUGIN_MEM_W); maybe_free_addr(addr, addr_new); - if (swap) { - tcg_temp_free_i64(swap); + if (val != orig_val) { + tcg_temp_free_i64(val); } } diff -Nru qemu-10.0.8+ds/tests/docker/dockerfiles/debian-hexagon-cross.docker qemu-10.0.10+ds/tests/docker/dockerfiles/debian-hexagon-cross.docker --- qemu-10.0.8+ds/tests/docker/dockerfiles/debian-hexagon-cross.docker 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tests/docker/dockerfiles/debian-hexagon-cross.docker 2026-05-25 22:03:53.000000000 +0000 @@ -5,10 +5,12 @@ # needs to be able to build QEMU itself in CI we include its # build-deps. # -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:13-slim + +# Add deb-src repository sources +RUN sed -i "s/^Types: deb$/Types: deb deb-src/" \ + /etc/apt/sources.list.d/debian.sources -# Duplicate deb line as deb-src -RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata \ @@ -24,6 +26,7 @@ ninja-build \ python3-pip \ python3-setuptools \ + python3-tomli \ python3-venv \ python3-wheel && \ # Install QEMU build deps for use in CI @@ -36,8 +39,6 @@ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt -RUN /usr/bin/pip3 install tomli - ENV TOOLCHAIN_INSTALL /opt ENV TOOLCHAIN_RELEASE 12.Dec.2023 ENV TOOLCHAIN_BASENAME "clang+llvm-${TOOLCHAIN_RELEASE}-cross-hexagon-unknown-linux-musl" diff -Nru qemu-10.0.8+ds/tests/docker/dockerfiles/debian-loongarch-cross.docker qemu-10.0.10+ds/tests/docker/dockerfiles/debian-loongarch-cross.docker --- qemu-10.0.8+ds/tests/docker/dockerfiles/debian-loongarch-cross.docker 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tests/docker/dockerfiles/debian-loongarch-cross.docker 2026-05-25 22:03:53.000000000 +0000 @@ -4,10 +4,11 @@ # This docker target uses prebuilt toolchains for LoongArch64 from: # https://github.com/loongson/build-tools/releases # -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:13-slim -# Duplicate deb line as deb-src -RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list +# Add deb-src repository sources +RUN sed -i "s/^Types: deb$/Types: deb deb-src/" \ + /etc/apt/sources.list.d/debian.sources RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -31,12 +32,11 @@ ninja-build \ python3-pip \ python3-setuptools \ + python3-tomli \ python3-venv \ python3-wheel && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt -RUN /usr/bin/pip3 install tomli - RUN curl -#SL https://github.com/loongson/build-tools/releases/download/2023.08.08/CLFS-loongarch64-8.1-x86_64-cross-tools-gcc-glibc.tar.xz \ | tar -xJC /opt diff -Nru qemu-10.0.8+ds/tests/docker/dockerfiles/debian-toolchain.docker qemu-10.0.10+ds/tests/docker/dockerfiles/debian-toolchain.docker --- qemu-10.0.8+ds/tests/docker/dockerfiles/debian-toolchain.docker 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tests/docker/dockerfiles/debian-toolchain.docker 2026-05-25 22:03:53.000000000 +0000 @@ -4,13 +4,15 @@ # This dockerfile is used for building a cross-compiler toolchain. # The script for building the toolchain is supplied via extra-files. # -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:13-slim # Install build utilities for building gcc and glibc. # ??? The build-dep isn't working, missing a number of # minimal build dependiencies, e.g. libmpc. -RUN sed 's/^deb /deb-src /' /etc/apt/sources.list.d/deb-src.list +# Add deb-src repository sources +RUN sed -i "s/^Types: deb$/Types: deb deb-src/" \ + /etc/apt/sources.list.d/debian.sources RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ @@ -34,7 +36,7 @@ # Throw away the extra toolchain build deps, the downloaded source, # and the build trees by restoring the original image, # then copying the built toolchain from stage 0. -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:13-slim RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata \ diff -Nru qemu-10.0.8+ds/tests/docker/dockerfiles/debian-tricore-cross.docker qemu-10.0.10+ds/tests/docker/dockerfiles/debian-tricore-cross.docker --- qemu-10.0.8+ds/tests/docker/dockerfiles/debian-tricore-cross.docker 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tests/docker/dockerfiles/debian-tricore-cross.docker 2026-05-25 22:03:53.000000000 +0000 @@ -9,7 +9,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later # -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:13-slim RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ @@ -31,12 +31,11 @@ pkgconf \ python3-pip \ python3-setuptools \ + python3-tomli \ python3-wheel \ python3-venv && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt -RUN /usr/bin/pip3 install tomli - RUN curl -#SL https://github.com/bkoppelmann/package_940/releases/download/tricore-toolchain-9.40/tricore-toolchain-9.4.0.tar.gz \ | tar -xzC /usr/local/ diff -Nru qemu-10.0.8+ds/tests/docker/dockerfiles/debian-xtensa-cross.docker qemu-10.0.10+ds/tests/docker/dockerfiles/debian-xtensa-cross.docker --- qemu-10.0.8+ds/tests/docker/dockerfiles/debian-xtensa-cross.docker 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tests/docker/dockerfiles/debian-xtensa-cross.docker 2026-05-25 22:03:53.000000000 +0000 @@ -5,7 +5,7 @@ # using a prebuilt toolchains for Xtensa cores from: # https://github.com/foss-xtensa/toolchain/releases # -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:13-slim RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ diff -Nru qemu-10.0.8+ds/tests/docker/dockerfiles/fedora-rust-nightly.docker qemu-10.0.10+ds/tests/docker/dockerfiles/fedora-rust-nightly.docker --- qemu-10.0.8+ds/tests/docker/dockerfiles/fedora-rust-nightly.docker 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tests/docker/dockerfiles/fedora-rust-nightly.docker 2026-05-25 22:03:53.000000000 +0000 @@ -172,7 +172,7 @@ test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \ test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)" ENV PATH=$CARGO_HOME/bin:$PATH -RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli +RUN /usr/local/cargo/bin/rustup run nightly cargo install --locked bindgen-cli RUN $CARGO --list # As a final step configure the user (if env is defined) ARG USER diff -Nru qemu-10.0.8+ds/tests/docker/dockerfiles/ubuntu2204.docker qemu-10.0.10+ds/tests/docker/dockerfiles/ubuntu2204.docker --- qemu-10.0.8+ds/tests/docker/dockerfiles/ubuntu2204.docker 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tests/docker/dockerfiles/ubuntu2204.docker 2026-05-25 22:03:53.000000000 +0000 @@ -154,7 +154,7 @@ ENV PATH=$CARGO_HOME/bin:$PATH RUN DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends cargo -RUN cargo install bindgen-cli +RUN cargo install --locked bindgen-cli # As a final step configure the user (if env is defined) ARG USER ARG UID diff -Nru qemu-10.0.8+ds/tests/functional/qemu_test/asset.py qemu-10.0.10+ds/tests/functional/qemu_test/asset.py --- qemu-10.0.8+ds/tests/functional/qemu_test/asset.py 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tests/functional/qemu_test/asset.py 2026-05-25 22:03:53.000000000 +0000 @@ -183,11 +183,14 @@ raise AssetError(self, "Download retries exceeded", transient=True) try: - # Set these just for informational purposes - os.setxattr(str(tmp_cache_file), "user.qemu-asset-url", - self.url.encode('utf8')) - os.setxattr(str(tmp_cache_file), "user.qemu-asset-hash", - self.hash.encode('utf8')) + # Set these just for informational purposes. Note that + # setxattr is Linux-only; as this is only informational + # we can simply skip it on other platforms. + if hasattr(os, "setxattr"): + os.setxattr(str(tmp_cache_file), "user.qemu-asset-url", + self.url.encode('utf8')) + os.setxattr(str(tmp_cache_file), "user.qemu-asset-hash", + self.hash.encode('utf8')) except Exception as e: self.log.debug("Unable to set xattr on %s: %s", tmp_cache_file, e) pass diff -Nru qemu-10.0.8+ds/tests/lcitool/refresh qemu-10.0.10+ds/tests/lcitool/refresh --- qemu-10.0.8+ds/tests/lcitool/refresh 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tests/lcitool/refresh 2026-05-25 22:03:53.000000000 +0000 @@ -137,7 +137,7 @@ ' test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \\\n', ' test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)"\n', 'ENV PATH=$CARGO_HOME/bin:$PATH\n', - 'RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli\n', + 'RUN /usr/local/cargo/bin/rustup run nightly cargo install --locked bindgen-cli\n', 'RUN $CARGO --list\n', ] @@ -146,7 +146,7 @@ 'ENV PATH=$CARGO_HOME/bin:$PATH\n', "RUN DEBIAN_FRONTEND=noninteractive eatmydata \\\n", " apt install -y --no-install-recommends cargo\n", - 'RUN cargo install bindgen-cli\n', + 'RUN cargo install --locked bindgen-cli\n', ] def cross_build(prefix, targets): diff -Nru qemu-10.0.8+ds/tests/qemu-iotests/257 qemu-10.0.10+ds/tests/qemu-iotests/257 --- qemu-10.0.8+ds/tests/qemu-iotests/257 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tests/qemu-iotests/257 2026-05-25 22:03:53.000000000 +0000 @@ -310,14 +310,18 @@ 'state': 1, 'new_state': 2 }, { - 'event': 'read_aio', + 'event': 'flush_to_disk', 'state': 2, 'new_state': 3 + }, { + 'event': "read_aio", + 'state': 3, + 'new_state': 4 }], 'inject-error': [{ 'event': 'read_aio', 'errno': 5, - 'state': 3, + 'state': 4, 'immediately': False, 'once': True }] diff -Nru qemu-10.0.8+ds/tests/qemu-iotests/257.out qemu-10.0.10+ds/tests/qemu-iotests/257.out --- qemu-10.0.8+ds/tests/qemu-iotests/257.out 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tests/qemu-iotests/257.out 2026-05-25 22:03:53.000000000 +0000 @@ -272,7 +272,7 @@ --- Preparing image & VM --- -{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} +{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 4}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "flush_to_disk", "new-state": 3, "state": 2}, {"event": "read_aio", "new-state": 4, "state": 3}]}, "node-name": "drive0"}} {"return": {}} --- Write #0 --- @@ -1017,7 +1017,7 @@ --- Preparing image & VM --- -{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} +{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 4}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "flush_to_disk", "new-state": 3, "state": 2}, {"event": "read_aio", "new-state": 4, "state": 3}]}, "node-name": "drive0"}} {"return": {}} --- Write #0 --- @@ -1762,7 +1762,7 @@ --- Preparing image & VM --- -{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} +{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 4}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "flush_to_disk", "new-state": 3, "state": 2}, {"event": "read_aio", "new-state": 4, "state": 3}]}, "node-name": "drive0"}} {"return": {}} --- Write #0 --- @@ -2507,7 +2507,7 @@ --- Preparing image & VM --- -{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} +{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 4}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "flush_to_disk", "new-state": 3, "state": 2}, {"event": "read_aio", "new-state": 4, "state": 3}]}, "node-name": "drive0"}} {"return": {}} --- Write #0 --- @@ -3252,7 +3252,7 @@ --- Preparing image & VM --- -{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} +{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 4}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "flush_to_disk", "new-state": 3, "state": 2}, {"event": "read_aio", "new-state": 4, "state": 3}]}, "node-name": "drive0"}} {"return": {}} --- Write #0 --- @@ -3997,7 +3997,7 @@ --- Preparing image & VM --- -{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} +{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 4}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "flush_to_disk", "new-state": 3, "state": 2}, {"event": "read_aio", "new-state": 4, "state": 3}]}, "node-name": "drive0"}} {"return": {}} --- Write #0 --- @@ -4742,7 +4742,7 @@ --- Preparing image & VM --- -{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} +{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 4}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "flush_to_disk", "new-state": 3, "state": 2}, {"event": "read_aio", "new-state": 4, "state": 3}]}, "node-name": "drive0"}} {"return": {}} --- Write #0 --- diff -Nru qemu-10.0.8+ds/tests/qtest/ide-test.c qemu-10.0.10+ds/tests/qtest/ide-test.c --- qemu-10.0.8+ds/tests/qtest/ide-test.c 2026-02-12 20:30:14.000000000 +0000 +++ qemu-10.0.10+ds/tests/qtest/ide-test.c 2026-05-25 22:03:53.000000000 +0000 @@ -41,8 +41,11 @@ #define IDE_PCI_FUNC 1 #define IDE_BASE 0x1f0 +#define IDE_BASE2 0x3f6 #define IDE_PRIMARY_IRQ 14 +#define IDE_CTRL_RESET 0x04 + #define ATAPI_BLOCK_SIZE 2048 /* How many bytes to receive via ATAPI PIO at one time. @@ -99,6 +102,7 @@ CMDF_ABORT = 0x100, CMDF_NO_BM = 0x200, + CMDF_NO_WAIT = 0x400, }; enum { @@ -200,21 +204,49 @@ return cpu_to_le64(((uint64_t)count << 48) + sector); } -static int send_dma_request(QTestState *qts, int cmd, uint64_t sector, - int nb_sectors, PrdtEntry *prdt, int prdt_entries, - void(*post_exec)(QPCIDevice *dev, QPCIBar ide_bar, - uint64_t sector, int nb_sectors)) +static uint8_t wait_dma_completion(QTestState *qts, QPCIDevice *dev, + QPCIBar bmdma_bar, QPCIBar ide_bar) +{ + uint8_t status; + + /* Wait for the DMA transfer to complete */ + do { + status = qpci_io_readb(dev, bmdma_bar, bmreg_status); + } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE); + + g_assert_cmpint(qtest_get_irq(qts, IDE_PRIMARY_IRQ), ==, + !!(status & BM_STS_INTR)); + + /* Check IDE status code */ + assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRDY); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), BSY | DRQ); + + /* Reading the status register clears the IRQ */ + g_assert(!qtest_get_irq(qts, IDE_PRIMARY_IRQ)); + + /* Stop DMA transfer if still active */ + if (status & BM_STS_ACTIVE) { + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); + } + + return status; +} + +static int send_dma_request_dev(QTestState *qts, QPCIDevice *dev, + QPCIBar bmdma_bar, QPCIBar ide_bar, int cmd, + uint64_t sector, int nb_sectors, + PrdtEntry *prdt, int prdt_entries, + void(*post_exec)(QPCIDevice *dev, + QPCIBar ide_bar, + uint64_t sector, + int nb_sectors)) { - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; uintptr_t guest_prdt; size_t len; bool from_dev; uint8_t status; int flags; - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - flags = cmd & ~0xff; cmd &= 0xff; @@ -280,26 +312,28 @@ qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); } - /* Wait for the DMA transfer to complete */ - do { - status = qpci_io_readb(dev, bmdma_bar, bmreg_status); - } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE); - - g_assert_cmpint(qtest_get_irq(qts, IDE_PRIMARY_IRQ), ==, - !!(status & BM_STS_INTR)); + if (flags & CMDF_NO_WAIT) { + return 0; + } - /* Check IDE status code */ - assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRDY); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), BSY | DRQ); + status = wait_dma_completion(qts, dev, bmdma_bar, ide_bar); - /* Reading the status register clears the IRQ */ - g_assert(!qtest_get_irq(qts, IDE_PRIMARY_IRQ)); + return status; +} - /* Stop DMA transfer if still active */ - if (status & BM_STS_ACTIVE) { - qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); - } +static int send_dma_request(QTestState *qts, int cmd, uint64_t sector, + int nb_sectors, PrdtEntry *prdt, int prdt_entries, + void(*post_exec)(QPCIDevice *dev, QPCIBar ide_bar, + uint64_t sector, int nb_sectors)) +{ + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t status; + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + status = send_dma_request_dev(qts, dev, bmdma_bar, ide_bar, + cmd, sector, nb_sectors, prdt, prdt_entries, + post_exec); free_pci_device(dev); return status; @@ -447,6 +481,60 @@ test_bmdma_teardown(qts); } +static void test_bmdma_trim_reset(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar, ide_bar2; + uint8_t status; + const uint64_t trim_range[] = { + trim_range_le(0, 2), + trim_range_le(6, 8), + }; + size_t len = 512; + uint8_t *buf; + uintptr_t guest_buf; + PrdtEntry prdt[1]; + + qts = ide_test_start( + "-blockdev file,filename=%s,node-name=img " + "-blockdev blkdebug,image=img,node-name=dbg,discard=unmap," + "inject-error.0.event=none,inject-error.0.iotype=discard," + "inject-error.0.errno=0,inject-error.0.delay-ns=1000000 " + "-device ide-hd,drive=dbg,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), + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + ide_bar2 = qpci_legacy_iomap(dev, IDE_BASE2); + + buf = g_malloc(len); + + /* TRIM request with two segments */ + *((uint64_t *)buf) = trim_range[0]; + *((uint64_t *)buf + 1) = trim_range[1]; + + qtest_memwrite(qts, guest_buf, buf, 2 * sizeof(uint64_t)); + + send_dma_request_dev(qts, dev, bmdma_bar, ide_bar, CMD_DSM | CMDF_NO_WAIT, 0, 1, prdt, + ARRAY_SIZE(prdt), NULL); + + /* Reset the device while the first segment is in flight */ + qpci_io_writeb(dev, ide_bar2, 0, IDE_CTRL_RESET); + + status = wait_dma_completion(qts, dev, bmdma_bar, ide_bar); + g_assert_cmphex(status, ==, BM_STS_INTR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + + free_pci_device(dev); + g_free(buf); + test_bmdma_teardown(qts); +} + /* * This test is developed according to the Programming Interface for * Bus Master IDE Controller (Revision 1.0 5/16/94) @@ -1128,6 +1216,7 @@ qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw); qtest_add_func("/ide/bmdma/trim", test_bmdma_trim); + qtest_add_func("/ide/bmdma/trim_reset", test_bmdma_trim_reset); qtest_add_func("/ide/bmdma/various_prdts", test_bmdma_various_prdts); qtest_add_func("/ide/bmdma/no_busmaster", test_bmdma_no_busmaster); diff -Nru qemu-10.0.8+ds/tests/tcg/multiarch/test-mmap.c qemu-10.0.10+ds/tests/tcg/multiarch/test-mmap.c --- qemu-10.0.8+ds/tests/tcg/multiarch/test-mmap.c 2026-02-12 20:30:15.000000000 +0000 +++ qemu-10.0.10+ds/tests/tcg/multiarch/test-mmap.c 2026-05-25 22:03:53.000000000 +0000 @@ -491,6 +491,20 @@ munmap(c, 2 * pagesize); } +void check_mmaps_beyond_addr_space(void) +{ + unsigned char *addr; + addr = mmap((void *)(-(unsigned long)pagesize * 10), pagesize * 2, + PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + fprintf(stdout, "%s addr=%p errno=%d", __func__, (void *)addr, errno); + fail_unless(addr != MAP_FAILED); + + memcpy(dummybuf, addr, 2 * pagesize); + munmap(addr, 2 * pagesize); + + fprintf(stdout, " passed\n"); +} + int main(int argc, char **argv) { char tempname[] = "/tmp/.cmmapXXXXXX"; @@ -534,6 +548,7 @@ check_file_unfixed_eof_mmaps(); check_invalid_mmaps(); check_shrink_mmaps(); + check_mmaps_beyond_addr_space(); /* Fails at the moment. */ /* check_aligned_anonymous_fixed_mmaps_collide_with_host(); */ diff -Nru qemu-10.0.8+ds/tests/tcg/plugins/mem.c qemu-10.0.10+ds/tests/tcg/plugins/mem.c --- qemu-10.0.8+ds/tests/tcg/plugins/mem.c 2026-02-12 20:30:15.000000000 +0000 +++ qemu-10.0.10+ds/tests/tcg/plugins/mem.c 2026-05-25 22:03:53.000000000 +0000 @@ -67,7 +67,7 @@ static GMutex lock; static GHashTable *regions; -static gint addr_order(gconstpointer a, gconstpointer b) +static gint addr_order(gconstpointer a, gconstpointer b, gpointer d) { RegionInfo *na = (RegionInfo *) a; RegionInfo *nb = (RegionInfo *) b; @@ -94,7 +94,7 @@ if (do_region_summary) { GList *counts = g_hash_table_get_values(regions); - counts = g_list_sort(counts, addr_order); + counts = g_list_sort_with_data(counts, addr_order, NULL); g_string_printf(out, "Region Base, Reads, Writes, Seen all\n"); diff -Nru qemu-10.0.8+ds/tests/tcg/plugins/syscall.c qemu-10.0.10+ds/tests/tcg/plugins/syscall.c --- qemu-10.0.8+ds/tests/tcg/plugins/syscall.c 2026-02-12 20:30:15.000000000 +0000 +++ qemu-10.0.10+ds/tests/tcg/plugins/syscall.c 2026-05-25 22:03:53.000000000 +0000 @@ -180,7 +180,7 @@ qemu_plugin_outs(out); } -static gint comp_func(gconstpointer ea, gconstpointer eb) +static gint comp_func(gconstpointer ea, gconstpointer eb, gpointer d) { SyscallStats *ent_a = (SyscallStats *) ea; SyscallStats *ent_b = (SyscallStats *) eb; @@ -197,7 +197,7 @@ g_mutex_lock(&lock); GList *entries = g_hash_table_get_values(statistics); - entries = g_list_sort(entries, comp_func); + entries = g_list_sort_with_data(entries, comp_func, NULL); qemu_plugin_outs("syscall no. calls errors\n"); g_list_foreach(entries, print_entry, NULL); diff -Nru qemu-10.0.8+ds/tests/unit/rcutorture.c qemu-10.0.10+ds/tests/unit/rcutorture.c --- qemu-10.0.8+ds/tests/unit/rcutorture.c 2026-02-12 20:30:15.000000000 +0000 +++ qemu-10.0.10+ds/tests/unit/rcutorture.c 2026-05-25 22:03:53.000000000 +0000 @@ -248,7 +248,7 @@ int pc; long long n_reads_local = 0; long long rcu_stress_local[RCU_STRESS_PIPE_LEN + 1] = { 0 }; - volatile int garbage = 0; + volatile int garbage __attribute__ ((unused)) = 0; rcu_register_thread(); diff -Nru qemu-10.0.8+ds/tests/unit/test-block-iothread.c qemu-10.0.10+ds/tests/unit/test-block-iothread.c --- qemu-10.0.8+ds/tests/unit/test-block-iothread.c 2026-02-12 20:30:15.000000000 +0000 +++ qemu-10.0.10+ds/tests/unit/test-block-iothread.c 2026-05-25 22:03:53.000000000 +0000 @@ -270,11 +270,11 @@ int ret; /* Early success: UNMAP not supported */ - ret = blk_pdiscard(blk, 0, 512); + ret = blk_pdiscard(blk, 0, 512, 0); g_assert_cmpint(ret, ==, 0); /* Early error: Negative offset */ - ret = blk_pdiscard(blk, -2, 512); + ret = blk_pdiscard(blk, -2, 512, 0); g_assert_cmpint(ret, ==, -EIO); } diff -Nru qemu-10.0.8+ds/tests/unit/test-io-task.c qemu-10.0.10+ds/tests/unit/test-io-task.c --- qemu-10.0.8+ds/tests/unit/test-io-task.c 2026-02-12 20:30:15.000000000 +0000 +++ qemu-10.0.10+ds/tests/unit/test-io-task.c 2026-05-25 22:03:53.000000000 +0000 @@ -73,6 +73,7 @@ src = qio_task_get_source(task); qio_task_complete(task); + qio_task_free(task); g_assert(obj == src); @@ -84,6 +85,28 @@ } +static void test_task_cancel(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + Object *src; + struct TestTaskData data = { NULL, NULL, false }; + + task = qio_task_new(obj, task_callback, &data, NULL); + src = qio_task_get_source(task); + + qio_task_free(task); + + g_assert(obj == src); + + object_unref(obj); + + g_assert(data.source == NULL); + g_assert(data.err == NULL); + g_assert(data.freed == false); +} + + static void task_data_free(gpointer opaque) { struct TestTaskData *data = opaque; @@ -101,6 +124,7 @@ task = qio_task_new(obj, task_callback, &data, task_data_free); qio_task_complete(task); + qio_task_free(task); object_unref(obj); @@ -123,6 +147,7 @@ qio_task_set_error(task, err); qio_task_complete(task); + qio_task_free(task); object_unref(obj); @@ -260,6 +285,7 @@ module_call_init(MODULE_INIT_QOM); type_register_static(&dummy_info); g_test_add_func("/crypto/task/complete", test_task_complete); + g_test_add_func("/crypto/task/cancel", test_task_cancel); g_test_add_func("/crypto/task/datafree", test_task_data_free); g_test_add_func("/crypto/task/failure", test_task_failure); g_test_add_func("/crypto/task/thread_complete", test_task_thread_complete); diff -Nru qemu-10.0.8+ds/ui/console-vc.c qemu-10.0.10+ds/ui/console-vc.c --- qemu-10.0.8+ds/ui/console-vc.c 2026-02-12 20:30:15.000000000 +0000 +++ qemu-10.0.10+ds/ui/console-vc.c 2026-05-25 22:03:53.000000000 +0000 @@ -899,7 +899,7 @@ break; case 2: /* clear entire screen */ - for (y = 0; y <= s->height; y++) { + for (y = 0; y < s->height; y++) { for (x = 0; x < s->width; x++) { vc_clear_xy(vc, x, y); } diff -Nru qemu-10.0.8+ds/ui/spice-app.c qemu-10.0.10+ds/ui/spice-app.c --- qemu-10.0.8+ds/ui/spice-app.c 2026-02-12 20:30:15.000000000 +0000 +++ qemu-10.0.10+ds/ui/spice-app.c 2026-05-25 22:03:53.000000000 +0000 @@ -153,7 +153,7 @@ if (qemu_name) { app_dir = g_build_filename(g_get_user_runtime_dir(), "qemu", qemu_name, NULL); - if (g_mkdir_with_parents(app_dir, S_IRWXU) < -1) { + if (g_mkdir_with_parents(app_dir, S_IRWXU) < 0) { error_report("Failed to create directory %s: %s", app_dir, strerror(errno)); exit(1); diff -Nru qemu-10.0.8+ds/ui/vnc-jobs.c qemu-10.0.10+ds/ui/vnc-jobs.c --- qemu-10.0.8+ds/ui/vnc-jobs.c 2026-02-12 20:30:15.000000000 +0000 +++ qemu-10.0.10+ds/ui/vnc-jobs.c 2026-05-25 22:03:53.000000000 +0000 @@ -108,11 +108,25 @@ return 1; } +static void vnc_job_free(VncJob *job) +{ + VncRectEntry *entry, *tmp; + + if (!job) { + return; + } + QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) { + /* no need for QLIST_REMOVE(entry, next) */ + g_free(entry); + } + g_free(job); +} + void vnc_job_push(VncJob *job) { vnc_lock_queue(queue); if (queue->exit || QLIST_EMPTY(&job->rectangles)) { - g_free(job); + vnc_job_free(job); } else { QTAILQ_INSERT_TAIL(&queue->jobs, job, next); qemu_cond_broadcast(&queue->cond); @@ -302,6 +316,7 @@ n_rectangles += n; } } + QLIST_REMOVE(entry, next); g_free(entry); } trace_vnc_job_nrects(&vs, job, n_rectangles); @@ -330,7 +345,7 @@ QTAILQ_REMOVE(&queue->jobs, job, next); vnc_unlock_queue(queue); qemu_cond_broadcast(&queue->cond); - g_free(job); + vnc_job_free(job); vs.magic = 0; return 0; } diff -Nru qemu-10.0.8+ds/util/cutils.c qemu-10.0.10+ds/util/cutils.c --- qemu-10.0.8+ds/util/cutils.c 2026-02-12 20:30:15.000000000 +0000 +++ qemu-10.0.10+ds/util/cutils.c 2026-05-25 22:03:53.000000000 +0000 @@ -1165,9 +1165,10 @@ PCWSTR wdir_skipped_root; if (PathCchSkipRoot(wdir, &wdir_skipped_root) == S_OK) { + char *cursor; size = wcsrtombs(NULL, &wdir_skipped_root, 0, &(mbstate_t){0}); - char *cursor = result->str + result->len; g_string_set_size(result, result->len + size); + cursor = result->str + result->len - size; wcsrtombs(cursor, &wdir_skipped_root, size + 1, &(mbstate_t){0}); } else { g_string_append(result, dir); diff -Nru qemu-10.0.8+ds/util/meson.build qemu-10.0.10+ds/util/meson.build --- qemu-10.0.8+ds/util/meson.build 2026-02-12 20:30:15.000000000 +0000 +++ qemu-10.0.10+ds/util/meson.build 2026-05-25 22:03:53.000000000 +0000 @@ -79,6 +79,7 @@ if have_block or have_ga util_ss.add(files('aiocb.c', 'async.c')) + util_ss.add(files('aio-wait.c')) util_ss.add(files('base64.c')) util_ss.add(files('main-loop.c')) util_ss.add(files('qemu-coroutine.c', 'qemu-coroutine-lock.c', 'qemu-coroutine-io.c')) @@ -89,7 +90,6 @@ util_ss.add(files('qemu-sockets.c')) endif if have_block - util_ss.add(files('aio-wait.c')) util_ss.add(files('buffer.c')) util_ss.add(files('bufferiszero.c')) util_ss.add(files('hbitmap.c')) diff -Nru qemu-10.0.8+ds/util/readline.c qemu-10.0.10+ds/util/readline.c --- qemu-10.0.8+ds/util/readline.c 2026-02-12 20:30:15.000000000 +0000 +++ qemu-10.0.10+ds/util/readline.c 2026-05-25 22:03:53.000000000 +0000 @@ -84,7 +84,9 @@ static void readline_insert_char(ReadLineState *rs, int ch) { - if (rs->cmd_buf_index < READLINE_CMD_BUF_SIZE) { + assert(rs->cmd_buf_index <= rs->cmd_buf_size); + + if (rs->cmd_buf_size < READLINE_CMD_BUF_SIZE) { memmove(rs->cmd_buf + rs->cmd_buf_index + 1, rs->cmd_buf + rs->cmd_buf_index, rs->cmd_buf_size - rs->cmd_buf_index);