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,
Paolo Bonzini <pbonzini@redhat.com>,
Richard Henderson <richard.henderson@linaro.org>
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3223
Signed-off-by: Andrey Polivoda <apolivodaa433@gmail.com>
---
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