@samitouri / QOSamiQemu / commits / 51aa3f3e05

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 <pbonzini@redhat.com> Cc: Richard Henderson <richard.henderson@linaro.org> Fixes: 14ce26e75513 ("x86_64 target support") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3223 Signed-off-by: Andrey Polivoda <apolivodaa433@gmail.com> Link: https://lore.kernel.org/r/20260608091815.31303-1-apolivodaa433@gmail.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Andrey Polivoda committed Jun 8, 2026 at 19:18 UTC 51aa3f3e05772c38cb6f6d8e79eca0f6afaaace0
1 file changed +13 -4
target/i386/tcg/seg_helper.c
+13 -4
@@ -1096,17 +1096,22 @@ void helper_sysret(CPUX86State *env, int dflag)
1096 selector = (env->star >> 48) & 0xffff;
1097 #ifdef TARGET_X86_64
1098 if (env->hflags & HF_LMA_MASK) {
1099 - cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK
1100 - | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK |
1101 - NT_MASK);
1099 if (dflag == 2) {
1100 + uint64_t new_rip = env->regs[R_ECX];
1101 + if (IS_INTEL_CPU(env)) {
1102 + int shift = (get_pg_mode(env) & PG_MODE_LA57) ? 56 : 47;
1103 + int64_t sext = (int64_t)new_rip >> shift;
1104 + if (sext != 0 && sext != -1) {
1105 + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
1106 + }
1107 + }
1108 cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3,
1109 0, 0xffffffff,
1110 DESC_G_MASK | DESC_P_MASK |
1111 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1112 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
1113 DESC_L_MASK);
1109 - env->eip = env->regs[R_ECX];
1114 + env->eip = new_rip;
1115 } else {
1116 cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1117 0, 0xffffffff,
@@ -1120,6 +1125,10 @@ void helper_sysret(CPUX86State *env, int dflag)
1125 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1126 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1127 DESC_W_MASK | DESC_A_MASK);
1128 +
1129 + cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK
1130 + | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK |
1131 + NT_MASK);
1132 } else
1133 #endif
1134 {