/src/zydis/tools/ZydisFuzzShared.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*************************************************************************************************** |
2 | | |
3 | | Zyan Disassembler Library (Zydis) |
4 | | |
5 | | Original Author : Joel Hoener |
6 | | |
7 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | | * of this software and associated documentation files (the "Software"), to deal |
9 | | * in the Software without restriction, including without limitation the rights |
10 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | | * copies of the Software, and to permit persons to whom the Software is |
12 | | * furnished to do so, subject to the following conditions: |
13 | | * |
14 | | * The above copyright notice and this permission notice shall be included in all |
15 | | * copies or substantial portions of the Software. |
16 | | * |
17 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
20 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
23 | | * SOFTWARE. |
24 | | |
25 | | ***************************************************************************************************/ |
26 | | |
27 | | /** |
28 | | * @file |
29 | | * |
30 | | * This file implements an entry point and common functions used by fuzz target projects. To create |
31 | | * a new fuzz target add this file to your project and implement `ZydisFuzzTarget` in a separate |
32 | | * compilation unit. |
33 | | */ |
34 | | |
35 | | #include "ZydisFuzzShared.h" |
36 | | |
37 | | #ifdef ZYAN_WINDOWS |
38 | | # include <fcntl.h> |
39 | | # include <io.h> |
40 | | #endif |
41 | | |
42 | | #ifdef ZYAN_POSIX |
43 | | # include <unistd.h> |
44 | | #endif |
45 | | |
46 | | /* ============================================================================================== */ |
47 | | /* Stream reading abstraction */ |
48 | | /* ============================================================================================== */ |
49 | | |
50 | | ZyanUSize ZydisStdinRead(void *ctx, ZyanU8* buf, ZyanUSize max_len) |
51 | 0 | { |
52 | 0 | ZYAN_UNUSED(ctx); |
53 | 0 | #ifdef ZYAN_POSIX |
54 | | // `fread` does internal buffering that can result in different code paths to be taken every |
55 | | // time we call it. This is detrimental for fuzzing stability in persistent mode. Use direct |
56 | | // syscall when possible. |
57 | 0 | return read(0, buf, max_len); |
58 | | #else |
59 | | return fread(buf, 1, max_len, ZYAN_STDIN); |
60 | | #endif |
61 | 0 | } |
62 | | |
63 | | #ifdef ZYDIS_LIBFUZZER |
64 | | typedef struct |
65 | | { |
66 | | ZyanU8 *buf; |
67 | | ZyanISize buf_len; |
68 | | ZyanISize read_offs; |
69 | | } ZydisLibFuzzerContext; |
70 | | |
71 | | ZyanUSize ZydisLibFuzzerRead(void* ctx, ZyanU8* buf, ZyanUSize max_len) |
72 | 1.66k | { |
73 | 1.66k | ZydisLibFuzzerContext* c = (ZydisLibFuzzerContext*)ctx; |
74 | 1.66k | ZyanUSize len = ZYAN_MIN((ZyanUSize)(c->buf_len - c->read_offs), max_len); |
75 | | // printf("buf_len: %ld, read_offs: %ld, len: %ld, max_len: %ld, ptr: %p\n", |
76 | | // c->buf_len, c->read_offs, len, max_len, c->buf + c->read_offs); |
77 | 1.66k | if (!len) |
78 | 0 | { |
79 | 0 | return 0; |
80 | 0 | } |
81 | 1.66k | ZYAN_MEMCPY(buf, c->buf + c->read_offs, len); |
82 | 1.66k | c->read_offs += len; |
83 | 1.66k | return len; |
84 | 1.66k | } |
85 | | #endif // ZYDIS_LIBFUZZER |
86 | | |
87 | | /* ============================================================================================== */ |
88 | | /* Shared utility functions */ |
89 | | /* ============================================================================================== */ |
90 | | |
91 | | #if !defined(ZYDIS_FUZZ_AFL_FAST) && !defined(ZYDIS_LIBFUZZER) |
92 | | |
93 | | void ZydisPrintInstruction(const ZydisDecodedInstruction* instruction, |
94 | | const ZydisDecodedOperand* operands, ZyanU8 operand_count, const ZyanU8* instruction_bytes) |
95 | | { |
96 | | switch (instruction->machine_mode) |
97 | | { |
98 | | case ZYDIS_MACHINE_MODE_LONG_64: |
99 | | printf("-64 "); |
100 | | break; |
101 | | case ZYDIS_MACHINE_MODE_LONG_COMPAT_32: |
102 | | case ZYDIS_MACHINE_MODE_LEGACY_32: |
103 | | printf("-32 "); |
104 | | break; |
105 | | case ZYDIS_MACHINE_MODE_LONG_COMPAT_16: |
106 | | case ZYDIS_MACHINE_MODE_LEGACY_16: |
107 | | case ZYDIS_MACHINE_MODE_REAL_16: |
108 | | printf("-16 "); |
109 | | break; |
110 | | default: |
111 | | ZYAN_UNREACHABLE; |
112 | | } |
113 | | printf("-%u ", instruction->stack_width); |
114 | | if (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX) |
115 | | { |
116 | | printf("-knc "); |
117 | | } |
118 | | |
119 | | for (ZyanU8 i = 0; i < instruction->length; ++i) |
120 | | { |
121 | | printf("%02X", instruction_bytes[i]); |
122 | | } |
123 | | |
124 | | ZydisFormatter formatter; |
125 | | if (!ZYAN_SUCCESS(ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL)) || |
126 | | !ZYAN_SUCCESS(ZydisFormatterSetProperty(&formatter, ZYDIS_FORMATTER_PROP_FORCE_SEGMENT, |
127 | | ZYAN_TRUE)) || |
128 | | !ZYAN_SUCCESS(ZydisFormatterSetProperty(&formatter, ZYDIS_FORMATTER_PROP_FORCE_SIZE, |
129 | | ZYAN_TRUE))) |
130 | | { |
131 | | fputs("Failed to initialize instruction formatter\n", ZYAN_STDERR); |
132 | | abort(); |
133 | | } |
134 | | |
135 | | char buffer[256]; |
136 | | ZydisFormatterFormatInstruction(&formatter, instruction, operands, operand_count, buffer, |
137 | | sizeof(buffer), 0, ZYAN_NULL); |
138 | | printf(" %s\n", buffer); |
139 | | } |
140 | | |
141 | | #endif |
142 | | |
143 | | void ZydisValidateImmediateSize(ZyanU64 value) |
144 | 0 | { |
145 | 0 | if ((value != 0) && |
146 | 0 | (value != 8) && |
147 | 0 | (value != 16) && |
148 | 0 | (value != 32) && |
149 | 0 | (value != 64)) |
150 | 0 | { |
151 | 0 | fprintf(stderr, "Value 0x%016" PRIX64 " does not match any of the expected " |
152 | 0 | "values (0, 8, 16, 32, 64).\n", value); |
153 | 0 | abort(); |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | // NOTE: This function doesn't validate flag values, yet. |
158 | | void ZydisValidateEnumRanges(const ZydisDecodedInstruction* insn, |
159 | | const ZydisDecodedOperand* operands, ZyanU8 operand_count) |
160 | 0 | { |
161 | 0 | # define ZYDIS_CHECK_ENUM(value, max) \ |
162 | 0 | if ((ZyanU64)(value) > (ZyanU64)(max)) \ |
163 | 0 | { \ |
164 | 0 | fprintf(stderr, "Value " #value " = 0x%016" PRIX64 " is above expected max " #max \ |
165 | 0 | " = 0x%016" PRIX64 "\n", (ZyanU64)(value), (ZyanU64)(max)); \ |
166 | 0 | abort(); \ |
167 | 0 | } |
168 | 0 | # define ZYDIS_CHECK_MAX ZYDIS_CHECK_ENUM |
169 | |
|
170 | 0 | ZYDIS_CHECK_ENUM(insn->length, ZYDIS_MAX_INSTRUCTION_LENGTH); |
171 | |
|
172 | 0 | ZYDIS_CHECK_ENUM(insn->machine_mode, ZYDIS_MACHINE_MODE_MAX_VALUE); |
173 | 0 | ZYDIS_CHECK_ENUM(insn->mnemonic, ZYDIS_MNEMONIC_MAX_VALUE); |
174 | 0 | ZYDIS_CHECK_ENUM(insn->encoding, ZYDIS_INSTRUCTION_ENCODING_MAX_VALUE); |
175 | 0 | ZYDIS_CHECK_ENUM(insn->opcode_map, ZYDIS_OPCODE_MAP_MAX_VALUE); |
176 | 0 | ZYDIS_CHECK_ENUM(insn->opcode_map, ZYDIS_OPCODE_MAP_MAX_VALUE); |
177 | | |
178 | | // Operands. |
179 | 0 | for (ZyanU32 i = 0; i < operand_count; ++i) |
180 | 0 | { |
181 | 0 | const ZydisDecodedOperand* op = &operands[i]; |
182 | 0 | ZYDIS_CHECK_ENUM(op->type, ZYDIS_OPERAND_TYPE_MAX_VALUE); |
183 | 0 | ZYDIS_CHECK_ENUM(op->visibility, ZYDIS_OPERAND_VISIBILITY_MAX_VALUE); |
184 | 0 | ZYDIS_CHECK_ENUM(op->encoding, ZYDIS_OPERAND_ENCODING_MAX_VALUE); |
185 | 0 | ZYDIS_CHECK_ENUM(op->element_type, ZYDIS_ELEMENT_TYPE_MAX_VALUE); |
186 | |
|
187 | 0 | switch (op->type) |
188 | 0 | { |
189 | 0 | case ZYDIS_OPERAND_TYPE_REGISTER: |
190 | 0 | ZYDIS_CHECK_ENUM(op->reg.value, ZYDIS_REGISTER_MAX_VALUE); |
191 | 0 | break; |
192 | 0 | case ZYDIS_OPERAND_TYPE_MEMORY: |
193 | 0 | ZYDIS_CHECK_ENUM(op->mem.type, ZYDIS_MEMOP_TYPE_MAX_VALUE); |
194 | 0 | ZYDIS_CHECK_ENUM(op->mem.segment, ZYDIS_REGISTER_MAX_VALUE); |
195 | 0 | ZYDIS_CHECK_ENUM(op->mem.base, ZYDIS_REGISTER_MAX_VALUE); |
196 | 0 | ZYDIS_CHECK_ENUM(op->mem.index, ZYDIS_REGISTER_MAX_VALUE); |
197 | 0 | ZydisValidateImmediateSize(op->mem.disp.size); |
198 | 0 | ZYDIS_CHECK_MAX(op->mem.disp.offset + (op->mem.disp.size / 8), insn->length); |
199 | 0 | break; |
200 | 0 | case ZYDIS_OPERAND_TYPE_IMMEDIATE: |
201 | 0 | ZYDIS_CHECK_ENUM(op->imm.is_signed, ZYAN_TRUE); |
202 | 0 | ZYDIS_CHECK_ENUM(op->imm.is_relative, ZYAN_TRUE); |
203 | 0 | ZydisValidateImmediateSize(op->imm.size); |
204 | 0 | ZYDIS_CHECK_MAX(op->imm.offset + (op->imm.size / 8), insn->length); |
205 | 0 | break; |
206 | 0 | default: |
207 | 0 | break; |
208 | 0 | } |
209 | 0 | } |
210 | | |
211 | | // AVX. |
212 | 0 | ZYDIS_CHECK_ENUM(insn->avx.mask.mode, ZYDIS_MASK_MODE_MAX_VALUE); |
213 | 0 | ZYDIS_CHECK_ENUM(insn->avx.mask.reg, ZYDIS_REGISTER_MAX_VALUE); |
214 | 0 | ZYDIS_CHECK_ENUM(insn->avx.broadcast.is_static, ZYAN_TRUE); |
215 | 0 | ZYDIS_CHECK_ENUM(insn->avx.broadcast.mode, ZYDIS_BROADCAST_MODE_MAX_VALUE); |
216 | 0 | ZYDIS_CHECK_ENUM(insn->avx.rounding.mode, ZYDIS_ROUNDING_MODE_MAX_VALUE); |
217 | 0 | ZYDIS_CHECK_ENUM(insn->avx.swizzle.mode, ZYDIS_SWIZZLE_MODE_MAX_VALUE); |
218 | 0 | ZYDIS_CHECK_ENUM(insn->avx.conversion.mode, ZYDIS_CONVERSION_MODE_MAX_VALUE); |
219 | 0 | ZYDIS_CHECK_ENUM(insn->avx.has_sae, ZYAN_TRUE); |
220 | 0 | ZYDIS_CHECK_ENUM(insn->avx.has_eviction_hint, ZYAN_TRUE); |
221 | | |
222 | | // Meta. |
223 | 0 | ZYDIS_CHECK_ENUM(insn->meta.category, ZYDIS_CATEGORY_MAX_VALUE); |
224 | 0 | ZYDIS_CHECK_ENUM(insn->meta.isa_set, ZYDIS_ISA_SET_MAX_VALUE); |
225 | 0 | ZYDIS_CHECK_ENUM(insn->meta.isa_ext, ZYDIS_ISA_SET_MAX_VALUE); |
226 | 0 | ZYDIS_CHECK_ENUM(insn->meta.branch_type, ZYDIS_BRANCH_TYPE_MAX_VALUE); |
227 | 0 | ZYDIS_CHECK_ENUM(insn->meta.exception_class, ZYDIS_EXCEPTION_CLASS_MAX_VALUE); |
228 | | |
229 | | // Raw. |
230 | 0 | for (ZyanU32 i = 0; i < ZYAN_ARRAY_LENGTH(insn->raw.prefixes); ++i) |
231 | 0 | { |
232 | 0 | ZYDIS_CHECK_ENUM(insn->raw.prefixes[i].type, ZYDIS_PREFIX_TYPE_MAX_VALUE); |
233 | 0 | } |
234 | 0 | for (ZyanU32 i = 0; i < ZYAN_ARRAY_LENGTH(insn->raw.imm); ++i) |
235 | 0 | { |
236 | 0 | ZYDIS_CHECK_ENUM(insn->raw.imm[i].is_signed, ZYAN_TRUE); |
237 | 0 | ZYDIS_CHECK_ENUM(insn->raw.imm[i].is_relative, ZYAN_TRUE); |
238 | 0 | } |
239 | |
|
240 | 0 | # undef ZYDIS_CHECK_ENUM |
241 | 0 | # undef ZYDIS_CHECK_MAX |
242 | 0 | } |
243 | | |
244 | | void ZydisValidateInstructionIdentity(const ZydisDecodedInstruction* insn1, |
245 | | const ZydisDecodedOperand* operands1, const ZydisDecodedInstruction* insn2, |
246 | | const ZydisDecodedOperand* operands2) |
247 | 0 | { |
248 | | // TODO: Probably a good idea to input validate operand_counts to this function |
249 | | // TODO: I don't like accessing buffers without having their actual sizes available... |
250 | | |
251 | | // Special case, `xchg rAX, rAX` is an alias for `NOP` except `xchg eax, eax` in 64-bit mode |
252 | 0 | if ((insn1->mnemonic == ZYDIS_MNEMONIC_XCHG) && |
253 | 0 | (insn1->operand_count == 2) && |
254 | 0 | (operands1[0].type == ZYDIS_OPERAND_TYPE_REGISTER) && |
255 | 0 | (operands1[1].type == ZYDIS_OPERAND_TYPE_REGISTER) && |
256 | 0 | (operands1[0].reg.value == operands1[1].reg.value) && |
257 | 0 | (insn2->mnemonic == ZYDIS_MNEMONIC_NOP)) |
258 | 0 | { |
259 | 0 | switch (operands1[0].reg.value) |
260 | 0 | { |
261 | 0 | case ZYDIS_REGISTER_AX: |
262 | 0 | case ZYDIS_REGISTER_RAX: |
263 | 0 | return; |
264 | 0 | case ZYDIS_REGISTER_EAX: |
265 | 0 | if (insn1->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) |
266 | 0 | return; |
267 | 0 | break; |
268 | 0 | default: |
269 | 0 | break; |
270 | 0 | } |
271 | 0 | } |
272 | | |
273 | 0 | ZydisSwizzleMode swizzle1 = insn1->avx.swizzle.mode == ZYDIS_SWIZZLE_MODE_DCBA ? |
274 | 0 | ZYDIS_SWIZZLE_MODE_NONE : insn1->avx.swizzle.mode; |
275 | 0 | ZydisSwizzleMode swizzle2 = insn2->avx.swizzle.mode == ZYDIS_SWIZZLE_MODE_DCBA ? |
276 | 0 | ZYDIS_SWIZZLE_MODE_NONE : insn2->avx.swizzle.mode; |
277 | 0 | if ((insn1->machine_mode != insn2->machine_mode) || |
278 | 0 | (insn1->mnemonic != insn2->mnemonic) || |
279 | 0 | (insn1->stack_width != insn2->stack_width) || |
280 | 0 | (insn1->operand_count != insn2->operand_count) || |
281 | | //(insn1->avx.mask.mode != insn2->avx.mask.mode) || // TODO: APX breaks mask mode |
282 | 0 | (insn1->avx.broadcast.is_static != insn2->avx.broadcast.is_static) || |
283 | 0 | (insn1->avx.broadcast.mode != insn2->avx.broadcast.mode) || |
284 | 0 | (insn1->avx.conversion.mode != insn2->avx.conversion.mode) || |
285 | 0 | (insn1->avx.rounding.mode != insn2->avx.rounding.mode) || |
286 | 0 | (insn1->avx.has_sae != insn2->avx.has_sae) || |
287 | 0 | (insn1->avx.has_eviction_hint != insn2->avx.has_eviction_hint) || |
288 | 0 | (swizzle1 != swizzle2)) |
289 | 0 | { |
290 | 0 | fputs("Basic instruction attributes mismatch\n", ZYAN_STDERR); |
291 | 0 | abort(); |
292 | 0 | } |
293 | | |
294 | 0 | for (ZyanU8 i = 0; i < insn1->operand_count; ++i) |
295 | 0 | { |
296 | 0 | const ZydisDecodedOperand *op1 = &operands1[i]; |
297 | 0 | const ZydisDecodedOperand *op2 = &operands2[i]; |
298 | 0 | if ((op1->type != op2->type) || |
299 | 0 | (op1->size != op2->size && op1->type != ZYDIS_OPERAND_TYPE_IMMEDIATE)) |
300 | 0 | { |
301 | 0 | fprintf(ZYAN_STDERR, "Mismatch for operand %u\n", i); |
302 | 0 | abort(); |
303 | 0 | } |
304 | 0 | switch (op1->type) |
305 | 0 | { |
306 | 0 | case ZYDIS_OPERAND_TYPE_REGISTER: |
307 | 0 | if (op1->reg.value != op2->reg.value) |
308 | 0 | { |
309 | 0 | fprintf(ZYAN_STDERR, "Mismatch for register operand %u\n", i); |
310 | 0 | abort(); |
311 | 0 | } |
312 | 0 | break; |
313 | 0 | case ZYDIS_OPERAND_TYPE_MEMORY: |
314 | 0 | { |
315 | | // Usually this check is done after verifying instruction identity but in this case |
316 | | // we have to fail early |
317 | 0 | if (insn1->length < insn2->length) |
318 | 0 | { |
319 | 0 | fputs("Suboptimal output size detected\n", ZYAN_STDERR); |
320 | 0 | abort(); |
321 | 0 | } |
322 | 0 | ZyanU64 addr1, addr2; |
323 | 0 | ZyanStatus status1 = ZydisCalcAbsoluteAddress(insn1, op1, 0, &addr1); |
324 | 0 | ZyanStatus status2 = ZydisCalcAbsoluteAddress(insn2, op2, |
325 | 0 | insn1->length - insn2->length, &addr2); |
326 | 0 | ZyanBool addresses_match = ZYAN_FALSE; |
327 | 0 | if (ZYAN_SUCCESS(status1) && ZYAN_SUCCESS(status2)) |
328 | 0 | { |
329 | 0 | if (addr1 != addr2) |
330 | 0 | { |
331 | 0 | fprintf(ZYAN_STDERR, "Mismatch for memory operand %u (absolute address)\n", i); |
332 | 0 | abort(); |
333 | 0 | } |
334 | 0 | addresses_match = ZYAN_TRUE; |
335 | 0 | } |
336 | 0 | if ((op1->mem.type != op2->mem.type) || |
337 | 0 | (op1->mem.segment != op2->mem.segment) || |
338 | 0 | (op1->mem.base != op2->mem.base) || |
339 | 0 | (op1->mem.index != op2->mem.index) || |
340 | 0 | ((op1->mem.scale != op2->mem.scale) && (op1->mem.type != ZYDIS_MEMOP_TYPE_MIB)) || |
341 | 0 | ((op1->mem.disp.value != op2->mem.disp.value) && !addresses_match)) |
342 | 0 | { |
343 | 0 | fprintf(ZYAN_STDERR, "Mismatch for memory operand %u\n", i); |
344 | 0 | abort(); |
345 | 0 | } |
346 | 0 | break; |
347 | 0 | } |
348 | 0 | case ZYDIS_OPERAND_TYPE_POINTER: |
349 | 0 | if ((op1->ptr.segment != op2->ptr.segment) || |
350 | 0 | (op1->ptr.offset != op2->ptr.offset)) |
351 | 0 | { |
352 | 0 | fprintf(ZYAN_STDERR, "Mismatch for pointer operand %u\n", i); |
353 | 0 | abort(); |
354 | 0 | } |
355 | 0 | break; |
356 | 0 | case ZYDIS_OPERAND_TYPE_IMMEDIATE: |
357 | 0 | if ((op1->imm.is_relative != op2->imm.is_relative) || |
358 | 0 | (op1->imm.is_signed != op2->imm.is_signed) || |
359 | 0 | (op1->imm.value.u != op2->imm.value.u)) |
360 | 0 | { |
361 | 0 | ZyanBool acceptable_mismatch = ZYAN_FALSE; |
362 | 0 | if ((insn1->meta.category == ZYDIS_CATEGORY_DATAXFER) || |
363 | 0 | (insn1->meta.category == ZYDIS_CATEGORY_LOGICAL)) |
364 | 0 | { |
365 | 0 | const ZyanU16 size = ZYAN_MAX(op1->size, op2->size); |
366 | 0 | if (size < 64) |
367 | 0 | { |
368 | 0 | const ZyanU64 mask = (1ULL << size) - 1; |
369 | 0 | acceptable_mismatch = |
370 | 0 | (op1->imm.value.u & mask) == (op2->imm.value.u & mask); |
371 | 0 | } |
372 | 0 | else |
373 | 0 | { |
374 | 0 | acceptable_mismatch = op1->imm.value.u == op2->imm.value.u; |
375 | 0 | } |
376 | 0 | } |
377 | 0 | if (!acceptable_mismatch) |
378 | 0 | { |
379 | 0 | fprintf(ZYAN_STDERR, "Mismatch for immediate operand %u\n", i); |
380 | 0 | abort(); |
381 | 0 | } |
382 | 0 | } |
383 | 0 | break; |
384 | 0 | default: |
385 | 0 | fprintf(ZYAN_STDERR, "Invalid operand type for operand %u\n", i); |
386 | 0 | abort(); |
387 | 0 | } |
388 | 0 | } |
389 | 0 | } |
390 | | |
391 | | #if !defined(ZYDIS_DISABLE_ENCODER) |
392 | | |
393 | | static void ZydisReEncodeInstructionAbsolute(ZydisEncoderRequest* req, |
394 | | const ZydisDecodedInstruction* insn2, const ZydisDecodedOperand* insn2_operands, |
395 | | const ZyanU8* insn2_bytes) |
396 | 0 | { |
397 | 0 | ZyanU64 runtime_address; |
398 | 0 | switch (insn2->address_width) |
399 | 0 | { |
400 | 0 | case 16: |
401 | 0 | runtime_address = (ZyanU64)(ZyanU16)ZYAN_INT16_MIN; |
402 | 0 | break; |
403 | 0 | case 32: |
404 | 0 | runtime_address = (ZyanU64)(ZyanU32)ZYAN_INT32_MIN; |
405 | 0 | break; |
406 | 0 | case 64: |
407 | 0 | runtime_address = (ZyanU64)ZYAN_INT64_MIN; |
408 | 0 | break; |
409 | 0 | default: |
410 | 0 | ZYAN_UNREACHABLE; |
411 | 0 | } |
412 | 0 | if ((insn2->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) && (insn2->operand_width == 16)) |
413 | 0 | { |
414 | 0 | runtime_address = (ZyanU64)(ZyanU16)ZYAN_INT16_MIN; |
415 | 0 | } |
416 | 0 | runtime_address -= insn2->length; |
417 | |
|
418 | 0 | ZyanBool has_relative = ZYAN_FALSE; |
419 | 0 | for (ZyanU8 i = 0; i < req->operand_count; ++i) |
420 | 0 | { |
421 | 0 | const ZydisDecodedOperand *decoded_op = &insn2_operands[i]; |
422 | 0 | ZydisEncoderOperand *op = &req->operands[i]; |
423 | 0 | ZyanU64 *dst_address = ZYAN_NULL; |
424 | 0 | switch (op->type) |
425 | 0 | { |
426 | 0 | case ZYDIS_OPERAND_TYPE_IMMEDIATE: |
427 | 0 | if (decoded_op->imm.is_relative) |
428 | 0 | { |
429 | 0 | dst_address = &op->imm.u; |
430 | 0 | } |
431 | 0 | break; |
432 | 0 | case ZYDIS_OPERAND_TYPE_MEMORY: |
433 | 0 | if ((decoded_op->mem.base == ZYDIS_REGISTER_EIP) || |
434 | 0 | (decoded_op->mem.base == ZYDIS_REGISTER_RIP)) |
435 | 0 | { |
436 | 0 | dst_address = (ZyanU64 *)&op->mem.displacement; |
437 | 0 | } |
438 | 0 | break; |
439 | 0 | default: |
440 | 0 | break; |
441 | 0 | } |
442 | 0 | if (!dst_address) |
443 | 0 | { |
444 | 0 | continue; |
445 | 0 | } |
446 | 0 | has_relative = ZYAN_TRUE; |
447 | 0 | if (!ZYAN_SUCCESS(ZydisCalcAbsoluteAddress(insn2, decoded_op, runtime_address, |
448 | 0 | dst_address))) |
449 | 0 | { |
450 | 0 | fputs("ZydisCalcAbsoluteAddress has failed\n", ZYAN_STDERR); |
451 | 0 | abort(); |
452 | 0 | } |
453 | 0 | } |
454 | 0 | if (!has_relative) |
455 | 0 | { |
456 | 0 | return; |
457 | 0 | } |
458 | | |
459 | 0 | ZyanU8 insn1_bytes[ZYDIS_MAX_INSTRUCTION_LENGTH]; |
460 | 0 | ZyanUSize insn1_length = sizeof(insn1_bytes); |
461 | 0 | ZyanStatus status = ZydisEncoderEncodeInstructionAbsolute(req, insn1_bytes, &insn1_length, |
462 | 0 | runtime_address); |
463 | 0 | if (!ZYAN_SUCCESS(status)) |
464 | 0 | { |
465 | 0 | fputs("Failed to re-encode instruction (absolute)\n", ZYAN_STDERR); |
466 | 0 | abort(); |
467 | 0 | } |
468 | 0 | if (insn1_length != insn2->length || ZYAN_MEMCMP(insn1_bytes, insn2_bytes, insn2->length)) |
469 | 0 | { |
470 | 0 | fputs("Instruction mismatch (absolute)\n", ZYAN_STDERR); |
471 | 0 | abort(); |
472 | 0 | } |
473 | 0 | } |
474 | | |
475 | | void ZydisReEncodeInstruction(const ZydisDecoder *decoder, const ZydisDecodedInstruction *insn1, |
476 | | const ZydisDecodedOperand* operands1, ZyanU8 operand_count, const ZyanU8 *insn1_bytes) |
477 | 0 | { |
478 | 0 | ZydisPrintInstruction(insn1, operands1, operand_count, insn1_bytes); |
479 | 0 | ZydisValidateEnumRanges(insn1, operands1, operand_count); |
480 | |
|
481 | 0 | ZYAN_ASSERT(operand_count >= insn1->operand_count_visible); |
482 | | |
483 | 0 | ZydisEncoderRequest request; |
484 | 0 | ZyanStatus status = ZydisEncoderDecodedInstructionToEncoderRequest(insn1, operands1, |
485 | 0 | insn1->operand_count_visible, &request); |
486 | 0 | if (!ZYAN_SUCCESS(status)) |
487 | 0 | { |
488 | 0 | fputs("ZydisEncoderDecodedInstructionToEncoderRequest failed\n", ZYAN_STDERR); |
489 | 0 | abort(); |
490 | 0 | } |
491 | | |
492 | 0 | ZyanU8 encoded_instruction[ZYDIS_MAX_INSTRUCTION_LENGTH]; |
493 | 0 | ZyanUSize encoded_length = sizeof(encoded_instruction); |
494 | 0 | status = ZydisEncoderEncodeInstruction(&request, encoded_instruction, &encoded_length); |
495 | 0 | if (!ZYAN_SUCCESS(status)) |
496 | 0 | { |
497 | 0 | fputs("Failed to re-encode instruction\n", ZYAN_STDERR); |
498 | 0 | abort(); |
499 | 0 | } |
500 | | |
501 | 0 | ZydisDecodedInstruction insn2; |
502 | 0 | ZydisDecodedOperand operands2[ZYDIS_MAX_OPERAND_COUNT]; |
503 | 0 | status = ZydisDecoderDecodeFull(decoder, encoded_instruction, encoded_length, &insn2, |
504 | 0 | operands2); |
505 | 0 | if (!ZYAN_SUCCESS(status)) |
506 | 0 | { |
507 | 0 | fputs("Failed to decode re-encoded instruction\n", ZYAN_STDERR); |
508 | 0 | abort(); |
509 | 0 | } |
510 | | |
511 | 0 | ZydisPrintInstruction(&insn2, operands2, insn2.operand_count_visible, encoded_instruction); |
512 | 0 | ZydisValidateEnumRanges(&insn2, operands2, insn2.operand_count_visible); |
513 | 0 | ZydisValidateInstructionIdentity(insn1, operands1, &insn2, operands2); |
514 | |
|
515 | 0 | if (insn2.length > insn1->length) |
516 | 0 | { |
517 | 0 | fputs("Suboptimal output size detected\n", ZYAN_STDERR); |
518 | 0 | abort(); |
519 | 0 | } |
520 | | |
521 | 0 | ZydisReEncodeInstructionAbsolute(&request, &insn2, operands2, encoded_instruction); |
522 | 0 | } |
523 | | |
524 | | #endif |
525 | | |
526 | | /* ============================================================================================== */ |
527 | | /* Entry point */ |
528 | | /* ============================================================================================== */ |
529 | | |
530 | | int ZydisFuzzerInit(void) |
531 | 2 | { |
532 | 2 | if (ZydisGetVersion() != ZYDIS_VERSION) |
533 | 0 | { |
534 | 0 | fputs("Invalid Zydis version\n", ZYAN_STDERR); |
535 | 0 | return EXIT_FAILURE; |
536 | 0 | } |
537 | | |
538 | | #ifdef ZYAN_WINDOWS |
539 | | // The `stdin` pipe uses text-mode on Windows platforms by default. We need it to be opened in |
540 | | // binary mode |
541 | | (void)_setmode(_fileno(ZYAN_STDIN), _O_BINARY); |
542 | | #endif |
543 | | |
544 | 2 | return EXIT_SUCCESS; |
545 | 2 | } |
546 | | |
547 | | #ifdef ZYDIS_LIBFUZZER |
548 | | |
549 | | #ifdef __cplusplus |
550 | | extern "C" { |
551 | | #endif |
552 | | |
553 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
554 | 2 | { |
555 | 2 | ZYAN_UNUSED(argc); |
556 | 2 | ZYAN_UNUSED(argv); |
557 | | |
558 | 2 | return ZydisFuzzerInit(); |
559 | 2 | } |
560 | | |
561 | | int LLVMFuzzerTestOneInput(ZyanU8 *buf, ZyanUSize len) |
562 | 1.66k | { |
563 | 1.66k | ZydisLibFuzzerContext ctx; |
564 | 1.66k | ctx.buf = buf; |
565 | 1.66k | ctx.buf_len = len; |
566 | 1.66k | ctx.read_offs = 0; |
567 | | |
568 | 1.66k | ZydisFuzzTarget(&ZydisLibFuzzerRead, &ctx); |
569 | 1.66k | return 0; |
570 | 1.66k | } |
571 | | |
572 | | #ifdef __cplusplus |
573 | | } // extern "C" |
574 | | #endif |
575 | | |
576 | | #else // !ZYDIS_LIBFUZZER |
577 | | |
578 | | int main(void) |
579 | | { |
580 | | if (ZydisFuzzerInit() != EXIT_SUCCESS) |
581 | | { |
582 | | return EXIT_FAILURE; |
583 | | } |
584 | | |
585 | | #ifdef ZYDIS_FUZZ_AFL_FAST |
586 | | while (__AFL_LOOP(1000)) |
587 | | { |
588 | | ZydisFuzzTarget(&ZydisStdinRead, ZYAN_NULL); |
589 | | } |
590 | | return EXIT_SUCCESS; |
591 | | #else |
592 | | return ZydisFuzzTarget(&ZydisStdinRead, ZYAN_NULL); |
593 | | #endif |
594 | | } |
595 | | |
596 | | #endif // ZYDIS_LIBFUZZER |
597 | | |
598 | | /* ============================================================================================== */ |