Skip to content

x64: es/cs/ss/ds segment override prefixes should be ignored #2818

@jxors

Description

@jxors

Work environment

Questions Answers
System Capstone runs on OS/arch/bits Ubuntu 24.04 (x86 64-bit)
Capstone module affected x64
Source of Capstone git clone
Version/git commit 8872be6

Instruction bytes giving faulty results

0x64,0x26,0x00,0x00

Expected results

It should be:

addb %al, %fs:(%rax)

Steps to get the wrong result

With cstool:

cstool -d x64 64260000

Additional Logs, screenshots, source code, configuration dump, ...

The current output is:

addb %al, %es:(%rax)

Although (I believe) stacking multiple segment prefixes is technically undefined behavior, CPUs typically completely ignore ES/CS/SS/DS segment overrides and use a previous FS/GS segment override if present. Other disassemblers (Zydis, XED, libopcodes) all copy this behavior.

The fix below seems to work, but I have not been able to determine if there are any unexpected side-effects from not setting segmentOverride while still setting prefix1. Ideally prefix1 should only be set for conditional jumps, as 26 and 36 are also used as branch taken/not taken hints.

diff --git a/arch/X86/X86DisassemblerDecoder.c b/arch/X86/X86DisassemblerDecoder.c
index 0938d561..3b141036 100644
--- a/arch/X86/X86DisassemblerDecoder.c
+++ b/arch/X86/X86DisassemblerDecoder.c
@@ -523,19 +523,27 @@ static int readPrefixes(struct InternalInstruction *insn)
                case 0x65: /* GS segment override */
                        switch (byte) {
                        case 0x2e:
-                               insn->segmentOverride = SEG_OVERRIDE_CS;
+                               if (insn->mode != MODE_64BIT) {
+                                       insn->segmentOverride = SEG_OVERRIDE_CS;
+                               }
                                insn->prefix1 = byte;
                                break;
                        case 0x36:
-                               insn->segmentOverride = SEG_OVERRIDE_SS;
+                               if (insn->mode != MODE_64BIT) {
+                                       insn->segmentOverride = SEG_OVERRIDE_SS;
+                               }
                                insn->prefix1 = byte;
                                break;
                        case 0x3e:
-                               insn->segmentOverride = SEG_OVERRIDE_DS;
+                               if (insn->mode != MODE_64BIT) {
+                                       insn->segmentOverride = SEG_OVERRIDE_DS;
+                               }
                                insn->prefix1 = byte;
                                break;
                        case 0x26:
-                               insn->segmentOverride = SEG_OVERRIDE_ES;
+                               if (insn->mode != MODE_64BIT) {
+                                       insn->segmentOverride = SEG_OVERRIDE_ES;
+                               }
                                insn->prefix1 = byte;
                                break;

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions