Commit 51aa3f3e05 for qemu.org

commit 51aa3f3e05772c38cb6f6d8e79eca0f6afaaace0
Author: Andrey Polivoda <apolivodaa433@gmail.com>
Date:   Mon Jun 8 19:18:15 2026 +1000

    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>

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
     {