/src/zydis/tools/ZydisFuzzShared.c
Line | Count | Source |
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 | 6.54k | { |
73 | 6.54k | ZydisLibFuzzerContext* c = (ZydisLibFuzzerContext*)ctx; |
74 | 6.54k | 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 | 6.54k | if (!len) |
78 | 4 | { |
79 | 4 | return 0; |
80 | 4 | } |
81 | 6.54k | ZYAN_MEMCPY(buf, c->buf + c->read_offs, len); |
82 | 6.54k | c->read_offs += len; |
83 | 6.54k | return len; |
84 | 6.54k | } |
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 | 5.01k | { |
145 | 5.01k | if ((value != 0) && |
146 | 3.98k | (value != 8) && |
147 | 3.19k | (value != 16) && |
148 | 2.58k | (value != 32) && |
149 | 572 | (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 | 5.01k | } |
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 | 5.67k | { |
161 | 5.67k | # define ZYDIS_CHECK_ENUM(value, max) \ |
162 | 295k | if ((ZyanU64)(value) > (ZyanU64)(max)) \ |
163 | 295k | { \ |
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 | 5.67k | # define ZYDIS_CHECK_MAX ZYDIS_CHECK_ENUM |
169 | | |
170 | 5.67k | ZYDIS_CHECK_ENUM(insn->length, ZYDIS_MAX_INSTRUCTION_LENGTH); |
171 | | |
172 | 5.67k | ZYDIS_CHECK_ENUM(insn->machine_mode, ZYDIS_MACHINE_MODE_MAX_VALUE); |
173 | 5.67k | ZYDIS_CHECK_ENUM(insn->mnemonic, ZYDIS_MNEMONIC_MAX_VALUE); |
174 | 5.67k | ZYDIS_CHECK_ENUM(insn->encoding, ZYDIS_INSTRUCTION_ENCODING_MAX_VALUE); |
175 | 5.67k | ZYDIS_CHECK_ENUM(insn->opcode_map, ZYDIS_OPCODE_MAP_MAX_VALUE); |
176 | 5.67k | ZYDIS_CHECK_ENUM(insn->opcode_map, ZYDIS_OPCODE_MAP_MAX_VALUE); |
177 | | |
178 | | // Operands. |
179 | 17.1k | for (ZyanU32 i = 0; i < operand_count; ++i) |
180 | 11.4k | { |
181 | 11.4k | const ZydisDecodedOperand* op = &operands[i]; |
182 | 11.4k | ZYDIS_CHECK_ENUM(op->type, ZYDIS_OPERAND_TYPE_MAX_VALUE); |
183 | 11.4k | ZYDIS_CHECK_ENUM(op->visibility, ZYDIS_OPERAND_VISIBILITY_MAX_VALUE); |
184 | 11.4k | ZYDIS_CHECK_ENUM(op->encoding, ZYDIS_OPERAND_ENCODING_MAX_VALUE); |
185 | 11.4k | ZYDIS_CHECK_ENUM(op->element_type, ZYDIS_ELEMENT_TYPE_MAX_VALUE); |
186 | | |
187 | 11.4k | switch (op->type) |
188 | 11.4k | { |
189 | 6.34k | case ZYDIS_OPERAND_TYPE_REGISTER: |
190 | 6.34k | ZYDIS_CHECK_ENUM(op->reg.value, ZYDIS_REGISTER_MAX_VALUE); |
191 | 6.34k | break; |
192 | 3.31k | case ZYDIS_OPERAND_TYPE_MEMORY: |
193 | 3.31k | ZYDIS_CHECK_ENUM(op->mem.type, ZYDIS_MEMOP_TYPE_MAX_VALUE); |
194 | 3.31k | ZYDIS_CHECK_ENUM(op->mem.segment, ZYDIS_REGISTER_MAX_VALUE); |
195 | 3.31k | ZYDIS_CHECK_ENUM(op->mem.base, ZYDIS_REGISTER_MAX_VALUE); |
196 | 3.31k | ZYDIS_CHECK_ENUM(op->mem.index, ZYDIS_REGISTER_MAX_VALUE); |
197 | 3.31k | ZydisValidateImmediateSize(op->mem.disp.size); |
198 | 3.31k | ZYDIS_CHECK_MAX(op->mem.disp.offset + (op->mem.disp.size / 8), insn->length); |
199 | 3.31k | break; |
200 | 1.70k | case ZYDIS_OPERAND_TYPE_IMMEDIATE: |
201 | 1.70k | ZYDIS_CHECK_ENUM(op->imm.is_signed, ZYAN_TRUE); |
202 | 1.70k | ZYDIS_CHECK_ENUM(op->imm.is_relative, ZYAN_TRUE); |
203 | 1.70k | ZydisValidateImmediateSize(op->imm.size); |
204 | 1.70k | ZYDIS_CHECK_MAX(op->imm.offset + (op->imm.size / 8), insn->length); |
205 | 1.70k | break; |
206 | 108 | default: |
207 | 108 | break; |
208 | 11.4k | } |
209 | 11.4k | } |
210 | | |
211 | | // AVX. |
212 | 5.67k | ZYDIS_CHECK_ENUM(insn->avx.mask.mode, ZYDIS_MASK_MODE_MAX_VALUE); |
213 | 5.67k | ZYDIS_CHECK_ENUM(insn->avx.mask.reg, ZYDIS_REGISTER_MAX_VALUE); |
214 | 5.67k | ZYDIS_CHECK_ENUM(insn->avx.broadcast.is_static, ZYAN_TRUE); |
215 | 5.67k | ZYDIS_CHECK_ENUM(insn->avx.broadcast.mode, ZYDIS_BROADCAST_MODE_MAX_VALUE); |
216 | 5.67k | ZYDIS_CHECK_ENUM(insn->avx.rounding.mode, ZYDIS_ROUNDING_MODE_MAX_VALUE); |
217 | 5.67k | ZYDIS_CHECK_ENUM(insn->avx.swizzle.mode, ZYDIS_SWIZZLE_MODE_MAX_VALUE); |
218 | 5.67k | ZYDIS_CHECK_ENUM(insn->avx.conversion.mode, ZYDIS_CONVERSION_MODE_MAX_VALUE); |
219 | 5.67k | ZYDIS_CHECK_ENUM(insn->avx.has_sae, ZYAN_TRUE); |
220 | 5.67k | ZYDIS_CHECK_ENUM(insn->avx.has_eviction_hint, ZYAN_TRUE); |
221 | | |
222 | | // Meta. |
223 | 5.67k | ZYDIS_CHECK_ENUM(insn->meta.category, ZYDIS_CATEGORY_MAX_VALUE); |
224 | 5.67k | ZYDIS_CHECK_ENUM(insn->meta.isa_set, ZYDIS_ISA_SET_MAX_VALUE); |
225 | 5.67k | ZYDIS_CHECK_ENUM(insn->meta.isa_ext, ZYDIS_ISA_SET_MAX_VALUE); |
226 | 5.67k | ZYDIS_CHECK_ENUM(insn->meta.branch_type, ZYDIS_BRANCH_TYPE_MAX_VALUE); |
227 | 5.67k | ZYDIS_CHECK_ENUM(insn->meta.exception_class, ZYDIS_EXCEPTION_CLASS_MAX_VALUE); |
228 | | |
229 | | // Raw. |
230 | 90.8k | for (ZyanU32 i = 0; i < ZYAN_ARRAY_LENGTH(insn->raw.prefixes); ++i) |
231 | 85.1k | { |
232 | 85.1k | ZYDIS_CHECK_ENUM(insn->raw.prefixes[i].type, ZYDIS_PREFIX_TYPE_MAX_VALUE); |
233 | 85.1k | } |
234 | 17.0k | for (ZyanU32 i = 0; i < ZYAN_ARRAY_LENGTH(insn->raw.imm); ++i) |
235 | 11.3k | { |
236 | 11.3k | ZYDIS_CHECK_ENUM(insn->raw.imm[i].is_signed, ZYAN_TRUE); |
237 | 11.3k | ZYDIS_CHECK_ENUM(insn->raw.imm[i].is_relative, ZYAN_TRUE); |
238 | 11.3k | } |
239 | | |
240 | 5.67k | # undef ZYDIS_CHECK_ENUM |
241 | 5.67k | # undef ZYDIS_CHECK_MAX |
242 | 5.67k | } |
243 | | |
244 | | void ZydisValidateInstructionIdentity(const ZydisDecodedInstruction* insn1, |
245 | | const ZydisDecodedOperand* operands1, const ZydisDecodedInstruction* insn2, |
246 | | const ZydisDecodedOperand* operands2) |
247 | 2.83k | { |
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 | 2.83k | if ((insn1->mnemonic == ZYDIS_MNEMONIC_XCHG) && |
253 | 36 | (insn1->operand_count == 2) && |
254 | 36 | (operands1[0].type == ZYDIS_OPERAND_TYPE_REGISTER) && |
255 | 25 | (operands1[1].type == ZYDIS_OPERAND_TYPE_REGISTER) && |
256 | 25 | (operands1[0].reg.value == operands1[1].reg.value) && |
257 | 9 | (insn2->mnemonic == ZYDIS_MNEMONIC_NOP)) |
258 | 4 | { |
259 | 4 | switch (operands1[0].reg.value) |
260 | 4 | { |
261 | 1 | case ZYDIS_REGISTER_AX: |
262 | 2 | case ZYDIS_REGISTER_RAX: |
263 | 2 | return; |
264 | 2 | case ZYDIS_REGISTER_EAX: |
265 | 2 | if (insn1->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) |
266 | 2 | return; |
267 | 0 | break; |
268 | 0 | default: |
269 | 0 | break; |
270 | 4 | } |
271 | 4 | } |
272 | | |
273 | 2.83k | ZydisSwizzleMode swizzle1 = insn1->avx.swizzle.mode == ZYDIS_SWIZZLE_MODE_DCBA ? |
274 | 2.83k | ZYDIS_SWIZZLE_MODE_NONE : insn1->avx.swizzle.mode; |
275 | 2.83k | ZydisSwizzleMode swizzle2 = insn2->avx.swizzle.mode == ZYDIS_SWIZZLE_MODE_DCBA ? |
276 | 2.83k | ZYDIS_SWIZZLE_MODE_NONE : insn2->avx.swizzle.mode; |
277 | 2.83k | if ((insn1->machine_mode != insn2->machine_mode) || |
278 | 2.83k | (insn1->mnemonic != insn2->mnemonic) || |
279 | 2.83k | (insn1->stack_width != insn2->stack_width) || |
280 | 2.83k | (insn1->operand_count != insn2->operand_count) || |
281 | 2.83k | (insn1->avx.mask.mode != insn2->avx.mask.mode) || |
282 | 2.83k | (insn1->avx.mask.reg != insn2->avx.mask.reg) || |
283 | 2.83k | (insn1->avx.broadcast.is_static != insn2->avx.broadcast.is_static) || |
284 | 2.83k | (insn1->avx.broadcast.mode != insn2->avx.broadcast.mode) || |
285 | 2.83k | (insn1->avx.conversion.mode != insn2->avx.conversion.mode) || |
286 | 2.83k | (insn1->avx.rounding.mode != insn2->avx.rounding.mode) || |
287 | 2.83k | (insn1->avx.has_sae != insn2->avx.has_sae) || |
288 | 2.83k | (insn1->avx.has_eviction_hint != insn2->avx.has_eviction_hint) || |
289 | 2.83k | (swizzle1 != swizzle2)) |
290 | 0 | { |
291 | 0 | fputs("Basic instruction attributes mismatch\n", ZYAN_STDERR); |
292 | 0 | abort(); |
293 | 0 | } |
294 | | |
295 | 10.5k | for (ZyanU8 i = 0; i < insn1->operand_count; ++i) |
296 | 7.69k | { |
297 | 7.69k | const ZydisDecodedOperand *op1 = &operands1[i]; |
298 | 7.69k | const ZydisDecodedOperand *op2 = &operands2[i]; |
299 | 7.69k | if ((op1->type != op2->type) || |
300 | 7.69k | (op1->size != op2->size && op1->type != ZYDIS_OPERAND_TYPE_IMMEDIATE)) |
301 | 0 | { |
302 | 0 | fprintf(ZYAN_STDERR, "Mismatch for operand %u\n", i); |
303 | 0 | abort(); |
304 | 0 | } |
305 | 7.69k | switch (op1->type) |
306 | 7.69k | { |
307 | 4.83k | case ZYDIS_OPERAND_TYPE_REGISTER: |
308 | 4.83k | if (op1->reg.value != op2->reg.value) |
309 | 0 | { |
310 | 0 | fprintf(ZYAN_STDERR, "Mismatch for register operand %u\n", i); |
311 | 0 | abort(); |
312 | 0 | } |
313 | 4.83k | break; |
314 | 4.83k | case ZYDIS_OPERAND_TYPE_MEMORY: |
315 | 1.94k | { |
316 | | // Usually this check is done after verifying instruction identity but in this case |
317 | | // we have to fail early |
318 | 1.94k | if (insn1->length < insn2->length) |
319 | 0 | { |
320 | 0 | fputs("Suboptimal output size detected\n", ZYAN_STDERR); |
321 | 0 | abort(); |
322 | 0 | } |
323 | 1.94k | ZyanU64 addr1, addr2; |
324 | 1.94k | ZyanStatus status1 = ZydisCalcAbsoluteAddress(insn1, op1, 0, &addr1); |
325 | 1.94k | ZyanStatus status2 = ZydisCalcAbsoluteAddress(insn2, op2, |
326 | 1.94k | insn1->length - insn2->length, &addr2); |
327 | 1.94k | ZyanBool addresses_match = ZYAN_FALSE; |
328 | 1.94k | if (ZYAN_SUCCESS(status1) && ZYAN_SUCCESS(status2)) |
329 | 944 | { |
330 | 944 | if (addr1 != addr2) |
331 | 0 | { |
332 | 0 | fprintf(ZYAN_STDERR, "Mismatch for memory operand %u (absolute address)\n", i); |
333 | 0 | abort(); |
334 | 0 | } |
335 | 944 | addresses_match = ZYAN_TRUE; |
336 | 944 | } |
337 | 1.94k | if ((op1->mem.type != op2->mem.type) || |
338 | 1.94k | (op1->mem.segment != op2->mem.segment) || |
339 | 1.94k | (op1->mem.base != op2->mem.base) || |
340 | 1.94k | (op1->mem.index != op2->mem.index) || |
341 | 1.94k | ((op1->mem.scale != op2->mem.scale) && (op1->mem.type != ZYDIS_MEMOP_TYPE_MIB)) || |
342 | 1.94k | ((op1->mem.disp.value != op2->mem.disp.value) && !addresses_match)) |
343 | 0 | { |
344 | 0 | fprintf(ZYAN_STDERR, "Mismatch for memory operand %u\n", i); |
345 | 0 | abort(); |
346 | 0 | } |
347 | 1.94k | break; |
348 | 1.94k | } |
349 | 1.94k | case ZYDIS_OPERAND_TYPE_POINTER: |
350 | 54 | if ((op1->ptr.segment != op2->ptr.segment) || |
351 | 54 | (op1->ptr.offset != op2->ptr.offset)) |
352 | 0 | { |
353 | 0 | fprintf(ZYAN_STDERR, "Mismatch for pointer operand %u\n", i); |
354 | 0 | abort(); |
355 | 0 | } |
356 | 54 | break; |
357 | 851 | case ZYDIS_OPERAND_TYPE_IMMEDIATE: |
358 | 851 | if ((op1->imm.is_relative != op2->imm.is_relative) || |
359 | 851 | (op1->imm.is_signed != op2->imm.is_signed) || |
360 | 851 | (op1->imm.value.u != op2->imm.value.u)) |
361 | 0 | { |
362 | 0 | ZyanBool acceptable_mismatch = ZYAN_FALSE; |
363 | 0 | if ((insn1->meta.category == ZYDIS_CATEGORY_DATAXFER) || |
364 | 0 | (insn1->meta.category == ZYDIS_CATEGORY_LOGICAL)) |
365 | 0 | { |
366 | 0 | const ZyanU16 size = ZYAN_MAX(op1->size, op2->size); |
367 | 0 | if (size < 64) |
368 | 0 | { |
369 | 0 | const ZyanU64 mask = (1ULL << size) - 1; |
370 | 0 | acceptable_mismatch = |
371 | 0 | (op1->imm.value.u & mask) == (op2->imm.value.u & mask); |
372 | 0 | } |
373 | 0 | else |
374 | 0 | { |
375 | 0 | acceptable_mismatch = op1->imm.value.u == op2->imm.value.u; |
376 | 0 | } |
377 | 0 | } |
378 | 0 | if (!acceptable_mismatch) |
379 | 0 | { |
380 | 0 | fprintf(ZYAN_STDERR, "Mismatch for immediate operand %u\n", i); |
381 | 0 | abort(); |
382 | 0 | } |
383 | 0 | } |
384 | 851 | break; |
385 | 851 | default: |
386 | 0 | fprintf(ZYAN_STDERR, "Invalid operand type for operand %u\n", i); |
387 | 0 | abort(); |
388 | 7.69k | } |
389 | 7.69k | } |
390 | 2.83k | } |
391 | | |
392 | | #if !defined(ZYDIS_DISABLE_ENCODER) |
393 | | |
394 | | static void ZydisReEncodeInstructionAbsolute(ZydisEncoderRequest* req, |
395 | | const ZydisDecodedInstruction* insn2, const ZydisDecodedOperand* insn2_operands, |
396 | | const ZyanU8* insn2_bytes) |
397 | 2.83k | { |
398 | 2.83k | ZyanU64 runtime_address; |
399 | 2.83k | switch (insn2->address_width) |
400 | 2.83k | { |
401 | 504 | case 16: |
402 | 504 | runtime_address = (ZyanU64)(ZyanU16)ZYAN_INT16_MIN; |
403 | 504 | break; |
404 | 503 | case 32: |
405 | 503 | runtime_address = (ZyanU64)(ZyanU32)ZYAN_INT32_MIN; |
406 | 503 | break; |
407 | 1.83k | case 64: |
408 | 1.83k | runtime_address = (ZyanU64)ZYAN_INT64_MIN; |
409 | 1.83k | break; |
410 | 0 | default: |
411 | 0 | ZYAN_UNREACHABLE; |
412 | 2.83k | } |
413 | 2.83k | if ((insn2->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) && (insn2->operand_width == 16)) |
414 | 376 | { |
415 | 376 | runtime_address = (ZyanU64)(ZyanU16)ZYAN_INT16_MIN; |
416 | 376 | } |
417 | 2.83k | runtime_address -= insn2->length; |
418 | | |
419 | 2.83k | ZyanBool has_relative = ZYAN_FALSE; |
420 | 8.57k | for (ZyanU8 i = 0; i < req->operand_count; ++i) |
421 | 5.73k | { |
422 | 5.73k | const ZydisDecodedOperand *decoded_op = &insn2_operands[i]; |
423 | 5.73k | ZydisEncoderOperand *op = &req->operands[i]; |
424 | 5.73k | ZyanU64 *dst_address = ZYAN_NULL; |
425 | 5.73k | switch (op->type) |
426 | 5.73k | { |
427 | 851 | case ZYDIS_OPERAND_TYPE_IMMEDIATE: |
428 | 851 | if (decoded_op->imm.is_relative) |
429 | 231 | { |
430 | 231 | dst_address = &op->imm.u; |
431 | 231 | } |
432 | 851 | break; |
433 | 1.65k | case ZYDIS_OPERAND_TYPE_MEMORY: |
434 | 1.65k | if ((decoded_op->mem.base == ZYDIS_REGISTER_EIP) || |
435 | 1.65k | (decoded_op->mem.base == ZYDIS_REGISTER_RIP)) |
436 | 332 | { |
437 | 332 | dst_address = (ZyanU64 *)&op->mem.displacement; |
438 | 332 | } |
439 | 1.65k | break; |
440 | 3.23k | default: |
441 | 3.23k | break; |
442 | 5.73k | } |
443 | 5.73k | if (!dst_address) |
444 | 5.17k | { |
445 | 5.17k | continue; |
446 | 5.17k | } |
447 | 563 | has_relative = ZYAN_TRUE; |
448 | 563 | if (!ZYAN_SUCCESS(ZydisCalcAbsoluteAddress(insn2, decoded_op, runtime_address, |
449 | 563 | dst_address))) |
450 | 0 | { |
451 | 0 | fputs("ZydisCalcAbsoluteAddress has failed\n", ZYAN_STDERR); |
452 | 0 | abort(); |
453 | 0 | } |
454 | 563 | } |
455 | 2.83k | if (!has_relative) |
456 | 2.27k | { |
457 | 2.27k | return; |
458 | 2.27k | } |
459 | | |
460 | 563 | ZyanU8 insn1_bytes[ZYDIS_MAX_INSTRUCTION_LENGTH]; |
461 | 563 | ZyanUSize insn1_length = sizeof(insn1_bytes); |
462 | 563 | ZyanStatus status = ZydisEncoderEncodeInstructionAbsolute(req, insn1_bytes, &insn1_length, |
463 | 563 | runtime_address); |
464 | 563 | if (!ZYAN_SUCCESS(status)) |
465 | 0 | { |
466 | 0 | fputs("Failed to re-encode instruction (absolute)\n", ZYAN_STDERR); |
467 | 0 | abort(); |
468 | 0 | } |
469 | 563 | if (insn1_length != insn2->length || ZYAN_MEMCMP(insn1_bytes, insn2_bytes, insn2->length)) |
470 | 0 | { |
471 | 0 | fputs("Instruction mismatch (absolute)\n", ZYAN_STDERR); |
472 | 0 | abort(); |
473 | 0 | } |
474 | 563 | } |
475 | | |
476 | | void ZydisReEncodeInstruction(const ZydisDecoder *decoder, const ZydisDecodedInstruction *insn1, |
477 | | const ZydisDecodedOperand* operands1, ZyanU8 operand_count, const ZyanU8 *insn1_bytes) |
478 | 2.83k | { |
479 | 2.83k | ZydisPrintInstruction(insn1, operands1, operand_count, insn1_bytes); |
480 | 2.83k | ZydisValidateEnumRanges(insn1, operands1, operand_count); |
481 | | |
482 | 2.83k | ZYAN_ASSERT(operand_count >= insn1->operand_count_visible); |
483 | | |
484 | 2.83k | ZydisEncoderRequest request; |
485 | 2.83k | ZyanStatus status = ZydisEncoderDecodedInstructionToEncoderRequest(insn1, operands1, |
486 | 2.83k | insn1->operand_count_visible, &request); |
487 | 2.83k | if (!ZYAN_SUCCESS(status)) |
488 | 0 | { |
489 | 0 | fputs("ZydisEncoderDecodedInstructionToEncoderRequest failed\n", ZYAN_STDERR); |
490 | 0 | abort(); |
491 | 0 | } |
492 | | |
493 | 2.83k | ZyanU8 encoded_instruction[ZYDIS_MAX_INSTRUCTION_LENGTH]; |
494 | 2.83k | ZyanUSize encoded_length = sizeof(encoded_instruction); |
495 | 2.83k | status = ZydisEncoderEncodeInstruction(&request, encoded_instruction, &encoded_length); |
496 | 2.83k | if (!ZYAN_SUCCESS(status)) |
497 | 0 | { |
498 | 0 | fputs("Failed to re-encode instruction\n", ZYAN_STDERR); |
499 | 0 | abort(); |
500 | 0 | } |
501 | | |
502 | 2.83k | ZydisDecodedInstruction insn2; |
503 | 2.83k | ZydisDecodedOperand operands2[ZYDIS_MAX_OPERAND_COUNT]; |
504 | 2.83k | status = ZydisDecoderDecodeFull(decoder, encoded_instruction, encoded_length, &insn2, |
505 | 2.83k | operands2); |
506 | 2.83k | if (!ZYAN_SUCCESS(status)) |
507 | 0 | { |
508 | 0 | fputs("Failed to decode re-encoded instruction\n", ZYAN_STDERR); |
509 | 0 | abort(); |
510 | 0 | } |
511 | | |
512 | 2.83k | ZydisPrintInstruction(&insn2, operands2, insn2.operand_count_visible, encoded_instruction); |
513 | 2.83k | ZydisValidateEnumRanges(&insn2, operands2, insn2.operand_count_visible); |
514 | 2.83k | ZydisValidateInstructionIdentity(insn1, operands1, &insn2, operands2); |
515 | | |
516 | 2.83k | if (insn2.length > insn1->length) |
517 | 0 | { |
518 | 0 | fputs("Suboptimal output size detected\n", ZYAN_STDERR); |
519 | 0 | abort(); |
520 | 0 | } |
521 | | |
522 | 2.83k | ZydisReEncodeInstructionAbsolute(&request, &insn2, operands2, encoded_instruction); |
523 | 2.83k | } |
524 | | |
525 | | #endif |
526 | | |
527 | | /* ============================================================================================== */ |
528 | | /* Entry point */ |
529 | | /* ============================================================================================== */ |
530 | | |
531 | | int ZydisFuzzerInit(void) |
532 | 2 | { |
533 | 2 | if (ZydisGetVersion() != ZYDIS_VERSION) |
534 | 0 | { |
535 | 0 | fputs("Invalid Zydis version\n", ZYAN_STDERR); |
536 | 0 | return EXIT_FAILURE; |
537 | 0 | } |
538 | | |
539 | | #ifdef ZYAN_WINDOWS |
540 | | // The `stdin` pipe uses text-mode on Windows platforms by default. We need it to be opened in |
541 | | // binary mode |
542 | | (void)_setmode(_fileno(ZYAN_STDIN), _O_BINARY); |
543 | | #endif |
544 | | |
545 | 2 | return EXIT_SUCCESS; |
546 | 2 | } |
547 | | |
548 | | #ifdef ZYDIS_LIBFUZZER |
549 | | |
550 | | #ifdef __cplusplus |
551 | | extern "C" { |
552 | | #endif |
553 | | |
554 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
555 | 2 | { |
556 | 2 | ZYAN_UNUSED(argc); |
557 | 2 | ZYAN_UNUSED(argv); |
558 | | |
559 | 2 | if (ZydisFuzzerInit() != EXIT_SUCCESS) |
560 | 0 | abort(); // abort manually because LibFuzzer ignores return value |
561 | 2 | return EXIT_SUCCESS; |
562 | 2 | } |
563 | | |
564 | | int LLVMFuzzerTestOneInput(ZyanU8 *buf, ZyanUSize len) |
565 | 3.33k | { |
566 | 3.33k | ZydisLibFuzzerContext ctx; |
567 | 3.33k | ctx.buf = buf; |
568 | 3.33k | ctx.buf_len = len; |
569 | 3.33k | ctx.read_offs = 0; |
570 | | |
571 | 3.33k | ZydisFuzzTarget(&ZydisLibFuzzerRead, &ctx); |
572 | 3.33k | return 0; |
573 | 3.33k | } |
574 | | |
575 | | #ifdef __cplusplus |
576 | | } // extern "C" |
577 | | #endif |
578 | | |
579 | | #else // !ZYDIS_LIBFUZZER |
580 | | |
581 | | int main(void) |
582 | | { |
583 | | if (ZydisFuzzerInit() != EXIT_SUCCESS) |
584 | | { |
585 | | return EXIT_FAILURE; |
586 | | } |
587 | | |
588 | | #ifdef ZYDIS_FUZZ_AFL_FAST |
589 | | while (__AFL_LOOP(1000)) |
590 | | { |
591 | | ZydisFuzzTarget(&ZydisStdinRead, ZYAN_NULL); |
592 | | } |
593 | | return EXIT_SUCCESS; |
594 | | #else |
595 | | return ZydisFuzzTarget(&ZydisStdinRead, ZYAN_NULL); |
596 | | #endif |
597 | | } |
598 | | |
599 | | #endif // ZYDIS_LIBFUZZER |
600 | | |
601 | | /* ============================================================================================== */ |