1. "The memcpy is definitely wrong, because you're casting a guest address into 
a host address, which is incorrect. You have to use g2h()."
There is no need to use g2h(), Because there are both guest address whether 
source or dest memory. refer to "linux-user/i386/cpu_loop.c" 
target_cpu_copy_regs function, Only use g2h_untagged when convert gdt::base to 
gdt_table. I don't use and modify gdt_table, Only copy gdt::base from source 
CPU to dest CPU. They are same type so no needed to convert by g2h.

2. "I'm actually surprised that you need this for TARGET_X86_64 at all ..."
GDT on QEMU User Mode is Pseudorandom GDT,It is NOT kernel private data 
structures. It is NOT Global Descriptor Table. It IS index table of fs and gs. 
And It is Thread local data. The Memory which gdt::base point can be modified 
by syscall SYS_set_thread_data.

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/tls.c

SYS_get_thread_area and SYS_set_thread_area used on x32 usually, But They can 
be syscalled on x64 when emulate Wow64 environment. 





---------------------------------------------------------------------
From:   Richard Henderson
Subject:        Re: [PATCH] linux-user: fix bug about incorrect base addresss 
of gdt on i386 and x86_64
Date:   Sat, 14 Jan 2023 19:32:59 -1000
User-agent:     Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 
Thunderbird/102.4.2
> On 1/3/23 01:38, fanwenjie wrote:
On linux user mode, CPUX86State::gdt::base from Different CPUX86State Objects 
have same value, It is incorrect! Every CPUX86State::gdt::base Must points to 
independent memory space.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1405
Signed-off-by: fanwenjie <fa...@mail.ustc.edu.cn>
---
  linux-user/i386/cpu_loop.c | 9 +++++++++
  linux-user/main.c          | 7 +++++++
  2 files changed, 16 insertions(+)

diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c
index 865413c..48511cd 100644
--- a/linux-user/i386/cpu_loop.c
+++ b/linux-user/i386/cpu_loop.c
@@ -314,8 +314,17 @@ void cpu_loop(CPUX86State *env)
      }
  }
+static void target_cpu_free(void *obj)
+{
+    CPUArchState* env = ((CPUState*)obj)->env_ptr;
+    target_munmap(env->gdt.base, sizeof(uint64_t) * TARGET_GDT_ENTRIES);
+    g_free(obj);
+}
+
  void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
  {
+    CPUState* cpu = env_cpu(env);
+    OBJECT(cpu)->free = target_cpu_free;
      env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
      env->hflags |= HF_PE_MASK | HF_CPL_MASK;
      if (env->features[FEAT_1_EDX] & CPUID_SSE) {
diff --git a/linux-user/main.c b/linux-user/main.c
index a17fed0..3acd9b4 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -234,6 +234,13 @@ CPUArchState *cpu_copy(CPUArchState *env)
new_cpu->tcg_cflags = cpu->tcg_cflags;
      memcpy(new_env, env, sizeof(CPUArchState));
+#if defined(TARGET_I386) || defined(TARGET_X86_64)
+    new_env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES,
+                                    PROT_READ|PROT_WRITE,
+                                    MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+    memcpy((void*)new_env->gdt.base, (void*)env->gdt.base, sizeof(uint64_t) * 
TARGET_GDT_ENTRIES);
+    OBJECT(new_cpu)->free = OBJECT(cpu)->free;
+#endif

This isn't a fantastic solution, because neither the ldt nor the gdt should be 
mapped into the user address space -- these are kernel private data structures. 
But cpu.h uses a target_ulong, and seg_helper.c is set up to load data from the 
guest, and it would be a medium sized job to address that.

The memcpy is definitely wrong, because you're casting a guest address into a 
host address, which is incorrect. You have to use g2h().

I'm actually surprised that you need this for TARGET_X86_64 at all -- the two 
TLS segments don't really use the GDT at all, since fs_base and gs_base may be 
set directly.

Reply via email to