From 21069e6b35bcff7da2e8605b5100d227ddbeb7c1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 22 Jun 2026 16:39:30 +0200 Subject: [PATCH 1/5] target/i386: tcg: remove FIXME Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 83c889cc3b..e4ef5a8758 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -3424,7 +3424,9 @@ static void gen_rot_overflow(X86DecodedInsn *decode, TCGv result, TCGv old, /* * RCx operations are invariant modulo 8*operand_size+1. For 8 and 16-bit operands, * this is less than 0x1f (the mask applied by gen_shift_count) so reduce further. - * FIXME: are flags updated if the count is nonzero, but a multiple of (8 << op) + 1? + * A count that is a nonzero multiple of (8 << op) + 1 reduces to 0 and leaves + * CF and OF unmodified (confirmed against hardware that rcl $9,%al behaves + * exactly like rcl $0,%al). */ static MemOp gen_rotc_count(DisasContext *s, X86DecodedInsn *decode, bool *can_be_zero, TCGv *count, int unit) From 0e48aaae735f03592a619c69f476674fd2055823 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 13 Jul 2026 19:12:36 -0700 Subject: [PATCH 2/5] meson: propagate qemu_ldflags to Rust link step Sanitizer flags are added to qemu_ldflags and applied globally for all_languages, but all_languages never includes 'rust'. Fixes link errors like the ones below: -Wl,-rpath,$ORIGIN/../qemu-macros:/lib/rustlib/x86_64-unknown-linux-gnu/lib" = note: some arguments are omitted. use `--verbose` to show all linker arguments = note: rust-lld: error: undefined symbol: __ubsan_handle_type_mismatch_v1 >>> referenced by event-loop-base.c:104 (../qemu_before_fix/event-loop-base.c:104) >>> libevent-loop-base.a.p/event-loop-base.c.o:(event_loop_base_class_init) >>> referenced by event-loop-base.c:105 (../qemu_before_fix/event-loop-base.c:105) >>> libevent-loop-base.a.p/event-loop-base.c.o:(event_loop_base_class_init) >>> referenced by event-loop-base.c:58 (../qemu_before_fix/event-loop-base.c:58) >>> libevent-loop-base.a.p/event-loop-base.c.o:(event_loop_base_set_param) >>> referenced 11240 more times Signed-off-by: Brian Cain Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/r/20260714021236.2361604-1-brian.cain@oss.qualcomm.com Signed-off-by: Paolo Bonzini --- meson.build | 3 +++ 1 file changed, 3 insertions(+) diff --git a/meson.build b/meson.build index 164328ded8..5f42bbf40c 100644 --- a/meson.build +++ b/meson.build @@ -767,6 +767,9 @@ endif add_project_arguments(cc.get_supported_arguments(qemu_common_flags + qemu_cflags + warn_flags), native: false, language: 'c') add_global_link_arguments(qemu_ldflags, native: false, language: all_languages) +if have_rust + add_global_link_arguments(qemu_ldflags, native: false, language: 'rust') +endif if 'cpp' in all_languages add_project_arguments(cxx.get_supported_arguments(qemu_common_flags + qemu_cxxflags), From 51aa3f3e05772c38cb6f6d8e79eca0f6afaaace0 Mon Sep 17 00:00:00 2001 From: Andrey Polivoda Date: Mon, 8 Jun 2026 19:18:15 +1000 Subject: [PATCH 3/5] target/i386: helper_sysret(): Check that RCX contains a canonical address when emulating an Intel CPU Intel and AMD CPUs implement SYSRETQ instruction differently. One of these differences is whether a canonicality check of the address that will be loaded to RIP is performed: Intel CPUs do this check, AMD CPUs don't. Currently, QEMU does not perform this check when emulating Intel CPUs. This patch corrects this by implementing the canonlicality check on a new RIP value from RCX and performing it only when emulating Intel CPUs. Flags and segment registers' caches are updated only after checking the new RIP value to ensure that CPU state is not modified in case the #GP(0) exception is raised due to the check failure. Cc: qemu-devel@nongnu.org Cc: Paolo Bonzini Cc: Richard Henderson Fixes: 14ce26e75513 ("x86_64 target support") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3223 Signed-off-by: Andrey Polivoda Link: https://lore.kernel.org/r/20260608091815.31303-1-apolivodaa433@gmail.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/seg_helper.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 58aac72011..d5c174b7fd 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -1096,17 +1096,22 @@ void helper_sysret(CPUX86State *env, int dflag) selector = (env->star >> 48) & 0xffff; #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { - cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK - | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | - NT_MASK); if (dflag == 2) { + uint64_t new_rip = env->regs[R_ECX]; + if (IS_INTEL_CPU(env)) { + int shift = (get_pg_mode(env) & PG_MODE_LA57) ? 56 : 47; + int64_t sext = (int64_t)new_rip >> shift; + if (sext != 0 && sext != -1) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + } cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3, 0, 0xffffffff, DESC_G_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK); - env->eip = env->regs[R_ECX]; + env->eip = new_rip; } else { cpu_x86_load_seg_cache(env, R_CS, selector | 3, 0, 0xffffffff, @@ -1120,6 +1125,10 @@ void helper_sysret(CPUX86State *env, int dflag) DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | DESC_W_MASK | DESC_A_MASK); + + cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK + | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | + NT_MASK); } else #endif { From d149a11c6c825e182cfb5db7d1d1492a049e82ab Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 21 Jul 2026 17:22:11 +0200 Subject: [PATCH 4/5] scsi-disk: protect against guest sending truncated data for MODE SELECT commands scsi-disk has a MODE SELECT path where a truncated mode page can be allowed by a compatibility quirk, but the parser continues to use the page's declared length rather than the number of bytes actually remaining in the request buffer. This means that scsi_disk_check_mode_select() and scsi_disk_apply_mode_select() can read beyond the valid part of inbuf[], potentially up to the emulated age's length. Clamping page_len (the size of the page) to len (whatever the guest provided) ensures that scsi_disk_check_mode_select() and scsi_disk_apply_mode_select() do not access anything beyond bounds; however, this requires care to accept and handle truncated input in those two functions. In particular, until scsi_disk_check_mode_select()'s first call to mode_sense_page() the number of bytes to be cleared in mode_current[] is unknown, so zero it completely. And for everything else, be conservative and use len when providing inputs to other functions; but at the same time, ensure all accesses to inbuf[] are bound by expected_len. Note that pages longer than the emulated one are still rejected. Fixes: 389e18eb9aa4 ("scsi-disk: add SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED quirk for Macintosh") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4051 Tested-by: Mark Cave-Ayland Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-disk.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 5ba5b46c4f..85d0bd0b81 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1524,7 +1524,7 @@ static void scsi_disk_emulate_read_data(SCSIRequest *req) static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, uint8_t *inbuf, int inlen) { - uint8_t mode_current[SCSI_MAX_MODE_LEN]; + uint8_t mode_current[SCSI_MAX_MODE_LEN] = { 0 }; uint8_t mode_changeable[SCSI_MAX_MODE_LEN]; uint8_t *p; int len, expected_len, changeable_len, i; @@ -1543,21 +1543,21 @@ static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, } p = mode_current; - memset(mode_current, 0, inlen + 2); len = mode_sense_page(s, page, &p, 0); - if (len < 0 || len != expected_len) { + /* The guest may send a truncated page, but not a longer one. */ + if (len < 0 || expected_len > len) { return -1; } p = mode_changeable; - memset(mode_changeable, 0, inlen + 2); + memset(mode_changeable, 0, len); changeable_len = mode_sense_page(s, page, &p, 1); assert(changeable_len == len); /* Check that unchangeable bits are the same as what MODE SENSE * would return. */ - for (i = 2; i < len; i++) { + for (i = 2; i < expected_len; i++) { if (((mode_current[i] ^ inbuf[i - 2]) & ~mode_changeable[i]) != 0) { return -1; } @@ -1565,11 +1565,15 @@ static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, return 0; } -static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, uint8_t *p) +/* Note p may be truncated, so check any bytes you access against len. */ +static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, + uint8_t *p, int len) { switch (page) { case MODE_PAGE_CACHING: - blk_set_enable_write_cache(s->qdev.conf.blk, (p[0] & 4) != 0); + if (len > 0) { + blk_set_enable_write_cache(s->qdev.conf.blk, (p[0] & 4) != 0); + } break; default: @@ -1612,6 +1616,7 @@ static int mode_select_pages(SCSIDiskReq *r, uint8_t *p, int len, bool change) goto invalid_param_len; } trace_scsi_disk_mode_select_page_truncated(page, page_len, len); + page_len = len; } if (!change) { @@ -1619,7 +1624,7 @@ static int mode_select_pages(SCSIDiskReq *r, uint8_t *p, int len, bool change) goto invalid_param; } } else { - scsi_disk_apply_mode_select(s, page, p); + scsi_disk_apply_mode_select(s, page, p, page_len); } p += page_len; From e2da3d92744d12b0c2e554ed533a4c9011dbd975 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 21 Jul 2026 17:56:42 +0200 Subject: [PATCH 5/5] scsi-disk: fix off by one in assertion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When documenting the invariant that mode pages need to fit the smallest output buffer of all callers (which is SCSI_MAX_MODE_LEN), the expression used by the assertion was incorrect. Even though SCSI_MAX_MODE_LEN is indeed 256, using "length < 256" had two issues: 1) it used the wrong operator, since "length < ..." is more related to having room for extra data; 2) it missed the extra two bytes for page number and length. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-disk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 85d0bd0b81..1b0cce128c 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1321,7 +1321,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, return -1; } - assert(length < 256); + assert(length + 2 <= SCSI_MAX_MODE_LEN); (*p_outbuf)[0] = page; (*p_outbuf)[1] = length; *p_outbuf += length + 2;