target/i386: Fix 32-bit wrapping of pc/eip computation
In 32-bit mode, pc = eip + cs_base is also 32-bit, and must wrap.
Failure to do so results in incorrect memory exceptions to the guest.
Before 732d548732, this was implicitly done via truncation to
target_ulong but only in qemu-system-i386, not qemu-system-x86_64.
To fix this, we must add conditional zero-extensions.
Since we have to test for 32 vs 64-bit anyway, note that cs_base
is always zero in 64-bit mode.
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2022
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20231212172510.103305-1-richard.henderson@linaro.org>
			
			
This commit is contained in:
		| @@ -2324,10 +2324,15 @@ static inline int cpu_mmu_index_kernel(CPUX86State *env) | ||||
| static inline void cpu_get_tb_cpu_state(CPUX86State *env, vaddr *pc, | ||||
|                                         uint64_t *cs_base, uint32_t *flags) | ||||
| { | ||||
|     *cs_base = env->segs[R_CS].base; | ||||
|     *pc = *cs_base + env->eip; | ||||
|     *flags = env->hflags | | ||||
|         (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK | AC_MASK)); | ||||
|     if (env->hflags & HF_CS64_MASK) { | ||||
|         *cs_base = 0; | ||||
|         *pc = env->eip; | ||||
|     } else { | ||||
|         *cs_base = env->segs[R_CS].base; | ||||
|         *pc = (uint32_t)(*cs_base + env->eip); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void do_cpu_init(X86CPU *cpu); | ||||
|   | ||||
| @@ -52,7 +52,12 @@ static void x86_cpu_synchronize_from_tb(CPUState *cs, | ||||
|     /* The instruction pointer is always up to date with CF_PCREL. */ | ||||
|     if (!(tb_cflags(tb) & CF_PCREL)) { | ||||
|         CPUX86State *env = cpu_env(cs); | ||||
|         env->eip = tb->pc - tb->cs_base; | ||||
|  | ||||
|         if (tb->flags & HF_CS64_MASK) { | ||||
|             env->eip = tb->pc; | ||||
|         } else { | ||||
|             env->eip = (uint32_t)(tb->pc - tb->cs_base); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -66,8 +71,10 @@ static void x86_restore_state_to_opc(CPUState *cs, | ||||
|  | ||||
|     if (tb_cflags(tb) & CF_PCREL) { | ||||
|         env->eip = (env->eip & TARGET_PAGE_MASK) | data[0]; | ||||
|     } else if (tb->flags & HF_CS64_MASK) { | ||||
|         env->eip = data[0]; | ||||
|     } else { | ||||
|         env->eip = data[0] - tb->cs_base; | ||||
|         env->eip = (uint32_t)(data[0] - tb->cs_base); | ||||
|     } | ||||
|     if (cc_op != CC_OP_DYNAMIC) { | ||||
|         env->cc_op = cc_op; | ||||
|   | ||||
| @@ -552,8 +552,10 @@ static void gen_update_eip_cur(DisasContext *s) | ||||
|     assert(s->pc_save != -1); | ||||
|     if (tb_cflags(s->base.tb) & CF_PCREL) { | ||||
|         tcg_gen_addi_tl(cpu_eip, cpu_eip, s->base.pc_next - s->pc_save); | ||||
|     } else if (CODE64(s)) { | ||||
|         tcg_gen_movi_tl(cpu_eip, s->base.pc_next); | ||||
|     } else { | ||||
|         tcg_gen_movi_tl(cpu_eip, s->base.pc_next - s->cs_base); | ||||
|         tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->base.pc_next - s->cs_base)); | ||||
|     } | ||||
|     s->pc_save = s->base.pc_next; | ||||
| } | ||||
| @@ -563,8 +565,10 @@ static void gen_update_eip_next(DisasContext *s) | ||||
|     assert(s->pc_save != -1); | ||||
|     if (tb_cflags(s->base.tb) & CF_PCREL) { | ||||
|         tcg_gen_addi_tl(cpu_eip, cpu_eip, s->pc - s->pc_save); | ||||
|     } else if (CODE64(s)) { | ||||
|         tcg_gen_movi_tl(cpu_eip, s->base.pc_next); | ||||
|     } else { | ||||
|         tcg_gen_movi_tl(cpu_eip, s->pc - s->cs_base); | ||||
|         tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->base.pc_next - s->cs_base)); | ||||
|     } | ||||
|     s->pc_save = s->pc; | ||||
| } | ||||
| @@ -610,8 +614,10 @@ static TCGv eip_next_tl(DisasContext *s) | ||||
|         TCGv ret = tcg_temp_new(); | ||||
|         tcg_gen_addi_tl(ret, cpu_eip, s->pc - s->pc_save); | ||||
|         return ret; | ||||
|     } else if (CODE64(s)) { | ||||
|         return tcg_constant_tl(s->pc); | ||||
|     } else { | ||||
|         return tcg_constant_tl(s->pc - s->cs_base); | ||||
|         return tcg_constant_tl((uint32_t)(s->pc - s->cs_base)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -622,8 +628,10 @@ static TCGv eip_cur_tl(DisasContext *s) | ||||
|         TCGv ret = tcg_temp_new(); | ||||
|         tcg_gen_addi_tl(ret, cpu_eip, s->base.pc_next - s->pc_save); | ||||
|         return ret; | ||||
|     } else if (CODE64(s)) { | ||||
|         return tcg_constant_tl(s->base.pc_next); | ||||
|     } else { | ||||
|         return tcg_constant_tl(s->base.pc_next - s->cs_base); | ||||
|         return tcg_constant_tl((uint32_t)(s->base.pc_next - s->cs_base)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -2837,6 +2845,10 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) | ||||
|         } | ||||
|     } | ||||
|     new_eip &= mask; | ||||
|     new_pc = new_eip + s->cs_base; | ||||
|     if (!CODE64(s)) { | ||||
|         new_pc = (uint32_t)new_pc; | ||||
|     } | ||||
|  | ||||
|     gen_update_cc_op(s); | ||||
|     set_cc_op(s, CC_OP_DYNAMIC); | ||||
| @@ -2854,8 +2866,7 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (use_goto_tb && | ||||
|         translator_use_goto_tb(&s->base, new_eip + s->cs_base)) { | ||||
|     if (use_goto_tb && translator_use_goto_tb(&s->base, new_pc)) { | ||||
|         /* jump to same page: we can use a direct jump */ | ||||
|         tcg_gen_goto_tb(tb_num); | ||||
|         if (!(tb_cflags(s->base.tb) & CF_PCREL)) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user