diff --git a/arch/X86/X86DisassemblerDecoder.c b/arch/X86/X86DisassemblerDecoder.c index 0938d56130..5ecf7d555c 100644 --- a/arch/X86/X86DisassemblerDecoder.c +++ b/arch/X86/X86DisassemblerDecoder.c @@ -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, @@ -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"); diff --git a/docs/cs_v6_release_guide.md b/docs/cs_v6_release_guide.md index 159d91a148..a1fe4d55b4 100644 --- a/docs/cs_v6_release_guide.md +++ b/docs/cs_v6_release_guide.md @@ -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`) diff --git a/tests/issues/x86-prefixes.yaml b/tests/issues/x86-prefixes.yaml new file mode 100644 index 0000000000..2731d02beb --- /dev/null +++ b/tests/issues/x86-prefixes.yaml @@ -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 ] \ No newline at end of file