Skip to content

Commit 120e0e7

Browse files
committed
Fixed xchg aliasing (Fixes #560)
1 parent bffbb61 commit 120e0e7

File tree

4 files changed

+40
-18
lines changed

4 files changed

+40
-18
lines changed

include/Zydis/Internal/EncoderData.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,8 @@ typedef struct ZydisEncodableInstruction_
194194
* (high bit in `REX.R`). Encoder uses swappable definitions to take advantage of this
195195
* optimization opportunity.
196196
*
197-
* Second use of this field is to handle special case for `mov` instruction. This particular
198-
* conflict is described in detail inside `ZydisHandleSwappableDefinition`.
197+
* This field is also used to handle special cases for optimizations. Those opportunities are
198+
* described inside `ZydisHandleSwappableDefinition`.
199199
*/
200200
ZyanU8 swappable ZYAN_BITFIELD(1);
201201
} ZydisEncodableInstruction;

src/Encoder.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2900,14 +2900,34 @@ static ZyanBool ZydisHandleSwappableDefinition(ZydisEncoderInstructionMatch *mat
29002900
if (match->request->mnemonic == ZYDIS_MNEMONIC_MOV)
29012901
{
29022902
const ZyanU8 imm_size = ZydisGetSignedImmSize(match->request->operands[1].imm.s);
2903-
if ((match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
2904-
(match->eosz == 64) &&
2905-
(imm_size < 64))
2903+
return (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
2904+
(match->eosz == 64) &&
2905+
(imm_size < 64);
2906+
}
2907+
2908+
// `xchg ax, ax`, `xchg eax, eax`, `xchg rax, rax` can be encoded as single-byte `nop` (opcode
2909+
// 0x90, see `xchg` documentation in Intel SDM Vol. 2C). However in 64-bit mode operations
2910+
// on 32-bit operands zero-extend results to full 64 bits (Intel SDM Vol. 1, 3.4.1), so
2911+
// `xchg eax, eax` should not be aliased in this mode.
2912+
if (match->request->mnemonic == ZYDIS_MNEMONIC_XCHG)
2913+
{
2914+
ZYAN_ASSERT((match->request->operand_count == 2) &&
2915+
(match->request->operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER));
2916+
switch (match->request->operands[0].reg.value)
29062917
{
2907-
return ZYAN_TRUE;
2918+
case ZYDIS_REGISTER_AX:
2919+
case ZYDIS_REGISTER_RAX:
2920+
match->eosz = 0;
2921+
return ZYAN_FALSE;
2922+
case ZYDIS_REGISTER_EAX:
2923+
match->eosz = 0;
2924+
return ZydisGetMachineModeWidth(match->request->machine_mode) == 64;
2925+
default:
2926+
return ZYAN_FALSE;
29082927
}
29092928
}
29102929

2930+
// Check for possible `VEX` optimization
29112931
ZYAN_ASSERT((match->request->operand_count == 2) || (match->request->operand_count == 3));
29122932
const ZyanU8 src_index = (match->request->operand_count == 3) ? 2 : 1;
29132933
const ZyanI8 dest_id = ZydisRegisterGetId(match->request->operands[0].reg.value);

src/Generated/EncoderTables.inc

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,18 +1765,19 @@ const ZydisEncoderLookupEntry encoder_instruction_lookup[] =
17651765
{ 0x1E47, 1 },
17661766
{ 0x1E48, 4 },
17671767
{ 0x1E4C, 1 },
1768-
{ 0x1E4D, 6 },
1768+
{ 0x1E4D, 5 },
1769+
{ 0x1E52, 1 },
17691770
{ 0x1E53, 1 },
17701771
{ 0x1E54, 1 },
17711772
{ 0x1E55, 1 },
17721773
{ 0x1E56, 1 },
17731774
{ 0x1E57, 1 },
17741775
{ 0x1E58, 1 },
17751776
{ 0x1E59, 1 },
1776-
{ 0x1E5A, 1 },
1777-
{ 0x1E5B, 18 },
1778-
{ 0x1E6D, 2 },
1779-
{ 0x1E6F, 2 },
1777+
{ 0x1E5A, 18 },
1778+
{ 0x1E6C, 2 },
1779+
{ 0x1E6E, 2 },
1780+
{ 0x1E70, 1 },
17801781
{ 0x1E71, 1 },
17811782
{ 0x1E72, 1 },
17821783
{ 0x1E73, 1 },
@@ -1792,10 +1793,9 @@ const ZydisEncoderLookupEntry encoder_instruction_lookup[] =
17921793
{ 0x1E7D, 1 },
17931794
{ 0x1E7E, 1 },
17941795
{ 0x1E7F, 1 },
1795-
{ 0x1E80, 1 },
1796-
{ 0x1E81, 2 },
1796+
{ 0x1E80, 2 },
1797+
{ 0x1E82, 1 },
17971798
{ 0x1E83, 1 },
1798-
{ 0x1E84, 1 },
17991799
};
18001800

18011801
const ZydisEncodableInstruction encoder_instructions[] =
@@ -9557,8 +9557,7 @@ const ZydisEncodableInstruction encoder_instructions[] =
95579557
{ 0x0767, 0x0002, 0xC1, 0xC0, ZYDIS_INSTRUCTION_ENCODING_LEGACY, ZYDIS_OPCODE_MAP_0F, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_MANDATORY_PREFIX_NONE, ZYAN_FALSE, ZYDIS_VECTOR_LENGTH_INVALID, ZYDIS_SIZE_HINT_NONE, ZYAN_FALSE },
95589558
{ 0x0768, 0x000A, 0xC1, 0x00, ZYDIS_INSTRUCTION_ENCODING_LEGACY, ZYDIS_OPCODE_MAP_0F, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_MANDATORY_PREFIX_NONE, ZYAN_FALSE, ZYDIS_VECTOR_LENGTH_INVALID, ZYDIS_SIZE_HINT_NONE, ZYAN_FALSE },
95599559
{ 0x0769, 0x0019, 0xC7, 0xF8, ZYDIS_INSTRUCTION_ENCODING_LEGACY, ZYDIS_OPCODE_MAP_DEFAULT, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_MANDATORY_PREFIX_NONE, ZYAN_FALSE, ZYDIS_VECTOR_LENGTH_INVALID, ZYDIS_SIZE_HINT_OSZ, ZYAN_FALSE },
9560-
{ 0x076E, 0x0002, 0x90, 0x00, ZYDIS_INSTRUCTION_ENCODING_LEGACY, ZYDIS_OPCODE_MAP_DEFAULT, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_MANDATORY_PREFIX_NONE, ZYAN_FALSE, ZYDIS_VECTOR_LENGTH_INVALID, ZYDIS_SIZE_HINT_NONE, ZYAN_FALSE },
9561-
{ 0x076F, 0x0002, 0x91, 0x00, ZYDIS_INSTRUCTION_ENCODING_LEGACY, ZYDIS_OPCODE_MAP_DEFAULT, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_MANDATORY_PREFIX_NONE, ZYAN_FALSE, ZYDIS_VECTOR_LENGTH_INVALID, ZYDIS_SIZE_HINT_NONE, ZYAN_FALSE },
9560+
{ 0x076E, 0x0002, 0x90, 0x00, ZYDIS_INSTRUCTION_ENCODING_LEGACY, ZYDIS_OPCODE_MAP_DEFAULT, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_MANDATORY_PREFIX_NONE, ZYAN_FALSE, ZYDIS_VECTOR_LENGTH_INVALID, ZYDIS_SIZE_HINT_NONE, ZYAN_TRUE },
95629561
{ 0x076A, 0x0002, 0x86, 0xC0, ZYDIS_INSTRUCTION_ENCODING_LEGACY, ZYDIS_OPCODE_MAP_DEFAULT, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_MANDATORY_PREFIX_NONE, ZYAN_FALSE, ZYDIS_VECTOR_LENGTH_INVALID, ZYDIS_SIZE_HINT_NONE, ZYAN_FALSE },
95639562
{ 0x076B, 0x000A, 0x86, 0x00, ZYDIS_INSTRUCTION_ENCODING_LEGACY, ZYDIS_OPCODE_MAP_DEFAULT, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_MANDATORY_PREFIX_NONE, ZYAN_FALSE, ZYDIS_VECTOR_LENGTH_INVALID, ZYDIS_SIZE_HINT_NONE, ZYAN_FALSE },
95649563
{ 0x076C, 0x0002, 0x87, 0xC0, ZYDIS_INSTRUCTION_ENCODING_LEGACY, ZYDIS_OPCODE_MAP_DEFAULT, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_WIDTH_16 | ZYDIS_WIDTH_32 | ZYDIS_WIDTH_64, ZYDIS_MANDATORY_PREFIX_NONE, ZYAN_FALSE, ZYDIS_VECTOR_LENGTH_INVALID, ZYDIS_SIZE_HINT_NONE, ZYAN_FALSE },

tools/ZydisFuzzShared.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ void ZydisValidateInstructionIdentity(const ZydisDecodedInstruction* insn1,
244244
// TODO: Probably a good idea to input validate operand_counts to this function
245245
// TODO: I don't like accessing buffers without having their actual sizes available...
246246

247-
// Special case, `xchg rAX, rAX` is an alias for `NOP`
247+
// Special case, `xchg rAX, rAX` is an alias for `NOP` except `xchg eax, eax` in 64-bit mode
248248
if ((insn1->mnemonic == ZYDIS_MNEMONIC_XCHG) &&
249249
(insn1->operand_count == 2) &&
250250
(operands1[0].type == ZYDIS_OPERAND_TYPE_REGISTER) &&
@@ -255,9 +255,12 @@ void ZydisValidateInstructionIdentity(const ZydisDecodedInstruction* insn1,
255255
switch (operands1[0].reg.value)
256256
{
257257
case ZYDIS_REGISTER_AX:
258-
case ZYDIS_REGISTER_EAX:
259258
case ZYDIS_REGISTER_RAX:
260259
return;
260+
case ZYDIS_REGISTER_EAX:
261+
if (insn1->machine_mode != ZYDIS_MACHINE_MODE_LONG_64)
262+
return;
263+
break;
261264
default:
262265
break;
263266
}

0 commit comments

Comments
 (0)