/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 | 5.44k | { |
73 | 5.44k | ZydisLibFuzzerContext* c = (ZydisLibFuzzerContext*)ctx; |
74 | 5.44k | 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 | 5.44k | if (!len) |
78 | 4 | { |
79 | 4 | return 0; |
80 | 4 | } |
81 | 5.44k | ZYAN_MEMCPY(buf, c->buf + c->read_offs, len); |
82 | 5.44k | c->read_offs += len; |
83 | 5.44k | return len; |
84 | 5.44k | } |
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 | | |
115 | | for (ZyanU8 i = 0; i < instruction->length; ++i) |
116 | | { |
117 | | printf("%02X", instruction_bytes[i]); |
118 | | } |
119 | | |
120 | | ZydisFormatter formatter; |
121 | | if (!ZYAN_SUCCESS(ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL)) || |
122 | | !ZYAN_SUCCESS(ZydisFormatterSetProperty(&formatter, ZYDIS_FORMATTER_PROP_FORCE_SEGMENT, |
123 | | ZYAN_TRUE)) || |
124 | | !ZYAN_SUCCESS(ZydisFormatterSetProperty(&formatter, ZYDIS_FORMATTER_PROP_FORCE_SIZE, |
125 | | ZYAN_TRUE))) |
126 | | { |
127 | | fputs("Failed to initialize instruction formatter\n", ZYAN_STDERR); |
128 | | abort(); |
129 | | } |
130 | | |
131 | | char buffer[256]; |
132 | | ZydisFormatterFormatInstruction(&formatter, instruction, operands, operand_count, buffer, |
133 | | sizeof(buffer), 0, ZYAN_NULL); |
134 | | printf(" %s\n", buffer); |
135 | | } |
136 | | |
137 | | #endif |
138 | | |
139 | | void ZydisValidateImmediateSize(ZyanU64 value) |
140 | 4.07k | { |
141 | 4.07k | if ((value != 0) && |
142 | 4.07k | (value != 8) && |
143 | 4.07k | (value != 16) && |
144 | 4.07k | (value != 32) && |
145 | 4.07k | (value != 64)) |
146 | 0 | { |
147 | 0 | fprintf(stderr, "Value 0x%016" PRIX64 " does not match any of the expected " |
148 | 0 | "values (0, 8, 16, 32, 64).\n", value); |
149 | 0 | abort(); |
150 | 0 | } |
151 | 4.07k | } |
152 | | |
153 | | // NOTE: This function doesn't validate flag values, yet. |
154 | | void ZydisValidateEnumRanges(const ZydisDecodedInstruction* insn, |
155 | | const ZydisDecodedOperand* operands, ZyanU8 operand_count) |
156 | 4.65k | { |
157 | 4.65k | # define ZYDIS_CHECK_ENUM(value, max) \ |
158 | 241k | if ((ZyanU64)(value) > (ZyanU64)(max)) \ |
159 | 241k | { \ |
160 | 0 | fprintf(stderr, "Value " #value " = 0x%016" PRIX64 " is above expected max " #max \ |
161 | 0 | " = 0x%016" PRIX64 "\n", (ZyanU64)(value), (ZyanU64)(max)); \ |
162 | 0 | abort(); \ |
163 | 0 | } |
164 | 4.65k | # define ZYDIS_CHECK_MAX ZYDIS_CHECK_ENUM |
165 | | |
166 | 4.65k | ZYDIS_CHECK_ENUM(insn->length, ZYDIS_MAX_INSTRUCTION_LENGTH); |
167 | | |
168 | 4.65k | ZYDIS_CHECK_ENUM(insn->machine_mode, ZYDIS_MACHINE_MODE_MAX_VALUE); |
169 | 4.65k | ZYDIS_CHECK_ENUM(insn->mnemonic, ZYDIS_MNEMONIC_MAX_VALUE); |
170 | 4.65k | ZYDIS_CHECK_ENUM(insn->encoding, ZYDIS_INSTRUCTION_ENCODING_MAX_VALUE); |
171 | 4.65k | ZYDIS_CHECK_ENUM(insn->opcode_map, ZYDIS_OPCODE_MAP_MAX_VALUE); |
172 | 4.65k | ZYDIS_CHECK_ENUM(insn->opcode_map, ZYDIS_OPCODE_MAP_MAX_VALUE); |
173 | | |
174 | | // Operands. |
175 | 13.9k | for (ZyanU32 i = 0; i < operand_count; ++i) |
176 | 9.26k | { |
177 | 9.26k | const ZydisDecodedOperand* op = &operands[i]; |
178 | 9.26k | ZYDIS_CHECK_ENUM(op->type, ZYDIS_OPERAND_TYPE_MAX_VALUE); |
179 | 9.26k | ZYDIS_CHECK_ENUM(op->visibility, ZYDIS_OPERAND_VISIBILITY_MAX_VALUE); |
180 | 9.26k | ZYDIS_CHECK_ENUM(op->encoding, ZYDIS_OPERAND_ENCODING_MAX_VALUE); |
181 | 9.26k | ZYDIS_CHECK_ENUM(op->element_type, ZYDIS_ELEMENT_TYPE_MAX_VALUE); |
182 | | |
183 | 9.26k | switch (op->type) |
184 | 9.26k | { |
185 | 5.06k | case ZYDIS_OPERAND_TYPE_REGISTER: |
186 | 5.06k | ZYDIS_CHECK_ENUM(op->reg.value, ZYDIS_REGISTER_MAX_VALUE); |
187 | 5.06k | break; |
188 | 2.72k | case ZYDIS_OPERAND_TYPE_MEMORY: |
189 | 2.72k | ZYDIS_CHECK_ENUM(op->mem.type, ZYDIS_MEMOP_TYPE_MAX_VALUE); |
190 | 2.72k | ZYDIS_CHECK_ENUM(op->mem.segment, ZYDIS_REGISTER_MAX_VALUE); |
191 | 2.72k | ZYDIS_CHECK_ENUM(op->mem.base, ZYDIS_REGISTER_MAX_VALUE); |
192 | 2.72k | ZYDIS_CHECK_ENUM(op->mem.index, ZYDIS_REGISTER_MAX_VALUE); |
193 | 2.72k | ZydisValidateImmediateSize(op->mem.disp.size); |
194 | 2.72k | ZYDIS_CHECK_MAX(op->mem.disp.offset + (op->mem.disp.size / 8), insn->length); |
195 | 2.72k | break; |
196 | 1.35k | case ZYDIS_OPERAND_TYPE_IMMEDIATE: |
197 | 1.35k | ZYDIS_CHECK_ENUM(op->imm.is_signed, ZYAN_TRUE); |
198 | 1.35k | ZYDIS_CHECK_ENUM(op->imm.is_relative, ZYAN_TRUE); |
199 | 1.35k | ZydisValidateImmediateSize(op->imm.size); |
200 | 1.35k | ZYDIS_CHECK_MAX(op->imm.offset + (op->imm.size / 8), insn->length); |
201 | 1.35k | break; |
202 | 124 | default: |
203 | 124 | break; |
204 | 9.26k | } |
205 | 9.26k | } |
206 | | |
207 | | // AVX. |
208 | 4.65k | ZYDIS_CHECK_ENUM(insn->avx.mask.mode, ZYDIS_MASK_MODE_MAX_VALUE); |
209 | 4.65k | ZYDIS_CHECK_ENUM(insn->avx.mask.reg, ZYDIS_REGISTER_MAX_VALUE); |
210 | 4.65k | ZYDIS_CHECK_ENUM(insn->avx.broadcast.is_static, ZYAN_TRUE); |
211 | 4.65k | ZYDIS_CHECK_ENUM(insn->avx.broadcast.mode, ZYDIS_BROADCAST_MODE_MAX_VALUE); |
212 | 4.65k | ZYDIS_CHECK_ENUM(insn->avx.rounding.mode, ZYDIS_ROUNDING_MODE_MAX_VALUE); |
213 | 4.65k | ZYDIS_CHECK_ENUM(insn->avx.swizzle.mode, ZYDIS_SWIZZLE_MODE_MAX_VALUE); |
214 | 4.65k | ZYDIS_CHECK_ENUM(insn->avx.conversion.mode, ZYDIS_CONVERSION_MODE_MAX_VALUE); |
215 | 4.65k | ZYDIS_CHECK_ENUM(insn->avx.has_sae, ZYAN_TRUE); |
216 | 4.65k | ZYDIS_CHECK_ENUM(insn->avx.has_eviction_hint, ZYAN_TRUE); |
217 | | |
218 | | // Meta. |
219 | 4.65k | ZYDIS_CHECK_ENUM(insn->meta.category, ZYDIS_CATEGORY_MAX_VALUE); |
220 | 4.65k | ZYDIS_CHECK_ENUM(insn->meta.isa_set, ZYDIS_ISA_SET_MAX_VALUE); |
221 | 4.65k | ZYDIS_CHECK_ENUM(insn->meta.isa_ext, ZYDIS_ISA_SET_MAX_VALUE); |
222 | 4.65k | ZYDIS_CHECK_ENUM(insn->meta.branch_type, ZYDIS_BRANCH_TYPE_MAX_VALUE); |
223 | 4.65k | ZYDIS_CHECK_ENUM(insn->meta.exception_class, ZYDIS_EXCEPTION_CLASS_MAX_VALUE); |
224 | | |
225 | | // Raw. |
226 | 74.4k | for (ZyanU32 i = 0; i < ZYAN_ARRAY_LENGTH(insn->raw.prefixes); ++i) |
227 | 69.7k | { |
228 | 69.7k | ZYDIS_CHECK_ENUM(insn->raw.prefixes[i].type, ZYDIS_PREFIX_TYPE_MAX_VALUE); |
229 | 69.7k | } |
230 | 13.9k | for (ZyanU32 i = 0; i < ZYAN_ARRAY_LENGTH(insn->raw.imm); ++i) |
231 | 9.30k | { |
232 | 9.30k | ZYDIS_CHECK_ENUM(insn->raw.imm[i].is_signed, ZYAN_TRUE); |
233 | 9.30k | ZYDIS_CHECK_ENUM(insn->raw.imm[i].is_relative, ZYAN_TRUE); |
234 | 9.30k | } |
235 | | |
236 | 4.65k | # undef ZYDIS_CHECK_ENUM |
237 | 4.65k | # undef ZYDIS_CHECK_MAX |
238 | 4.65k | } |
239 | | |
240 | | void ZydisValidateInstructionIdentity(const ZydisDecodedInstruction* insn1, |
241 | | const ZydisDecodedOperand* operands1, const ZydisDecodedInstruction* insn2, |
242 | | const ZydisDecodedOperand* operands2) |
243 | 2.32k | { |
244 | | // TODO: Probably a good idea to input validate operand_counts to this function |
245 | | // TODO: I don't like accessing buffers without having their actual sizes available... |
246 | | |
247 | | // Special case, `xchg rAX, rAX` is an alias for `NOP` except `xchg eax, eax` in 64-bit mode |
248 | 2.32k | if ((insn1->mnemonic == ZYDIS_MNEMONIC_XCHG) && |
249 | 2.32k | (insn1->operand_count == 2) && |
250 | 2.32k | (operands1[0].type == ZYDIS_OPERAND_TYPE_REGISTER) && |
251 | 2.32k | (operands1[1].type == ZYDIS_OPERAND_TYPE_REGISTER) && |
252 | 2.32k | (operands1[0].reg.value == operands1[1].reg.value) && |
253 | 2.32k | (insn2->mnemonic == ZYDIS_MNEMONIC_NOP)) |
254 | 4 | { |
255 | 4 | switch (operands1[0].reg.value) |
256 | 4 | { |
257 | 1 | case ZYDIS_REGISTER_AX: |
258 | 2 | case ZYDIS_REGISTER_RAX: |
259 | 2 | return; |
260 | 2 | case ZYDIS_REGISTER_EAX: |
261 | 2 | if (insn1->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) |
262 | 2 | return; |
263 | 0 | break; |
264 | 0 | default: |
265 | 0 | break; |
266 | 4 | } |
267 | 4 | } |
268 | | |
269 | 2.32k | ZydisSwizzleMode swizzle1 = insn1->avx.swizzle.mode == ZYDIS_SWIZZLE_MODE_DCBA ? |
270 | 2.32k | ZYDIS_SWIZZLE_MODE_INVALID : insn1->avx.swizzle.mode; |
271 | 2.32k | ZydisSwizzleMode swizzle2 = insn2->avx.swizzle.mode == ZYDIS_SWIZZLE_MODE_DCBA ? |
272 | 2.31k | ZYDIS_SWIZZLE_MODE_INVALID : insn2->avx.swizzle.mode; |
273 | 2.32k | if ((insn1->machine_mode != insn2->machine_mode) || |
274 | 2.32k | (insn1->mnemonic != insn2->mnemonic) || |
275 | 2.32k | (insn1->stack_width != insn2->stack_width) || |
276 | 2.32k | (insn1->operand_count != insn2->operand_count) || |
277 | 2.32k | (insn1->avx.mask.mode != insn2->avx.mask.mode) || |
278 | 2.32k | (insn1->avx.broadcast.is_static != insn2->avx.broadcast.is_static) || |
279 | 2.32k | (insn1->avx.broadcast.mode != insn2->avx.broadcast.mode) || |
280 | 2.32k | (insn1->avx.conversion.mode != insn2->avx.conversion.mode) || |
281 | 2.32k | (insn1->avx.rounding.mode != insn2->avx.rounding.mode) || |
282 | 2.32k | (insn1->avx.has_sae != insn2->avx.has_sae) || |
283 | 2.32k | (insn1->avx.has_eviction_hint != insn2->avx.has_eviction_hint) || |
284 | 2.32k | (swizzle1 != swizzle2)) |
285 | 0 | { |
286 | 0 | fputs("Basic instruction attributes mismatch\n", ZYAN_STDERR); |
287 | 0 | abort(); |
288 | 0 | } |
289 | | |
290 | 8.53k | for (ZyanU8 i = 0; i < insn1->operand_count; ++i) |
291 | 6.21k | { |
292 | 6.21k | const ZydisDecodedOperand *op1 = &operands1[i]; |
293 | 6.21k | const ZydisDecodedOperand *op2 = &operands2[i]; |
294 | 6.21k | if ((op1->type != op2->type) || |
295 | 6.21k | (op1->size != op2->size && op1->type != ZYDIS_OPERAND_TYPE_IMMEDIATE)) |
296 | 0 | { |
297 | 0 | fprintf(ZYAN_STDERR, "Mismatch for operand %u\n", i); |
298 | 0 | abort(); |
299 | 0 | } |
300 | 6.21k | switch (op1->type) |
301 | 6.21k | { |
302 | 3.86k | case ZYDIS_OPERAND_TYPE_REGISTER: |
303 | 3.86k | if (op1->reg.value != op2->reg.value) |
304 | 0 | { |
305 | 0 | fprintf(ZYAN_STDERR, "Mismatch for register operand %u\n", i); |
306 | 0 | abort(); |
307 | 0 | } |
308 | 3.86k | break; |
309 | 3.86k | case ZYDIS_OPERAND_TYPE_MEMORY: |
310 | 1.61k | { |
311 | | // Usually this check is done after verifying instruction identity but in this case |
312 | | // we have to fail early |
313 | 1.61k | if (insn1->length < insn2->length) |
314 | 0 | { |
315 | 0 | fputs("Suboptimal output size detected\n", ZYAN_STDERR); |
316 | 0 | abort(); |
317 | 0 | } |
318 | 1.61k | ZyanU64 addr1, addr2; |
319 | 1.61k | ZyanStatus status1 = ZydisCalcAbsoluteAddress(insn1, op1, 0, &addr1); |
320 | 1.61k | ZyanStatus status2 = ZydisCalcAbsoluteAddress(insn2, op2, |
321 | 1.61k | insn1->length - insn2->length, &addr2); |
322 | 1.61k | ZyanBool addresses_match = ZYAN_FALSE; |
323 | 1.61k | if (ZYAN_SUCCESS(status1) && ZYAN_SUCCESS(status2)) |
324 | 821 | { |
325 | 821 | if (addr1 != addr2) |
326 | 0 | { |
327 | 0 | fprintf(ZYAN_STDERR, "Mismatch for memory operand %u (absolute address)\n", i); |
328 | 0 | abort(); |
329 | 0 | } |
330 | 821 | addresses_match = ZYAN_TRUE; |
331 | 821 | } |
332 | 1.61k | if ((op1->mem.type != op2->mem.type) || |
333 | 1.61k | (op1->mem.segment != op2->mem.segment) || |
334 | 1.61k | (op1->mem.base != op2->mem.base) || |
335 | 1.61k | (op1->mem.index != op2->mem.index) || |
336 | 1.61k | ((op1->mem.scale != op2->mem.scale) && (op1->mem.type != ZYDIS_MEMOP_TYPE_MIB)) || |
337 | 1.61k | ((op1->mem.disp.value != op2->mem.disp.value) && !addresses_match)) |
338 | 0 | { |
339 | 0 | fprintf(ZYAN_STDERR, "Mismatch for memory operand %u\n", i); |
340 | 0 | abort(); |
341 | 0 | } |
342 | 1.61k | break; |
343 | 1.61k | } |
344 | 1.61k | case ZYDIS_OPERAND_TYPE_POINTER: |
345 | 62 | if ((op1->ptr.segment != op2->ptr.segment) || |
346 | 62 | (op1->ptr.offset != op2->ptr.offset)) |
347 | 0 | { |
348 | 0 | fprintf(ZYAN_STDERR, "Mismatch for pointer operand %u\n", i); |
349 | 0 | abort(); |
350 | 0 | } |
351 | 62 | break; |
352 | 675 | case ZYDIS_OPERAND_TYPE_IMMEDIATE: |
353 | 675 | if ((op1->imm.is_relative != op2->imm.is_relative) || |
354 | 675 | (op1->imm.is_signed != op2->imm.is_signed) || |
355 | 675 | (op1->imm.value.u != op2->imm.value.u)) |
356 | 0 | { |
357 | 0 | ZyanBool acceptable_mismatch = ZYAN_FALSE; |
358 | 0 | if ((insn1->meta.category == ZYDIS_CATEGORY_DATAXFER) || |
359 | 0 | (insn1->meta.category == ZYDIS_CATEGORY_LOGICAL)) |
360 | 0 | { |
361 | 0 | const ZyanU16 size = ZYAN_MAX(op1->size, op2->size); |
362 | 0 | if (size < 64) |
363 | 0 | { |
364 | 0 | const ZyanU64 mask = (1ULL << size) - 1; |
365 | 0 | acceptable_mismatch = |
366 | 0 | (op1->imm.value.u & mask) == (op2->imm.value.u & mask); |
367 | 0 | } |
368 | 0 | else |
369 | 0 | { |
370 | 0 | acceptable_mismatch = op1->imm.value.u == op2->imm.value.u; |
371 | 0 | } |
372 | 0 | } |
373 | 0 | if (!acceptable_mismatch) |
374 | 0 | { |
375 | 0 | fprintf(ZYAN_STDERR, "Mismatch for immediate operand %u\n", i); |
376 | 0 | abort(); |
377 | 0 | } |
378 | 0 | } |
379 | 675 | break; |
380 | 675 | default: |
381 | 0 | fprintf(ZYAN_STDERR, "Invalid operand type for operand %u\n", i); |
382 | 0 | abort(); |
383 | 6.21k | } |
384 | 6.21k | } |
385 | 2.32k | } |
386 | | |
387 | | #if !defined(ZYDIS_DISABLE_ENCODER) |
388 | | |
389 | | static void ZydisReEncodeInstructionAbsolute(ZydisEncoderRequest* req, |
390 | | const ZydisDecodedInstruction* insn2, const ZydisDecodedOperand* insn2_operands, |
391 | | const ZyanU8* insn2_bytes) |
392 | 2.32k | { |
393 | 2.32k | ZyanU64 runtime_address; |
394 | 2.32k | switch (insn2->address_width) |
395 | 2.32k | { |
396 | 508 | case 16: |
397 | 508 | runtime_address = (ZyanU64)(ZyanU16)ZYAN_INT16_MIN; |
398 | 508 | break; |
399 | 448 | case 32: |
400 | 448 | runtime_address = (ZyanU64)(ZyanU32)ZYAN_INT32_MIN; |
401 | 448 | break; |
402 | 1.36k | case 64: |
403 | 1.36k | runtime_address = (ZyanU64)ZYAN_INT64_MIN; |
404 | 1.36k | break; |
405 | 0 | default: |
406 | 0 | ZYAN_UNREACHABLE; |
407 | 2.32k | } |
408 | 2.32k | if ((insn2->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) && (insn2->operand_width == 16)) |
409 | 389 | { |
410 | 389 | runtime_address = (ZyanU64)(ZyanU16)ZYAN_INT16_MIN; |
411 | 389 | } |
412 | 2.32k | runtime_address -= insn2->length; |
413 | | |
414 | 2.32k | ZyanBool has_relative = ZYAN_FALSE; |
415 | 6.96k | for (ZyanU8 i = 0; i < req->operand_count; ++i) |
416 | 4.63k | { |
417 | 4.63k | const ZydisDecodedOperand *decoded_op = &insn2_operands[i]; |
418 | 4.63k | ZydisEncoderOperand *op = &req->operands[i]; |
419 | 4.63k | ZyanU64 *dst_address = ZYAN_NULL; |
420 | 4.63k | switch (op->type) |
421 | 4.63k | { |
422 | 675 | case ZYDIS_OPERAND_TYPE_IMMEDIATE: |
423 | 675 | if (decoded_op->imm.is_relative) |
424 | 208 | { |
425 | 208 | dst_address = &op->imm.u; |
426 | 208 | } |
427 | 675 | break; |
428 | 1.36k | case ZYDIS_OPERAND_TYPE_MEMORY: |
429 | 1.36k | if ((decoded_op->mem.base == ZYDIS_REGISTER_EIP) || |
430 | 1.36k | (decoded_op->mem.base == ZYDIS_REGISTER_RIP)) |
431 | 295 | { |
432 | 295 | dst_address = (ZyanU64 *)&op->mem.displacement; |
433 | 295 | } |
434 | 1.36k | break; |
435 | 2.60k | default: |
436 | 2.60k | break; |
437 | 4.63k | } |
438 | 4.63k | if (!dst_address) |
439 | 4.13k | { |
440 | 4.13k | continue; |
441 | 4.13k | } |
442 | 503 | has_relative = ZYAN_TRUE; |
443 | 503 | if (!ZYAN_SUCCESS(ZydisCalcAbsoluteAddress(insn2, decoded_op, runtime_address, |
444 | 503 | dst_address))) |
445 | 0 | { |
446 | 0 | fputs("ZydisCalcAbsoluteAddress has failed\n", ZYAN_STDERR); |
447 | 0 | abort(); |
448 | 0 | } |
449 | 503 | } |
450 | 2.32k | if (!has_relative) |
451 | 1.82k | { |
452 | 1.82k | return; |
453 | 1.82k | } |
454 | | |
455 | 503 | ZyanU8 insn1_bytes[ZYDIS_MAX_INSTRUCTION_LENGTH]; |
456 | 503 | ZyanUSize insn1_length = sizeof(insn1_bytes); |
457 | 503 | ZyanStatus status = ZydisEncoderEncodeInstructionAbsolute(req, insn1_bytes, &insn1_length, |
458 | 503 | runtime_address); |
459 | 503 | if (!ZYAN_SUCCESS(status)) |
460 | 0 | { |
461 | 0 | fputs("Failed to re-encode instruction (absolute)\n", ZYAN_STDERR); |
462 | 0 | abort(); |
463 | 0 | } |
464 | 503 | if (insn1_length != insn2->length || ZYAN_MEMCMP(insn1_bytes, insn2_bytes, insn2->length)) |
465 | 0 | { |
466 | 0 | fputs("Instruction mismatch (absolute)\n", ZYAN_STDERR); |
467 | 0 | abort(); |
468 | 0 | } |
469 | 503 | } |
470 | | |
471 | | void ZydisReEncodeInstruction(const ZydisDecoder *decoder, const ZydisDecodedInstruction *insn1, |
472 | | const ZydisDecodedOperand* operands1, ZyanU8 operand_count, const ZyanU8 *insn1_bytes) |
473 | 2.32k | { |
474 | 2.32k | ZydisPrintInstruction(insn1, operands1, operand_count, insn1_bytes); |
475 | 2.32k | ZydisValidateEnumRanges(insn1, operands1, operand_count); |
476 | | |
477 | 2.32k | ZYAN_ASSERT(operand_count >= insn1->operand_count_visible); |
478 | | |
479 | 2.32k | ZydisEncoderRequest request; |
480 | 2.32k | ZyanStatus status = ZydisEncoderDecodedInstructionToEncoderRequest(insn1, operands1, |
481 | 2.32k | insn1->operand_count_visible, &request); |
482 | 2.32k | if (!ZYAN_SUCCESS(status)) |
483 | 0 | { |
484 | 0 | fputs("ZydisEncoderDecodedInstructionToEncoderRequest failed\n", ZYAN_STDERR); |
485 | 0 | abort(); |
486 | 0 | } |
487 | | |
488 | 2.32k | ZyanU8 encoded_instruction[ZYDIS_MAX_INSTRUCTION_LENGTH]; |
489 | 2.32k | ZyanUSize encoded_length = sizeof(encoded_instruction); |
490 | 2.32k | status = ZydisEncoderEncodeInstruction(&request, encoded_instruction, &encoded_length); |
491 | 2.32k | if (!ZYAN_SUCCESS(status)) |
492 | 0 | { |
493 | 0 | fputs("Failed to re-encode instruction\n", ZYAN_STDERR); |
494 | 0 | abort(); |
495 | 0 | } |
496 | | |
497 | 2.32k | ZydisDecodedInstruction insn2; |
498 | 2.32k | ZydisDecodedOperand operands2[ZYDIS_MAX_OPERAND_COUNT]; |
499 | 2.32k | status = ZydisDecoderDecodeFull(decoder, encoded_instruction, encoded_length, &insn2, |
500 | 2.32k | operands2); |
501 | 2.32k | if (!ZYAN_SUCCESS(status)) |
502 | 0 | { |
503 | 0 | fputs("Failed to decode re-encoded instruction\n", ZYAN_STDERR); |
504 | 0 | abort(); |
505 | 0 | } |
506 | | |
507 | 2.32k | ZydisPrintInstruction(&insn2, operands2, insn2.operand_count_visible, encoded_instruction); |
508 | 2.32k | ZydisValidateEnumRanges(&insn2, operands2, insn2.operand_count_visible); |
509 | 2.32k | ZydisValidateInstructionIdentity(insn1, operands1, &insn2, operands2); |
510 | | |
511 | 2.32k | if (insn2.length > insn1->length) |
512 | 0 | { |
513 | 0 | fputs("Suboptimal output size detected\n", ZYAN_STDERR); |
514 | 0 | abort(); |
515 | 0 | } |
516 | | |
517 | 2.32k | ZydisReEncodeInstructionAbsolute(&request, &insn2, operands2, encoded_instruction); |
518 | 2.32k | } |
519 | | |
520 | | #endif |
521 | | |
522 | | /* ============================================================================================== */ |
523 | | /* Entry point */ |
524 | | /* ============================================================================================== */ |
525 | | |
526 | | int ZydisFuzzerInit(void) |
527 | 2 | { |
528 | 2 | if (ZydisGetVersion() != ZYDIS_VERSION) |
529 | 0 | { |
530 | 0 | fputs("Invalid Zydis version\n", ZYAN_STDERR); |
531 | 0 | return EXIT_FAILURE; |
532 | 0 | } |
533 | | |
534 | | #ifdef ZYAN_WINDOWS |
535 | | // The `stdin` pipe uses text-mode on Windows platforms by default. We need it to be opened in |
536 | | // binary mode |
537 | | (void)_setmode(_fileno(ZYAN_STDIN), _O_BINARY); |
538 | | #endif |
539 | | |
540 | 2 | return EXIT_SUCCESS; |
541 | 2 | } |
542 | | |
543 | | #ifdef ZYDIS_LIBFUZZER |
544 | | |
545 | | #ifdef __cplusplus |
546 | | extern "C" { |
547 | | #endif |
548 | | |
549 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
550 | 2 | { |
551 | 2 | ZYAN_UNUSED(argc); |
552 | 2 | ZYAN_UNUSED(argv); |
553 | | |
554 | 2 | return ZydisFuzzerInit(); |
555 | 2 | } |
556 | | |
557 | | int LLVMFuzzerTestOneInput(ZyanU8 *buf, ZyanUSize len) |
558 | 2.76k | { |
559 | 2.76k | ZydisLibFuzzerContext ctx; |
560 | 2.76k | ctx.buf = buf; |
561 | 2.76k | ctx.buf_len = len; |
562 | 2.76k | ctx.read_offs = 0; |
563 | | |
564 | 2.76k | ZydisFuzzTarget(&ZydisLibFuzzerRead, &ctx); |
565 | 2.76k | return 0; |
566 | 2.76k | } |
567 | | |
568 | | #ifdef __cplusplus |
569 | | } // extern "C" |
570 | | #endif |
571 | | |
572 | | #else // !ZYDIS_LIBFUZZER |
573 | | |
574 | | int main(void) |
575 | | { |
576 | | if (ZydisFuzzerInit() != EXIT_SUCCESS) |
577 | | { |
578 | | return EXIT_FAILURE; |
579 | | } |
580 | | |
581 | | #ifdef ZYDIS_FUZZ_AFL_FAST |
582 | | while (__AFL_LOOP(1000)) |
583 | | { |
584 | | ZydisFuzzTarget(&ZydisStdinRead, ZYAN_NULL); |
585 | | } |
586 | | return EXIT_SUCCESS; |
587 | | #else |
588 | | return ZydisFuzzTarget(&ZydisStdinRead, ZYAN_NULL); |
589 | | #endif |
590 | | } |
591 | | |
592 | | #endif // ZYDIS_LIBFUZZER |
593 | | |
594 | | /* ============================================================================================== */ |