308 lines
12 KiB
C
308 lines
12 KiB
C
|
/***************************************************************************************************
|
||
|
|
||
|
Zyan Disassembler Library (Zydis)
|
||
|
|
||
|
Original Author : Mappa
|
||
|
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
* of this software and associated documentation files (the "Software"), to deal
|
||
|
* in the Software without restriction, including without limitation the rights
|
||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
* copies of the Software, and to permit persons to whom the Software is
|
||
|
* furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in all
|
||
|
* copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
* SOFTWARE.
|
||
|
|
||
|
***************************************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* @file
|
||
|
*
|
||
|
* This file implements fuzz target for encoder.
|
||
|
*/
|
||
|
|
||
|
#include "ZydisFuzzShared.h"
|
||
|
|
||
|
/* ============================================================================================== */
|
||
|
/* Fuzz target */
|
||
|
/* ============================================================================================== */
|
||
|
|
||
|
// TODO: This could check `EVEX`/`MVEX` stuff as well
|
||
|
void ZydisCompareRequestToInstruction(const ZydisEncoderRequest *request,
|
||
|
const ZydisDecodedInstruction *insn, const ZydisDecodedOperand* operands, const ZyanU8 *insn_bytes)
|
||
|
{
|
||
|
// Special case, `xchg rAX, rAX` is an alias for `NOP`
|
||
|
if ((request->mnemonic == ZYDIS_MNEMONIC_XCHG) &&
|
||
|
(request->operand_count == 2) &&
|
||
|
(request->operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER) &&
|
||
|
(request->operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER) &&
|
||
|
(request->operands[0].reg.value == request->operands[1].reg.value) &&
|
||
|
(insn->mnemonic == ZYDIS_MNEMONIC_NOP))
|
||
|
{
|
||
|
switch (request->operands[0].reg.value)
|
||
|
{
|
||
|
case ZYDIS_REGISTER_AX:
|
||
|
case ZYDIS_REGISTER_EAX:
|
||
|
case ZYDIS_REGISTER_RAX:
|
||
|
return;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Handle possible KNC overlap
|
||
|
ZydisDecodedInstruction knc_insn;
|
||
|
ZydisDecodedOperand knc_operands[ZYDIS_MAX_OPERAND_COUNT];
|
||
|
if (request->mnemonic != insn->mnemonic)
|
||
|
{
|
||
|
ZydisDecoder decoder;
|
||
|
ZydisStackWidth stack_width = (ZydisStackWidth)(insn->stack_width >> 5);
|
||
|
if (!ZYAN_SUCCESS(ZydisDecoderInit(&decoder, insn->machine_mode, stack_width)))
|
||
|
{
|
||
|
fputs("Failed to initialize decoder\n", ZYAN_STDERR);
|
||
|
abort();
|
||
|
}
|
||
|
if (!ZYAN_SUCCESS(ZydisDecoderEnableMode(&decoder, ZYDIS_DECODER_MODE_KNC, ZYAN_TRUE)))
|
||
|
{
|
||
|
fputs("Failed to enable KNC mode\n", ZYAN_STDERR);
|
||
|
abort();
|
||
|
}
|
||
|
if (!ZYAN_SUCCESS(ZydisDecoderDecodeFull(&decoder, insn_bytes, insn->length, &knc_insn,
|
||
|
knc_operands)))
|
||
|
{
|
||
|
fputs("Failed to decode instruction\n", ZYAN_STDERR);
|
||
|
abort();
|
||
|
}
|
||
|
insn = &knc_insn;
|
||
|
operands = knc_operands;
|
||
|
}
|
||
|
|
||
|
ZyanBool prefixes_match = ((insn->attributes & request->prefixes) == request->prefixes);
|
||
|
if (!prefixes_match &&
|
||
|
(request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) &&
|
||
|
(request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_DS))
|
||
|
{
|
||
|
// Encoder allows specifying DS override even when it might be interpreted as NOTRACK
|
||
|
ZyanU64 acceptable_prefixes = (request->prefixes & (~ZYDIS_ATTRIB_HAS_SEGMENT_DS)) |
|
||
|
ZYDIS_ATTRIB_HAS_NOTRACK;
|
||
|
prefixes_match = ((insn->attributes & acceptable_prefixes) == acceptable_prefixes);
|
||
|
}
|
||
|
if ((request->machine_mode != insn->machine_mode) ||
|
||
|
(request->mnemonic != insn->mnemonic) ||
|
||
|
(request->operand_count != insn->operand_count_visible) ||
|
||
|
!prefixes_match)
|
||
|
{
|
||
|
fputs("Basic instruction attributes mismatch\n", ZYAN_STDERR);
|
||
|
abort();
|
||
|
}
|
||
|
|
||
|
for (ZyanU8 i = 0; i < insn->operand_count_visible; ++i)
|
||
|
{
|
||
|
const ZydisEncoderOperand *op1 = &request->operands[i];
|
||
|
const ZydisDecodedOperand *op2 = &operands[i];
|
||
|
if (op1->type != op2->type)
|
||
|
{
|
||
|
fprintf(ZYAN_STDERR, "Mismatch for operand %u\n", i);
|
||
|
abort();
|
||
|
}
|
||
|
switch (op1->type)
|
||
|
{
|
||
|
case ZYDIS_OPERAND_TYPE_REGISTER:
|
||
|
if (op1->reg.value != op2->reg.value)
|
||
|
{
|
||
|
fprintf(ZYAN_STDERR, "Mismatch for register operand %u\n", i);
|
||
|
abort();
|
||
|
}
|
||
|
break;
|
||
|
case ZYDIS_OPERAND_TYPE_MEMORY:
|
||
|
if ((op1->mem.base != op2->mem.base) ||
|
||
|
(op1->mem.index != op2->mem.index) ||
|
||
|
(op1->mem.scale != op2->mem.scale && op2->mem.type != ZYDIS_MEMOP_TYPE_MIB) ||
|
||
|
(op1->mem.displacement != op2->mem.disp.value))
|
||
|
{
|
||
|
ZyanBool acceptable_mismatch = ZYAN_FALSE;
|
||
|
if (op1->mem.displacement != op2->mem.disp.value)
|
||
|
{
|
||
|
if ((op2->mem.disp.has_displacement) &&
|
||
|
(op1->mem.index == ZYDIS_REGISTER_NONE) &&
|
||
|
((op1->mem.base == ZYDIS_REGISTER_NONE) ||
|
||
|
(op1->mem.base == ZYDIS_REGISTER_EIP) ||
|
||
|
(op1->mem.base == ZYDIS_REGISTER_RIP)))
|
||
|
{
|
||
|
ZyanU64 addr;
|
||
|
ZydisCalcAbsoluteAddress(insn, op2, 0, &addr);
|
||
|
acceptable_mismatch = ((ZyanU64)op1->mem.displacement == addr);
|
||
|
}
|
||
|
if ((insn->machine_mode == ZYDIS_MACHINE_MODE_REAL_16) ||
|
||
|
(insn->machine_mode == ZYDIS_MACHINE_MODE_LEGACY_16) ||
|
||
|
(insn->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_16) ||
|
||
|
(insn->stack_width == 16) ||
|
||
|
(insn->address_width == 16))
|
||
|
{
|
||
|
acceptable_mismatch = ((op1->mem.displacement & 0xFFFF) ==
|
||
|
(op2->mem.disp.value & 0xFFFF));
|
||
|
}
|
||
|
}
|
||
|
if (!acceptable_mismatch)
|
||
|
{
|
||
|
fprintf(ZYAN_STDERR, "Mismatch for memory operand %u\n", i);
|
||
|
abort();
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case ZYDIS_OPERAND_TYPE_POINTER:
|
||
|
if ((op1->ptr.segment != op2->ptr.segment) ||
|
||
|
(op1->ptr.offset != op2->ptr.offset))
|
||
|
{
|
||
|
fprintf(ZYAN_STDERR, "Mismatch for pointer operand %u\n", i);
|
||
|
abort();
|
||
|
}
|
||
|
break;
|
||
|
case ZYDIS_OPERAND_TYPE_IMMEDIATE:
|
||
|
if (op1->imm.u != op2->imm.value.u)
|
||
|
{
|
||
|
ZyanBool acceptable_mismatch = ZYAN_FALSE;
|
||
|
if ((insn->meta.category == ZYDIS_CATEGORY_DATAXFER) ||
|
||
|
(insn->meta.category == ZYDIS_CATEGORY_LOGICAL))
|
||
|
{
|
||
|
if (op2->size < 64)
|
||
|
{
|
||
|
ZyanU64 mask = (1ULL << op2->size) - 1;
|
||
|
acceptable_mismatch =
|
||
|
(op1->imm.u & mask) == (op2->imm.value.u & mask);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
acceptable_mismatch = op1->imm.u == op2->imm.value.u;
|
||
|
}
|
||
|
}
|
||
|
if (!acceptable_mismatch)
|
||
|
{
|
||
|
fprintf(ZYAN_STDERR, "Mismatch for immediate operand %u\n", i);
|
||
|
abort();
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
fprintf(ZYAN_STDERR, "Invalid operand type for operand %u\n", i);
|
||
|
abort();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ZYAN_NO_SANITIZE("enum")
|
||
|
int ZydisFuzzTarget(ZydisStreamRead read_fn, void *stream_ctx)
|
||
|
{
|
||
|
ZydisEncoderRequest request;
|
||
|
if (read_fn(stream_ctx, (ZyanU8 *)&request, sizeof(request)) != sizeof(request))
|
||
|
{
|
||
|
ZYDIS_MAYBE_FPUTS("Not enough bytes to fuzz\n", ZYAN_STDERR);
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Sanitization greatly improves coverage, without it most inputs will fail at basic checks
|
||
|
// inside `ZydisEncoderCheckRequestSanity`
|
||
|
request.operand_count %= ZYDIS_ENCODER_MAX_OPERANDS + 1;
|
||
|
ZYDIS_SANITIZE_MASK32(request.allowed_encodings, ZydisEncodableEncoding,
|
||
|
ZYDIS_ENCODABLE_ENCODING_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_MASK64(request.prefixes, ZydisInstructionAttributes, ZYDIS_ENCODABLE_PREFIXES);
|
||
|
ZYDIS_SANITIZE_ENUM(request.machine_mode, ZydisMachineMode, ZYDIS_MACHINE_MODE_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_ENUM(request.mnemonic, ZydisMnemonic, ZYDIS_MNEMONIC_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_ENUM(request.branch_type, ZydisBranchType, ZYDIS_BRANCH_TYPE_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_ENUM(request.branch_width, ZydisBranchWidth, ZYDIS_BRANCH_WIDTH_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_ENUM(request.address_size_hint, ZydisAddressSizeHint,
|
||
|
ZYDIS_ADDRESS_SIZE_HINT_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_ENUM(request.operand_size_hint, ZydisOperandSizeHint,
|
||
|
ZYDIS_OPERAND_SIZE_HINT_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_ENUM(request.evex.broadcast, ZydisBroadcastMode, ZYDIS_BROADCAST_MODE_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_ENUM(request.evex.rounding, ZydisRoundingMode, ZYDIS_ROUNDING_MODE_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_ENUM(request.mvex.broadcast, ZydisBroadcastMode, ZYDIS_BROADCAST_MODE_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_ENUM(request.mvex.conversion, ZydisConversionMode,
|
||
|
ZYDIS_CONVERSION_MODE_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_ENUM(request.mvex.rounding, ZydisRoundingMode, ZYDIS_ROUNDING_MODE_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_ENUM(request.mvex.swizzle, ZydisSwizzleMode, ZYDIS_SWIZZLE_MODE_MAX_VALUE);
|
||
|
for (ZyanU8 i = 0; i < request.operand_count; ++i)
|
||
|
{
|
||
|
ZydisEncoderOperand *op = &request.operands[i];
|
||
|
op->type = (ZydisOperandType)(ZYDIS_OPERAND_TYPE_REGISTER +
|
||
|
((ZyanUSize)op->type % ZYDIS_OPERAND_TYPE_MAX_VALUE));
|
||
|
switch (op->type)
|
||
|
{
|
||
|
case ZYDIS_OPERAND_TYPE_REGISTER:
|
||
|
ZYDIS_SANITIZE_ENUM(op->reg.value, ZydisRegister, ZYDIS_REGISTER_MAX_VALUE);
|
||
|
break;
|
||
|
case ZYDIS_OPERAND_TYPE_MEMORY:
|
||
|
ZYDIS_SANITIZE_ENUM(op->mem.base, ZydisRegister, ZYDIS_REGISTER_MAX_VALUE);
|
||
|
ZYDIS_SANITIZE_ENUM(op->mem.index, ZydisRegister, ZYDIS_REGISTER_MAX_VALUE);
|
||
|
break;
|
||
|
case ZYDIS_OPERAND_TYPE_POINTER:
|
||
|
case ZYDIS_OPERAND_TYPE_IMMEDIATE:
|
||
|
break;
|
||
|
default:
|
||
|
ZYAN_UNREACHABLE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ZyanU8 encoded_instruction[ZYDIS_MAX_INSTRUCTION_LENGTH];
|
||
|
ZyanUSize encoded_length = sizeof(encoded_instruction);
|
||
|
ZyanStatus status = ZydisEncoderEncodeInstruction(&request, encoded_instruction,
|
||
|
&encoded_length);
|
||
|
if (!ZYAN_SUCCESS(status))
|
||
|
{
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
ZydisStackWidth stack_width;
|
||
|
switch (request.machine_mode)
|
||
|
{
|
||
|
case ZYDIS_MACHINE_MODE_LONG_64:
|
||
|
stack_width = ZYDIS_STACK_WIDTH_64;
|
||
|
break;
|
||
|
case ZYDIS_MACHINE_MODE_LONG_COMPAT_32:
|
||
|
case ZYDIS_MACHINE_MODE_LEGACY_32:
|
||
|
stack_width = ZYDIS_STACK_WIDTH_32;
|
||
|
break;
|
||
|
case ZYDIS_MACHINE_MODE_LONG_COMPAT_16:
|
||
|
case ZYDIS_MACHINE_MODE_LEGACY_16:
|
||
|
case ZYDIS_MACHINE_MODE_REAL_16:
|
||
|
stack_width = ZYDIS_STACK_WIDTH_16;
|
||
|
break;
|
||
|
default:
|
||
|
ZYAN_UNREACHABLE;
|
||
|
}
|
||
|
|
||
|
ZydisDecoder decoder;
|
||
|
if (!ZYAN_SUCCESS(ZydisDecoderInit(&decoder, request.machine_mode, stack_width)))
|
||
|
{
|
||
|
fputs("Failed to initialize decoder\n", ZYAN_STDERR);
|
||
|
abort();
|
||
|
}
|
||
|
|
||
|
ZydisDecodedInstruction insn1;
|
||
|
ZydisDecodedOperand operands1[ZYDIS_MAX_OPERAND_COUNT];
|
||
|
status = ZydisDecoderDecodeFull(&decoder, encoded_instruction, encoded_length, &insn1,
|
||
|
operands1);
|
||
|
if (!ZYAN_SUCCESS(status))
|
||
|
{
|
||
|
fputs("Failed to decode instruction\n", ZYAN_STDERR);
|
||
|
abort();
|
||
|
}
|
||
|
|
||
|
ZydisCompareRequestToInstruction(&request, &insn1, operands1, encoded_instruction);
|
||
|
ZydisReEncodeInstruction(&decoder, &insn1, operands1, insn1.operand_count,
|
||
|
encoded_instruction);
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* ============================================================================================== */
|