Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 47 additions & 12 deletions arch/X86/X86DisassemblerDecoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,47 @@ static void setPrefixPresent(struct InternalInstruction *insn, uint8_t prefix)
}
}

/*
* setSegmentOverride - Overrides an instruction's prefix1 based on CPU mode.
*
* @param insn - The instruction to be overridden.
* @param prefix - The segment override to use.
* @param byte - The current decoded prefix byte. Must be a segment override.
*/
static void setSegmentOverride(struct InternalInstruction *insn,
SegmentOverride prefix, uint8_t byte)
{
// In 32-bit or 16-bit mode all segment override prefixes are used.
if (insn->mode != MODE_64BIT) {
insn->segmentOverride = prefix;
insn->prefix1 = byte;
return;
}

// In 64-bit mode, the ES/CS/SS/DS segment overrides should be ignored.
// In the case there are multiple segment overrides, do not override
// an existing FS or GS segment prefix.
switch (insn->prefix1) {
case 0x64: // FS
case 0x65: // GS
return;
}

// If the proposed override is for FS or GS, mark it overridden.
// All other segment prefixes are ignored.
switch (byte) {
case 0x64: // FS
case 0x65: // GS
insn->segmentOverride = prefix;
break;
}

// `prefix1` may later be used to decode the `notrack` prefix.
// The `notrack` prefix reuses the DS segment override, so we
// need to store the prefix even if it is ignored for the segment overrides.
insn->prefix1 = byte;
}

/*
* readPrefixes - Consumes all of an instruction's prefix bytes, and marks the
* instruction as having them. Also sets the instruction's default operand,
Expand Down Expand Up @@ -523,28 +564,22 @@ static int readPrefixes(struct InternalInstruction *insn)
case 0x65: /* GS segment override */
switch (byte) {
case 0x2e:
insn->segmentOverride = SEG_OVERRIDE_CS;
insn->prefix1 = byte;
setSegmentOverride(insn, SEG_OVERRIDE_CS, byte);
break;
case 0x36:
insn->segmentOverride = SEG_OVERRIDE_SS;
insn->prefix1 = byte;
setSegmentOverride(insn, SEG_OVERRIDE_SS, byte);
break;
case 0x3e:
insn->segmentOverride = SEG_OVERRIDE_DS;
insn->prefix1 = byte;
setSegmentOverride(insn, SEG_OVERRIDE_DS, byte);
break;
case 0x26:
insn->segmentOverride = SEG_OVERRIDE_ES;
insn->prefix1 = byte;
setSegmentOverride(insn, SEG_OVERRIDE_ES, byte);
break;
case 0x64:
insn->segmentOverride = SEG_OVERRIDE_FS;
insn->prefix1 = byte;
setSegmentOverride(insn, SEG_OVERRIDE_FS, byte);
break;
case 0x65:
insn->segmentOverride = SEG_OVERRIDE_GS;
insn->prefix1 = byte;
setSegmentOverride(insn, SEG_OVERRIDE_GS, byte);
break;
default:
// debug("Unhandled override");
Expand Down
5 changes: 5 additions & 0 deletions docs/cs_v6_release_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ Nonetheless, we hope this additional information is useful to you.
- Architecture support was added (based on LLVM-18).
- Support for `LITBASE`. Set the `LITBASE` with `cs_option(handle, CS_OPT_LITBASE, litbase_value)`.

**x86-64**

- Decoding of conflicting segment overrides was changed to match CPU behavior:
For instructions with both an FS/GS and a ES/CS/SS/DS overrides the FS/GS override now takes priority, regardless of prefix ordering.

**BPF**

- Added support for eBPF `ATOMIC` class instructions (using Linux mnemonics, not GNU ones. E.g. `acmpxchg64` instead of `axchg`)
Expand Down
293 changes: 293 additions & 0 deletions tests/issues/x86-prefixes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
test_cases:
# Test segment override priority
-
input:
name: "x86-16: rightmost segment override should take priority"
bytes: [ 0x26, 0x65, 0x64, 0x3E, 0x65, 0x2E, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_16 ]
expected:
insns:
-
asm_text: "add byte ptr cs:[bx + si], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_CS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-32: rightmost segment override should take priority"
bytes: [ 0x26, 0x65, 0x64, 0x3E, 0x65, 0x2E, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_32 ]
expected:
insns:
-
asm_text: "add byte ptr cs:[eax], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_CS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-64: rightmost segment override should take priority"
bytes: [ 0x26, 0x65, 0x64, 0x3E, 0x65, 0x2E, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_64 ]
expected:
insns:
-
asm_text: "add byte ptr gs:[rax], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_GS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-16: rightmost segment override should take priority"
bytes: [ 0x3E, 0x3E, 0x26, 0x36, 0x64, 0x36, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_16 ]
expected:
insns:
-
asm_text: "add byte ptr ss:[bx + si], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_SS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-32: rightmost segment override should take priority"
bytes: [ 0x3E, 0x3E, 0x26, 0x36, 0x64, 0x36, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_32 ]
expected:
insns:
-
asm_text: "add byte ptr ss:[eax], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_SS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-64: rightmost segment override should take priority"
bytes: [ 0x3E, 0x3E, 0x26, 0x36, 0x64, 0x36, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_64 ]
expected:
insns:
-
asm_text: "add byte ptr fs:[rax], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_FS, X86_PREFIX_0, X86_PREFIX_0 ]
# Test segment override differences between 16/32 and 64-bit mode with ECDS and FS/GS
-
input:
name: "x86-16: ECSD segment overrides should override FS/GS segment overrides"
bytes: [ 0x64, 0x3E, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_16 ]
expected:
insns:
-
asm_text: "add byte ptr ds:[bx + si], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_DS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-32: ECSD segment overrides should override FS/GS segment overrides"
bytes: [ 0x64, 0x3E, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_32 ]
expected:
insns:
-
asm_text: "add byte ptr ds:[eax], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_DS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-64: ECSD segment overrides should be ignored and leave FS override intact"
bytes: [ 0x64, 0x3E, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_64 ]
expected:
insns:
-
asm_text: "add byte ptr fs:[rax], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_FS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-64: ECSD segment overrides should be ignored"
bytes: [ 0x3E, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_64 ]
expected:
insns:
-
asm_text: "add byte ptr [rax], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_DS, X86_PREFIX_0, X86_PREFIX_0 ]
# Test duplicate segment override prefixes
-
input:
name: "x86-16: Duplicate ES override prefixes should decode successfully"
bytes: [ 0x26, 0x26, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_16 ]
expected:
insns:
-
asm_text: "add byte ptr es:[bx + si], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_ES, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-32: Duplicate ES override prefixes should decode successfully"
bytes: [ 0x26, 0x26, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_32 ]
expected:
insns:
-
asm_text: "add byte ptr es:[eax], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_ES, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-64: Duplicate FS override prefixes should decode successfully"
bytes: [ 0x64, 0x64, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_64 ]
expected:
insns:
-
asm_text: "add byte ptr fs:[rax], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_FS, X86_PREFIX_0, X86_PREFIX_0 ]
# Test invalid REX prefix
-
input:
name: "x86-64: Invalid REX prefix should preserve previous segment override"
bytes: [ 0x64, 0x40, 0x2E, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_64 ]
expected:
insns:
-
asm_text: "add byte ptr fs:[rax], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_FS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-64: Invalid REX prefix should not add ECDS segment override"
bytes: [ 0x2E, 0x40, 0x2E, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_64 ]
expected:
insns:
-
asm_text: "add byte ptr [rax], al"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_CS, X86_PREFIX_0, X86_PREFIX_0 ]
# Test whether `notrack` is correctly decoded
-
input:
name: "x86-16: notrack should decode correctly"
bytes: [ 0x3E, 0xE8, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_16 ]
expected:
insns:
-
asm_text: "notrack call 4"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_DS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-32: notrack should decode correctly"
bytes: [ 0x3E, 0xE8, 0x00, 0x00, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_32 ]
expected:
insns:
-
asm_text: "notrack call 6"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_DS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-64: notrack should decode correctly"
bytes: [ 0x3E, 0xE8, 0x00, 0x00, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_64 ]
expected:
insns:
-
asm_text: "notrack call 6"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_DS, X86_PREFIX_0, X86_PREFIX_0 ]

-
input:
name: "x86-16: notrack should be applied when 0x3E is last segment override prefix"
bytes: [ 0x26, 0x64, 0x3E, 0xE8, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_16 ]
expected:
insns:
-
asm_text: "notrack call 6"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_DS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-32: notrack should be applied when 0x3E is last segment override prefix"
bytes: [ 0x26, 0x64, 0x3E, 0xE8, 0x00, 0x00, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_32 ]
expected:
insns:
-
asm_text: "notrack call 8"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_DS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-64: workaround: notrack should only be applied when 0x3E is last segment override prefix and no FS/GS segment override prefix is active"
bytes: [ 0x26, 0x64, 0x3E, 0xE8, 0x00, 0x00, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_64 ]
expected:
insns:
-
asm_text: "call 8"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_FS, X86_PREFIX_0, X86_PREFIX_0 ]
-
input:
name: "x86-64: workaround: notrack should only be applied when 0x3E is last segment override prefix and no FS/GS segment override prefix is active"
bytes: [ 0x26, 0x2E, 0x3E, 0xE8, 0x00, 0x00, 0x00, 0x00 ]
arch: "CS_ARCH_X86"
options: [ CS_OPT_DETAIL, CS_MODE_64 ]
expected:
insns:
-
asm_text: "notrack call 8"
details:
x86:
prefix: [ X86_PREFIX_0, X86_PREFIX_DS, X86_PREFIX_0, X86_PREFIX_0 ]
Loading