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: [email protected], Paolo Bonzini <[email protected]>, Richard Henderson <[email protected]> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3223 Signed-off-by: Andrey Polivoda <[email protected]> --- 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 { -- 2.53.0
