/src/zydis/tools/ZydisFuzzEncoder.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*************************************************************************************************** |
2 | | |
3 | | Zyan Disassembler Library (Zydis) |
4 | | |
5 | | Original Author : Mappa |
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 fuzz target for encoder. |
31 | | */ |
32 | | |
33 | | #include "ZydisFuzzShared.h" |
34 | | |
35 | | /* ============================================================================================== */ |
36 | | /* Fuzz target */ |
37 | | /* ============================================================================================== */ |
38 | | |
39 | | // TODO: This could check `EVEX`/`MVEX` stuff as well |
40 | | void ZydisCompareRequestToInstruction(const ZydisEncoderRequest *request, |
41 | | const ZydisDecodedInstruction *insn, const ZydisDecodedOperand* operands) |
42 | 1.31k | { |
43 | | // Special case, `xchg rAX, rAX` is an alias for `NOP` |
44 | 1.31k | if ((request->mnemonic == ZYDIS_MNEMONIC_XCHG) && |
45 | 1.31k | (request->operand_count == 2) && |
46 | 1.31k | (request->operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER) && |
47 | 1.31k | (request->operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER) && |
48 | 1.31k | (request->operands[0].reg.value == request->operands[1].reg.value) && |
49 | 1.31k | (insn->mnemonic == ZYDIS_MNEMONIC_NOP)) |
50 | 4 | { |
51 | 4 | switch (request->operands[0].reg.value) |
52 | 4 | { |
53 | 1 | case ZYDIS_REGISTER_AX: |
54 | 3 | case ZYDIS_REGISTER_EAX: |
55 | 4 | case ZYDIS_REGISTER_RAX: |
56 | 4 | return; |
57 | 0 | default: |
58 | 0 | break; |
59 | 4 | } |
60 | 4 | } |
61 | | |
62 | 1.31k | ZyanBool prefixes_match = ((insn->attributes & request->prefixes) == request->prefixes); |
63 | 1.31k | if (!prefixes_match && |
64 | 1.31k | (request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) && |
65 | 1.31k | (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_DS)) |
66 | 7 | { |
67 | | // Encoder allows specifying DS override even when it might be interpreted as NOTRACK |
68 | 7 | ZyanU64 acceptable_prefixes = (request->prefixes & (~ZYDIS_ATTRIB_HAS_SEGMENT_DS)) | |
69 | 7 | ZYDIS_ATTRIB_HAS_NOTRACK; |
70 | 7 | prefixes_match = ((insn->attributes & acceptable_prefixes) == acceptable_prefixes); |
71 | 7 | } |
72 | 1.31k | if ((request->machine_mode != insn->machine_mode) || |
73 | 1.31k | (request->mnemonic != insn->mnemonic) || |
74 | 1.31k | (request->operand_count != insn->operand_count_visible) || |
75 | 1.31k | !prefixes_match) |
76 | 0 | { |
77 | 0 | fputs("Basic instruction attributes mismatch\n", ZYAN_STDERR); |
78 | 0 | abort(); |
79 | 0 | } |
80 | | |
81 | 3.87k | for (ZyanU8 i = 0; i < insn->operand_count_visible; ++i) |
82 | 2.56k | { |
83 | 2.56k | const ZydisEncoderOperand *op1 = &request->operands[i]; |
84 | 2.56k | const ZydisDecodedOperand *op2 = &operands[i]; |
85 | 2.56k | if (op1->type != op2->type) |
86 | 0 | { |
87 | 0 | fprintf(ZYAN_STDERR, "Mismatch for operand %u\n", i); |
88 | 0 | abort(); |
89 | 0 | } |
90 | 2.56k | switch (op1->type) |
91 | 2.56k | { |
92 | 1.35k | case ZYDIS_OPERAND_TYPE_REGISTER: |
93 | 1.35k | if (op1->reg.value != op2->reg.value) |
94 | 0 | { |
95 | 0 | fprintf(ZYAN_STDERR, "Mismatch for register operand %u\n", i); |
96 | 0 | abort(); |
97 | 0 | } |
98 | 1.35k | break; |
99 | 1.35k | case ZYDIS_OPERAND_TYPE_MEMORY: |
100 | 595 | if ((op1->mem.base != op2->mem.base) || |
101 | 595 | (op1->mem.index != op2->mem.index) || |
102 | 595 | (op1->mem.scale != op2->mem.scale && op2->mem.type != ZYDIS_MEMOP_TYPE_MIB) || |
103 | 595 | (op1->mem.displacement != op2->mem.disp.value)) |
104 | 0 | { |
105 | 0 | ZyanBool acceptable_mismatch = ZYAN_FALSE; |
106 | 0 | if (op1->mem.displacement != op2->mem.disp.value) |
107 | 0 | { |
108 | 0 | if ((op2->mem.disp.size) && |
109 | 0 | (op1->mem.index == ZYDIS_REGISTER_NONE) && |
110 | 0 | ((op1->mem.base == ZYDIS_REGISTER_NONE) || |
111 | 0 | (op1->mem.base == ZYDIS_REGISTER_EIP) || |
112 | 0 | (op1->mem.base == ZYDIS_REGISTER_RIP))) |
113 | 0 | { |
114 | 0 | ZyanU64 addr; |
115 | 0 | ZydisCalcAbsoluteAddress(insn, op2, 0, &addr); |
116 | 0 | acceptable_mismatch = ((ZyanU64)op1->mem.displacement == addr); |
117 | 0 | } |
118 | 0 | if ((insn->machine_mode == ZYDIS_MACHINE_MODE_REAL_16) || |
119 | 0 | (insn->machine_mode == ZYDIS_MACHINE_MODE_LEGACY_16) || |
120 | 0 | (insn->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_16) || |
121 | 0 | (insn->stack_width == 16) || |
122 | 0 | (insn->address_width == 16)) |
123 | 0 | { |
124 | 0 | acceptable_mismatch = ((op1->mem.displacement & 0xFFFF) == |
125 | 0 | (op2->mem.disp.value & 0xFFFF)); |
126 | 0 | } |
127 | 0 | } |
128 | 0 | if (!acceptable_mismatch) |
129 | 0 | { |
130 | 0 | fprintf(ZYAN_STDERR, "Mismatch for memory operand %u\n", i); |
131 | 0 | abort(); |
132 | 0 | } |
133 | 0 | } |
134 | 595 | break; |
135 | 595 | case ZYDIS_OPERAND_TYPE_POINTER: |
136 | 37 | if ((op1->ptr.segment != op2->ptr.segment) || |
137 | 37 | (op1->ptr.offset != op2->ptr.offset)) |
138 | 0 | { |
139 | 0 | fprintf(ZYAN_STDERR, "Mismatch for pointer operand %u\n", i); |
140 | 0 | abort(); |
141 | 0 | } |
142 | 37 | break; |
143 | 574 | case ZYDIS_OPERAND_TYPE_IMMEDIATE: |
144 | 574 | if (op1->imm.u != op2->imm.value.u) |
145 | 0 | { |
146 | 0 | ZyanBool acceptable_mismatch = ZYAN_FALSE; |
147 | 0 | if ((insn->meta.category == ZYDIS_CATEGORY_DATAXFER) || |
148 | 0 | (insn->meta.category == ZYDIS_CATEGORY_LOGICAL)) |
149 | 0 | { |
150 | 0 | if (op2->size < 64) |
151 | 0 | { |
152 | 0 | ZyanU64 mask = (1ULL << op2->size) - 1; |
153 | 0 | acceptable_mismatch = |
154 | 0 | (op1->imm.u & mask) == (op2->imm.value.u & mask); |
155 | 0 | } |
156 | 0 | else |
157 | 0 | { |
158 | 0 | acceptable_mismatch = op1->imm.u == op2->imm.value.u; |
159 | 0 | } |
160 | 0 | } |
161 | 0 | if (!acceptable_mismatch) |
162 | 0 | { |
163 | 0 | fprintf(ZYAN_STDERR, "Mismatch for immediate operand %u\n", i); |
164 | 0 | abort(); |
165 | 0 | } |
166 | 0 | } |
167 | 574 | break; |
168 | 574 | default: |
169 | 0 | fprintf(ZYAN_STDERR, "Invalid operand type for operand %u\n", i); |
170 | 0 | abort(); |
171 | 2.56k | } |
172 | 2.56k | } |
173 | 1.31k | } |
174 | | |
175 | | ZYAN_NO_SANITIZE("enum") |
176 | | int ZydisFuzzTarget(ZydisStreamRead read_fn, void *stream_ctx) |
177 | 3.16k | { |
178 | 3.16k | ZydisEncoderRequest request; |
179 | 3.16k | if (read_fn(stream_ctx, (ZyanU8 *)&request, sizeof(request)) != sizeof(request)) |
180 | 23 | { |
181 | 23 | ZYDIS_MAYBE_FPUTS("Not enough bytes to fuzz\n", ZYAN_STDERR); |
182 | 23 | return EXIT_SUCCESS; |
183 | 23 | } |
184 | | |
185 | | // Sanitization greatly improves coverage, without it most inputs will fail at basic checks |
186 | | // inside `ZydisEncoderCheckRequestSanity` |
187 | 3.13k | request.operand_count %= ZYDIS_ENCODER_MAX_OPERANDS + 1; |
188 | 3.13k | ZYDIS_SANITIZE_MASK32(request.allowed_encodings, ZydisEncodableEncoding, |
189 | 3.13k | ZYDIS_ENCODABLE_ENCODING_MAX_VALUE); |
190 | 3.13k | ZYDIS_SANITIZE_MASK64(request.prefixes, ZydisInstructionAttributes, ZYDIS_ENCODABLE_PREFIXES); |
191 | 3.13k | ZYDIS_SANITIZE_ENUM(request.machine_mode, ZydisMachineMode, ZYDIS_MACHINE_MODE_MAX_VALUE); |
192 | 3.13k | ZYDIS_SANITIZE_ENUM(request.mnemonic, ZydisMnemonic, ZYDIS_MNEMONIC_MAX_VALUE); |
193 | 3.13k | ZYDIS_SANITIZE_ENUM(request.branch_type, ZydisBranchType, ZYDIS_BRANCH_TYPE_MAX_VALUE); |
194 | 3.13k | ZYDIS_SANITIZE_ENUM(request.branch_width, ZydisBranchWidth, ZYDIS_BRANCH_WIDTH_MAX_VALUE); |
195 | 3.13k | ZYDIS_SANITIZE_ENUM(request.address_size_hint, ZydisAddressSizeHint, |
196 | 3.13k | ZYDIS_ADDRESS_SIZE_HINT_MAX_VALUE); |
197 | 3.13k | ZYDIS_SANITIZE_ENUM(request.operand_size_hint, ZydisOperandSizeHint, |
198 | 3.13k | ZYDIS_OPERAND_SIZE_HINT_MAX_VALUE); |
199 | 3.13k | ZYDIS_SANITIZE_ENUM(request.evex.broadcast, ZydisBroadcastMode, ZYDIS_BROADCAST_MODE_MAX_VALUE); |
200 | 3.13k | ZYDIS_SANITIZE_ENUM(request.evex.rounding, ZydisRoundingMode, ZYDIS_ROUNDING_MODE_MAX_VALUE); |
201 | 3.13k | ZYDIS_SANITIZE_ENUM(request.mvex.broadcast, ZydisBroadcastMode, ZYDIS_BROADCAST_MODE_MAX_VALUE); |
202 | 3.13k | ZYDIS_SANITIZE_ENUM(request.mvex.conversion, ZydisConversionMode, |
203 | 3.13k | ZYDIS_CONVERSION_MODE_MAX_VALUE); |
204 | 3.13k | ZYDIS_SANITIZE_ENUM(request.mvex.rounding, ZydisRoundingMode, ZYDIS_ROUNDING_MODE_MAX_VALUE); |
205 | 3.13k | ZYDIS_SANITIZE_ENUM(request.mvex.swizzle, ZydisSwizzleMode, ZYDIS_SWIZZLE_MODE_MAX_VALUE); |
206 | 9.79k | for (ZyanU8 i = 0; i < request.operand_count; ++i) |
207 | 6.65k | { |
208 | 6.65k | ZydisEncoderOperand *op = &request.operands[i]; |
209 | 6.65k | op->type = (ZydisOperandType)(ZYDIS_OPERAND_TYPE_REGISTER + |
210 | 6.65k | ((ZyanUSize)op->type % ZYDIS_OPERAND_TYPE_MAX_VALUE)); |
211 | 6.65k | switch (op->type) |
212 | 6.65k | { |
213 | 3.65k | case ZYDIS_OPERAND_TYPE_REGISTER: |
214 | 3.65k | ZYDIS_SANITIZE_ENUM(op->reg.value, ZydisRegister, ZYDIS_REGISTER_MAX_VALUE); |
215 | 3.65k | break; |
216 | 1.81k | case ZYDIS_OPERAND_TYPE_MEMORY: |
217 | 1.81k | ZYDIS_SANITIZE_ENUM(op->mem.base, ZydisRegister, ZYDIS_REGISTER_MAX_VALUE); |
218 | 1.81k | ZYDIS_SANITIZE_ENUM(op->mem.index, ZydisRegister, ZYDIS_REGISTER_MAX_VALUE); |
219 | 1.81k | break; |
220 | 90 | case ZYDIS_OPERAND_TYPE_POINTER: |
221 | 1.19k | case ZYDIS_OPERAND_TYPE_IMMEDIATE: |
222 | 1.19k | break; |
223 | 0 | default: |
224 | 0 | ZYAN_UNREACHABLE; |
225 | 6.65k | } |
226 | 6.65k | } |
227 | | |
228 | | // APX has conflicts with KNC, so we override `ZYDIS_ENCODABLE_ENCODING_DEFAULT` to disallow |
229 | | // `MVEX` unless `MVEX`-only features are required. |
230 | 3.13k | const ZyanBool requested_knc = |
231 | 3.13k | (request.mvex.broadcast != ZYDIS_BROADCAST_MODE_NONE) || |
232 | 3.13k | (request.mvex.conversion != ZYDIS_CONVERSION_MODE_NONE) || |
233 | 3.13k | (request.mvex.rounding != ZYDIS_ROUNDING_MODE_NONE) || |
234 | 3.13k | (request.mvex.swizzle != ZYDIS_SWIZZLE_MODE_NONE) || |
235 | 3.13k | (request.mvex.sae) || |
236 | 3.13k | (request.mvex.eviction_hint) || |
237 | 3.13k | (request.mnemonic == ZYDIS_MNEMONIC_KMOV) || |
238 | 3.13k | (request.mnemonic == ZYDIS_MNEMONIC_KNOT) || |
239 | 3.13k | (request.mnemonic == ZYDIS_MNEMONIC_KORTEST); |
240 | 3.13k | static const ZydisEncodableEncoding conflicting_encodings = |
241 | 3.13k | ZYDIS_ENCODABLE_ENCODING_EVEX | ZYDIS_ENCODABLE_ENCODING_MVEX; |
242 | 3.13k | static const ZydisEncodableEncoding no_mvex = (ZydisEncodableEncoding) |
243 | 3.13k | (ZYDIS_ENCODABLE_ENCODING_MAX_VALUE & (~ZYDIS_ENCODABLE_ENCODING_MVEX)); |
244 | 3.13k | if ((request.allowed_encodings == ZYDIS_ENCODABLE_ENCODING_DEFAULT) && (!requested_knc)) |
245 | 1.70k | { |
246 | 1.70k | request.allowed_encodings = no_mvex; |
247 | 1.70k | } |
248 | 1.43k | else if ((request.allowed_encodings & conflicting_encodings) == conflicting_encodings) |
249 | 730 | { |
250 | | // In case of potential conflict favor `EVEX` space |
251 | 730 | request.allowed_encodings = |
252 | 730 | (ZydisEncodableEncoding)(request.allowed_encodings & (~ZYDIS_ENCODABLE_ENCODING_MVEX)); |
253 | 730 | } |
254 | 3.13k | if ((request.allowed_encodings & ZYDIS_ENCODABLE_ENCODING_MVEX) != 0) |
255 | 153 | { |
256 | 153 | if ((request.mnemonic == ZYDIS_MNEMONIC_KMOVW) || |
257 | 153 | (request.mnemonic == ZYDIS_MNEMONIC_KNOTW) || |
258 | 153 | (request.mnemonic == ZYDIS_MNEMONIC_KORTESTW)) |
259 | 3 | { |
260 | 3 | request.allowed_encodings = no_mvex; |
261 | 3 | } |
262 | 153 | } |
263 | | |
264 | 3.13k | ZyanU8 encoded_instruction[ZYDIS_MAX_INSTRUCTION_LENGTH]; |
265 | 3.13k | ZyanUSize encoded_length = sizeof(encoded_instruction); |
266 | 3.13k | ZyanStatus status = ZydisEncoderEncodeInstruction(&request, encoded_instruction, |
267 | 3.13k | &encoded_length); |
268 | 3.13k | if (!ZYAN_SUCCESS(status)) |
269 | 1.82k | { |
270 | 1.82k | #ifdef ZYDIS_LIBFUZZER |
271 | 1.82k | return EXIT_SUCCESS; |
272 | | #else |
273 | | return EXIT_ENCODING_FAILURE; |
274 | | #endif // ZYDIS_LIBFUZZER |
275 | 1.82k | } |
276 | | |
277 | 1.31k | ZydisStackWidth stack_width; |
278 | 1.31k | switch (request.machine_mode) |
279 | 1.31k | { |
280 | 1.03k | case ZYDIS_MACHINE_MODE_LONG_64: |
281 | 1.03k | stack_width = ZYDIS_STACK_WIDTH_64; |
282 | 1.03k | break; |
283 | 41 | case ZYDIS_MACHINE_MODE_LONG_COMPAT_32: |
284 | 81 | case ZYDIS_MACHINE_MODE_LEGACY_32: |
285 | 81 | stack_width = ZYDIS_STACK_WIDTH_32; |
286 | 81 | break; |
287 | 98 | case ZYDIS_MACHINE_MODE_LONG_COMPAT_16: |
288 | 166 | case ZYDIS_MACHINE_MODE_LEGACY_16: |
289 | 195 | case ZYDIS_MACHINE_MODE_REAL_16: |
290 | 195 | stack_width = ZYDIS_STACK_WIDTH_16; |
291 | 195 | break; |
292 | 0 | default: |
293 | 0 | ZYAN_UNREACHABLE; |
294 | 1.31k | } |
295 | | |
296 | 1.31k | ZydisDecoder decoder; |
297 | 1.31k | if (!ZYAN_SUCCESS(ZydisDecoderInit(&decoder, request.machine_mode, stack_width))) |
298 | 0 | { |
299 | 0 | fputs("Failed to initialize decoder\n", ZYAN_STDERR); |
300 | 0 | abort(); |
301 | 0 | } |
302 | 1.31k | if (requested_knc || ((request.allowed_encodings & ZYDIS_ENCODABLE_ENCODING_MVEX) != 0)) |
303 | 158 | { |
304 | 158 | status = ZydisDecoderEnableMode(&decoder, ZYDIS_DECODER_MODE_KNC, ZYAN_TRUE); |
305 | 158 | if (!ZYAN_SUCCESS(status)) |
306 | 0 | { |
307 | 0 | fputs("Failed to enable KNC mode\n", ZYAN_STDERR); |
308 | 0 | abort(); |
309 | 0 | } |
310 | 158 | } |
311 | 1.31k | if (request.mnemonic == ZYDIS_MNEMONIC_UD0 && request.operand_count == 0) |
312 | 1 | { |
313 | 1 | status = ZydisDecoderEnableMode(&decoder, ZYDIS_DECODER_MODE_UD0_COMPAT, ZYAN_TRUE); |
314 | 1 | if (!ZYAN_SUCCESS(status)) |
315 | 0 | { |
316 | 0 | fputs("Failed to enable UD0_COMPAT mode\n", ZYAN_STDERR); |
317 | 0 | abort(); |
318 | 0 | } |
319 | 1 | } |
320 | | |
321 | 1.31k | ZydisDecodedInstruction insn1; |
322 | 1.31k | ZydisDecodedOperand operands1[ZYDIS_MAX_OPERAND_COUNT]; |
323 | 1.31k | status = ZydisDecoderDecodeFull(&decoder, encoded_instruction, encoded_length, &insn1, |
324 | 1.31k | operands1); |
325 | 1.31k | if (!ZYAN_SUCCESS(status)) |
326 | 0 | { |
327 | 0 | fputs("Failed to decode instruction\n", ZYAN_STDERR); |
328 | 0 | abort(); |
329 | 0 | } |
330 | | |
331 | 1.31k | ZydisCompareRequestToInstruction(&request, &insn1, operands1); |
332 | 1.31k | ZydisReEncodeInstruction(&decoder, &insn1, operands1, insn1.operand_count, |
333 | 1.31k | encoded_instruction); |
334 | | |
335 | 1.31k | return EXIT_SUCCESS; |
336 | 1.31k | } |
337 | | |
338 | | /* ============================================================================================== */ |