From: "Craaijo, Jos" <[email protected]>

On x86, the ES/CS/SS/DS segment override prefixes are null prefixes in
long mode, and should be ignored. (AMD APM Volume 3, Section 1.2.4)

This patch fixes the prefix decoding to correctly ignore the prefixes in
64-bit mode.

Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3391
Signed-off-by: Jos Craaijo <[email protected]>
Tested-by: Yudistira Putra <[email protected]>
Link: 
https://lore.kernel.org/r/20260623-fix-x86-long-mode-segment-override-decoding-v1-1-26d9d4b58...@ou.nl
Signed-off-by: Paolo Bonzini <[email protected]>
---
 target/i386/emulate/x86_decode.c    |  6 ++++++
 tests/tcg/x86_64/segment-prefixes.c | 25 +++++++++++++++++++++++++
 target/i386/tcg/decode-new.c.inc    | 16 ++++++++++++----
 tests/tcg/x86_64/Makefile.target    |  1 +
 4 files changed, 44 insertions(+), 4 deletions(-)
 create mode 100644 tests/tcg/x86_64/segment-prefixes.c

diff --git a/target/i386/emulate/x86_decode.c b/target/i386/emulate/x86_decode.c
index bae1dd4d6f8..34dcac155aa 100644
--- a/target/i386/emulate/x86_decode.c
+++ b/target/i386/emulate/x86_decode.c
@@ -1851,6 +1851,12 @@ static void decode_prefix(CPUX86State *env, struct 
x86_decode *decode)
         case PREFIX_SS_SEG_OVERRIDE:
         case PREFIX_DS_SEG_OVERRIDE:
         case PREFIX_ES_SEG_OVERRIDE:
+            if (x86_is_long_mode(env_cpu(env))) {
+                /* ES/CS/SS/DS segment overrides are ignored in long mode */
+                decode->rex.rex = 0;
+                break;
+            }
+            /* fall through when not in long mode */
         case PREFIX_FS_SEG_OVERRIDE:
         case PREFIX_GS_SEG_OVERRIDE:
             decode->segment_override = byte;
diff --git a/tests/tcg/x86_64/segment-prefixes.c 
b/tests/tcg/x86_64/segment-prefixes.c
new file mode 100644
index 00000000000..a7e6e285b2c
--- /dev/null
+++ b/tests/tcg/x86_64/segment-prefixes.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* See https://gitlab.com/qemu-project/qemu/-/work_items/3391 */
+
+int main()
+{
+    int data = 0;
+
+    /* Ensure that ignored segment override prefixes are actually ignored */
+    asm volatile (
+        "wrgsbase %0\n\t"
+        ".byte 0x65, 0x26\n\t" /* prefixes: GS + ES */
+        "movb $0, 0\n\t"
+        ".byte 0x65, 0x2E\n\t" /* prefixes: GS + CS */
+        "movb $0, 0\n\t"
+        ".byte 0x65, 0x36\n\t" /* prefixes: GS + SS */
+        "movb $0, 0\n\t"
+        ".byte 0x65, 0x3E\n\t" /* prefixes: GS + DS */
+        "movb $0, 0\n\t"
+        :
+        : "r" (&data)
+        : "memory"
+    );
+
+    return 0;
+}
diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc
index ac5964b95de..faf74b00bbd 100644
--- a/target/i386/tcg/decode-new.c.inc
+++ b/target/i386/tcg/decode-new.c.inc
@@ -2816,16 +2816,24 @@ static void disas_insn(DisasContext *s, CPUState *cpu)
         s->prefix |= PREFIX_LOCK;
         goto next_byte;
     case 0x2e:
-        s->override = R_CS;
+        if (!CODE64(s)) {
+            s->override = R_CS;
+        }
         goto next_byte;
     case 0x36:
-        s->override = R_SS;
+        if (!CODE64(s)) {
+            s->override = R_SS;
+        }
         goto next_byte;
     case 0x3e:
-        s->override = R_DS;
+        if (!CODE64(s)) {
+            s->override = R_DS;
+        }
         goto next_byte;
     case 0x26:
-        s->override = R_ES;
+        if (!CODE64(s)) {
+            s->override = R_ES;
+        }
         goto next_byte;
     case 0x64:
         s->override = R_FS;
diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target
index be20fc64e88..c48767fef85 100644
--- a/tests/tcg/x86_64/Makefile.target
+++ b/tests/tcg/x86_64/Makefile.target
@@ -15,6 +15,7 @@ X86_64_TESTS += vsyscall
 X86_64_TESTS += noexec
 X86_64_TESTS += cmpxchg
 X86_64_TESTS += adox
+X86_64_TESTS += segment-prefixes
 X86_64_TESTS += test-1648
 X86_64_TESTS += test-2175
 X86_64_TESTS += cross-modifying-code
-- 
2.55.0


Reply via email to