OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 #include <assert.h> | |
8 #include <elf.h> | |
9 #include <inttypes.h> | |
10 #include <stddef.h> | |
11 #include <stdio.h> | |
12 #include <stdlib.h> | |
13 #include <string.h> | |
14 #include "validator.h" | |
15 | |
16 #undef TRUE | |
17 #define TRUE 1 | |
18 | |
19 #undef FALSE | |
20 #define FALSE 0 | |
21 | |
22 #include "validator-x86_64-instruction-consts.c" | |
23 | |
24 #define check_jump_dest \ | |
25 if ((jump_dest & bundle_mask) != bundle_mask) { \ | |
26 if (jump_dest >= size) { \ | |
27 printf("direct jump out of range: %zx\n", jump_dest); \ | |
28 result = 1; \ | |
29 goto error_detected; \ | |
30 } else { \ | |
31 BitmapSetBit(jump_dests, jump_dest + 1); \ | |
32 } \ | |
33 } \ | |
34 operand0 = JMP_TO; \ | |
35 base = REG_RIP; \ | |
36 index = REG_NONE; | |
37 | |
38 %%{ | |
39 machine x86_64_decoder; | |
40 alphtype unsigned char; | |
41 | |
42 action check_access { | |
43 if ((base == REG_RIP) || (base == REG_R15) || | |
44 ((base == REG_RSP) && (restricted_register != REG_RSP)) || | |
45 ((base == REG_RBP) && (restricted_register != REG_RBP))) { | |
46 if ((index == restricted_register) || | |
47 ((index == REG_RDI) && | |
48 (restricted_register == kSandboxedRsiRestrictedRdi))) { | |
49 BitmapClearBit(valid_targets, begin - data); | |
50 } else if ((index != REG_NONE) && (index != REG_RIZ)) { | |
51 fprintf(stderr,"Improper sandboxing in instruction %zx", begin - data); | |
52 exit(1); | |
53 } | |
54 } else if ((index == REG_RIP) || (index == REG_R15) || | |
55 ((index == REG_RSP) && (restricted_register != REG_RSP)) || | |
56 ((index == REG_RBP) && (restricted_register != REG_RBP))) { | |
57 if ((base == restricted_register) || | |
58 ((base == REG_RDI) && | |
59 (restricted_register == kSandboxedRsiRestrictedRdi))) { | |
60 BitmapClearBit(valid_targets, begin - data); | |
61 } else if ((base != REG_NONE) && (base != REG_RIZ)) { | |
62 fprintf(stderr,"Improper sandboxing in instruction @%zx", begin - data); | |
63 exit(1); | |
64 } | |
65 } else { | |
66 fprintf(stderr,"Improper sandboxing in instruction @%zx", begin - data); | |
67 } | |
68 } | |
69 | |
70 action rel8_operand { | |
71 int8_t offset = (uint8_t) (p[0]); | |
72 size_t jump_dest = offset + (p - data); | |
73 check_jump_dest; | |
74 } | |
75 action rel16_operand { | |
76 assert(FALSE); | |
77 } | |
78 action rel32_operand { | |
79 int32_t offset = | |
80 (uint32_t) (p[-3] + 256U * (p[-2] + 256U * (p[-1] + 256U * (p[0])))); | |
81 size_t jump_dest = offset + (p - data); | |
82 check_jump_dest; | |
83 } | |
84 | |
85 include decode_x86_64 "validator-x86_64-instruction.rl"; | |
86 | |
87 action process_normal_instruction { | |
88 /* Restricted %rsp or %rbp must be processed by appropriate nacl-special | |
89 instruction, not with regular instruction. */ | |
90 if (restricted_register == REG_RSP) { | |
91 printf("Incorrectly modified register %%rsp at the %zx\n", p - data); | |
92 exit(1); | |
93 } else if (restricted_register == REG_RBP) { | |
94 printf("Incorrectly modified register %%rbp at the %zx\n", p - data); | |
95 exit(1); | |
96 } | |
97 /* If Sandboxed Rsi is destroyed then we must note that. */ | |
98 if (restricted_register == kSandboxedRsi) { | |
99 for (i = 0; i < operands_count; ++i) { | |
100 if ((operands[i].write == TRUE) && | |
101 (operands[i].name == REG_RSI) && | |
102 ((operands[i].type == OperandSandboxRestricted) || | |
103 (operands[i].type == OperandSandboxUnrestricted))) { | |
104 restricted_register = kNoRestrictedReg; | |
105 break; | |
106 } | |
107 } | |
108 } | |
109 if (restricted_register == kSandboxedRsi) { | |
110 for (i = 0; i < operands_count; ++i) { | |
111 if ((operands[i].write == TRUE) && | |
112 (operands[i].name == REG_RDI) && | |
113 (operands[i].type == OperandSandboxRestricted)) { | |
114 sandboxed_rsi_restricted_rdi = begin; | |
115 restricted_register = kSandboxedRsiRestrictedRdi; | |
116 } | |
117 } | |
118 } | |
119 if (restricted_register != kSandboxedRsiRestrictedRdi) { | |
120 restricted_register = kNoRestrictedReg; | |
121 for (i = 0; i < operands_count; ++i) { | |
122 if (operands[i].write && operands[i].name <= REG_R15) { | |
123 if (operands[i].type == OperandSandboxRestricted) { | |
124 if (operands[i].name == REG_R15) { | |
125 printf("Incorrectly modified register %%r15 at the %zx\n", | |
126 p - data); | |
127 exit(1); | |
128 } else { | |
129 restricted_register = operands[i].name; | |
130 } | |
131 } else if (operands[i].type == OperandSandboxUnrestricted) { | |
132 if (operands[i].name == REG_RBP) { | |
133 printf("Incorrectly modified register %%rbp at the %zx\n", | |
134 p - data); | |
135 exit(1); | |
136 } else if (operands[i].name == REG_RSP) { | |
137 printf("Incorrectly modified register %%rsp at the %zx\n", | |
138 p - data); | |
139 exit(1); | |
140 } else if (operands[i].name == REG_R15) { | |
141 printf("Incorrectly modified register %%r15 at the %zx\n", | |
142 p - data); | |
143 exit(1); | |
144 } | |
145 } else if (operands[i].type == OperandNoSandboxEffect) { | |
146 if (operands[i].name == REG_R15) { | |
147 printf("Incorrectly modified register %%r15 at the %zx\n", | |
148 p - data); | |
149 exit(1); | |
150 } | |
151 } | |
152 } | |
153 } | |
154 } | |
155 } | |
156 | |
157 # Remove special instructions which are only allowed in special cases. | |
158 normal_instruction = (one_instruction - ( | |
159 (0x48 0x89 0xe5) | # mov %rsp,%rbp | |
160 (0x48 0x89 0xec) | # mov %rbp,%rsp | |
161 (0x48 0x81 0xe4 any{4}) | # and $XXX,%rsp | |
162 (0x48 0x83 0xe4 any) | # and $XXX,%rsp | |
163 (0x4c 0x01 0xfd) | # add %r15,%rbp | |
164 (0x49 0x8d 0x2c 0x2f) | # lea (%r15,%rbp,1),%rbp | |
165 (0x4a 0x8d 0x6c 0x3d any) | # lea 0x0(%rbp,%r15,1),%rbp | |
166 (0x48 0x81 0xe5 any{4}) | # and $XXX,%rsp | |
167 (0x48 0x83 0xe5 any) | # and $XXX,%rsp | |
168 (0x4c 0x01 0xfc) | # add %r15,%rsp | |
169 (0x4a 0x8d 0x24 0x3c) | # lea (%rsp,%r15,1),%rsp | |
170 (0x49 0x8d 0x34 0x37) | # lea (%r15,%rsi,1),%rsi | |
171 (0x49 0x8d 0x3c 0x3f) # lea (%r15,%rdi,1),%rdi | |
172 )) @process_normal_instruction; | |
173 | |
174 special_instruction = | |
175 (0x48 0x89 0xe5) | # mov %rsp,%rbp | |
176 (0x48 0x81 0xe4 any{3} (0x80 .. 0xff)) | # and $XXX,%rsp | |
177 (0x48 0x83 0xe4 (0x80 .. 0xff)) # and $XXX,%rsp | |
178 @{ if (restricted_register == REG_RSP) { | |
179 printf("Incorrectly modified register %%rsp at the %zx\n", p - data); | |
180 exit(1); | |
181 } | |
182 restricted_register = kNoRestrictedReg; | |
183 } | | |
184 (0x48 0x89 0xec) | # mov %rbp,%rsp | |
185 (0x48 0x81 0xe5 any{3} (0x80 .. 0xff)) | # and $XXX,%rsp | |
186 (0x48 0x83 0xe5 (0x80 .. 0xff)) # and $XXX,%rsp | |
187 @{ if (restricted_register == REG_RBP) { | |
188 printf("Incorrectly modified register %%rbp at the %zx\n", p - data); | |
189 exit(1); | |
190 } | |
191 restricted_register = kNoRestrictedReg; | |
192 } | | |
193 (0x4c 0x01 0xfd | # add %r15,%rbp | |
194 0x49 0x8d 0x2c 0x2f | # lea (%r15,%rbp,1),%rbp | |
195 0x4a 0x8d 0x6c 0x3d 0x00) # lea 0x0(%rbp,%r15,1),%rbp | |
196 @{ if (restricted_register != REG_RBP) { | |
197 printf("Incorrectly sandboxed %%rbp at the %zx\n", p - data); | |
198 exit(1); | |
199 } | |
200 restricted_register = kNoRestrictedReg; | |
201 BitmapClearBit(valid_targets, (begin - data)); | |
202 } | | |
203 (0x4c 0x01 0xfc | # add %r15,%rsp | |
204 0x4a 0x8d 0x24 0x3c) # lea (%rsp,%r15,1),%rsp | |
205 @{ if (restricted_register != REG_RSP) { | |
206 printf("Incorrectly sandboxed %%rsp at the %zx\n", p - data); | |
207 exit(1); | |
208 } | |
209 restricted_register = kNoRestrictedReg; | |
210 BitmapClearBit(valid_targets, (begin - data)); | |
211 } | | |
212 (0x83 0xe0 0xe0 0x4c 0x01 0xf8 0xff (0xd0|0xe0) | # naclcall/jmp %eax, %r15 | |
213 0x83 0xe1 0xe0 0x4c 0x01 0xf9 0xff (0xd1|0xe1) | # naclcall/jmp %ecx, %r15 | |
214 0x83 0xe2 0xe0 0x4c 0x01 0xfa 0xff (0xd2|0xe2) | # naclcall/jmp %edx, %r15 | |
215 0x83 0xe3 0xe0 0x4c 0x01 0xfb 0xff (0xd3|0xe3) | # naclcall/jmp %ebx, %r15 | |
216 0x83 0xe4 0xe0 0x4c 0x01 0xfc 0xff (0xd4|0xe4) | # naclcall/jmp %esp, %r15 | |
217 0x83 0xe5 0xe0 0x4c 0x01 0xfd 0xff (0xd5|0xe5) | # naclcall/jmp %ebp, %r15 | |
218 0x83 0xe6 0xe0 0x4c 0x01 0xfe 0xff (0xd6|0xe6) | # naclcall/jmp %esi, %r15 | |
219 0x83 0xe7 0xe0 0x4c 0x01 0xff 0xff (0xd7|0xe7)) # naclcall/jmp %edi, %r15 | |
220 @{ if (restricted_register == REG_RSP) { | |
221 printf("Incorrectly modified register %%rsp at the %zx\n", p - data); | |
222 exit(1); | |
223 } else if (restricted_register == REG_RBP) { | |
224 printf("Incorrectly modified register %%rbp at the %zx\n", p - data); | |
225 exit(1); | |
226 } | |
227 BitmapClearBit(valid_targets, (p - data) - 4); | |
228 BitmapClearBit(valid_targets, (p - data) - 1); | |
229 restricted_register = kNoRestrictedReg; | |
230 } | | |
231 (0x41 0x83 0xe0 0xe0 0x4d 0x01 0xf8 0x41 0xff (0xd0|0xe0) | # naclcall/jmp | |
232 0x41 0x83 0xe1 0xe0 0x4d 0x01 0xf9 0x41 0xff (0xd1|0xe1) | # %r8d, %r15 | |
233 0x41 0x83 0xe2 0xe0 0x4d 0x01 0xfa 0x41 0xff (0xd2|0xe2) | # ⋮ | |
234 0x41 0x83 0xe3 0xe0 0x4d 0x01 0xfb 0x41 0xff (0xd3|0xe3) | # ⋮ | |
235 0x41 0x83 0xe4 0xe0 0x4d 0x01 0xfc 0x41 0xff (0xd4|0xe4) | # ⋮ | |
236 0x41 0x83 0xe5 0xe0 0x4d 0x01 0xfd 0x41 0xff (0xd5|0xe5) | # naclcall/jmp | |
237 0x41 0x83 0xe6 0xe0 0x4d 0x01 0xfe 0x41 0xff (0xd6|0xe6)) # %r14d, %r15 | |
238 @{ if (restricted_register == REG_RSP) { | |
239 printf("Incorrectly modified register %%rsp at the %zx\n", p - data); | |
240 exit(1); | |
241 } else if (restricted_register == REG_RBP) { | |
242 printf("Incorrectly modified register %%rbp at the %zx\n", p - data); | |
243 exit(1); | |
244 } | |
245 BitmapClearBit(valid_targets, (p - data) - 5); | |
246 BitmapClearBit(valid_targets, (p - data) - 2); | |
247 restricted_register = kNoRestrictedReg; | |
248 } | | |
249 (0x49 0x8d 0x34 0x37) # lea (%r15,%rsi,1),%rsi | |
250 @{ if (restricted_register == REG_RSI) { | |
251 sandboxed_rsi = begin; | |
252 restricted_register = kSandboxedRsi; | |
253 } else { | |
254 restricted_register = kNoRestrictedReg; | |
255 } | |
256 } | | |
257 (0x49 0x8d 0x3c 0x3f) # lea (%r15,%rdi,1),%rdi | |
258 @{ if (restricted_register == REG_RDI) { | |
259 sandboxed_rdi = begin; | |
260 restricted_register = kSandboxedRdi; | |
261 } else if (restricted_register == kSandboxedRsiRestrictedRdi) { | |
262 sandboxed_rdi = begin; | |
263 restricted_register = kSandboxedRsiSandboxedRdi; | |
264 } else { | |
265 restricted_register = kNoRestrictedReg; | |
266 } | |
267 } | | |
268 (0xac | # lods %ds:(%rsi),%al | |
269 (data16|REXW_NONE)? 0xad) # lods %ds:(%rsi),%ax/%eax/%rax | |
270 @{ if (restricted_register != kSandboxedRsi) { | |
271 printf("Incorrectly sandboxed %%rdi at the %zx\n", p - data); | |
272 exit(1); | |
273 } | |
274 restricted_register = kNoRestrictedReg; | |
275 BitmapClearBit(valid_targets, (begin - data)); | |
276 BitmapClearBit(valid_targets, (sandboxed_rdi - data)); | |
277 } | | |
278 (0xae | # scas %ds:(%rsi),%al | |
279 (data16|REXW_NONE)? 0xaf | # scas %ds:(%rsi),%ax/%eax/%rax | |
280 rep? 0xaa | # stos %al,%es:(%rdi) | |
281 (data16 | | |
282 rep data16 | | |
283 data16 rep) 0xab | # stos %ax,%es:(%rdi) | |
284 rep? REXW_NONE? 0xab) # stos %eax/%rax,%es:(%rdi) | |
285 @{ if (restricted_register != kSandboxedRdi && | |
286 restricted_register != kSandboxedRsiSandboxedRdi) { | |
287 printf("Incorrectly sandboxed %%rdi at the %zx\n", p - data); | |
288 exit(1); | |
289 } | |
290 restricted_register = kNoRestrictedReg; | |
291 BitmapClearBit(valid_targets, (begin - data)); | |
292 BitmapClearBit(valid_targets, (sandboxed_rdi - data)); | |
293 } | | |
294 (condrep? 0xa6 | # cmpsb %es:(%rdi),%ds:(%rsi) | |
295 (data16 | | |
296 condrep data16 | | |
297 data16 condrep) 0xa7 | # cmpsw %es:(%rdi),%ds:(%rsi) | |
298 condrep? REXW_NONE? 0xa7 | # cmps[lq] %es:(%rdi),%ds:(%rsi) | |
299 rep? 0xa4 | # movsb %es:(%rdi),%ds:(%rsi) | |
300 (data16 | | |
301 rep data16 | | |
302 data16 rep) 0xa5 | # movsw %es:(%rdi),%ds:(%rsi) | |
303 rep? REXW_NONE? 0xa5) # movs[lq] %es:(%rdi),%ds:(%rsi) | |
304 @{ if (restricted_register != kSandboxedRsiSandboxedRdi) { | |
305 printf("Incorrectly sandboxed %%rsi or %%rdi at the %zx\n", p - data); | |
306 exit(1); | |
307 } | |
308 restricted_register = kNoRestrictedReg; | |
309 BitmapClearBit(valid_targets, (begin - data)); | |
310 BitmapClearBit(valid_targets, (sandboxed_rsi - data)); | |
311 BitmapClearBit(valid_targets, (sandboxed_rsi_restricted_rdi - data)); | |
312 BitmapClearBit(valid_targets, (sandboxed_rdi - data)); | |
313 }; | |
314 | |
315 main := ((normal_instruction | special_instruction) >{ | |
316 begin = p; | |
317 BitmapSetBit(valid_targets, p - data); | |
318 rex_prefix = FALSE; | |
319 vex_prefix2 = 0xe0; | |
320 vex_prefix3 = 0x00; | |
321 })* | |
322 $!{ process_error(p, userdata); | |
323 result = 1; | |
324 goto error_detected; | |
325 }; | |
326 | |
327 }%% | |
328 | |
329 %% write data; | |
330 | |
331 /* Ignore this information for now. */ | |
332 #define data16_prefix if (0) result | |
333 #define lock_prefix if (0) result | |
334 #define repz_prefix if (0) result | |
335 #define repnz_prefix if (0) result | |
336 #define branch_not_taken if (0) result | |
337 #define branch_taken if (0) result | |
338 #define imm if (0) begin | |
339 #define imm_operand if (0) result | |
340 #define imm2 if (0) begin | |
341 #define imm2_operand if (0) result | |
342 #define scale if (0) result | |
343 #define disp if (0) begin | |
344 #define disp_type if (0) result | |
345 #define operand0 operands[0].name | |
346 #define operand1 operands[1].name | |
347 #define operand2 operands[2].name | |
348 #define operand3 operands[3].name | |
349 #define operand4 operands[4].name | |
350 #define operand0_type operands[0].type | |
351 #define operand1_type operands[1].type | |
352 #define operand2_type operands[2].type | |
353 #define operand3_type operands[3].type | |
354 #define operand4_type operands[4].type | |
355 /* It's not important for us. */ | |
356 #define operand0_read if (0) result | |
357 #define operand1_read if (0) result | |
358 #define operand2_read if (0) result | |
359 #define operand3_read if (0) result | |
360 #define operand4_read if (0) result | |
361 #define operand0_write operands[0].write | |
362 #define operand1_write operands[1].write | |
363 #define operand2_write operands[2].write | |
364 #define operand3_write operands[3].write | |
365 #define operand4_write operands[4].write | |
366 | |
367 enum { | |
368 REX_B = 1, | |
369 REX_X = 2, | |
370 REX_R = 4, | |
371 REX_W = 8 | |
372 }; | |
373 | |
374 enum disp_mode { | |
375 DISPNONE, | |
376 DISP8, | |
377 DISP16, | |
378 DISP32, | |
379 DISP64, | |
380 }; | |
381 | |
382 enum imm_mode { | |
383 IMMNONE, | |
384 IMM2, | |
385 IMM8, | |
386 IMM16, | |
387 IMM32, | |
388 IMM64 | |
389 }; | |
390 | |
391 static const int kBitsPerByte = 8; | |
392 | |
393 static inline uint8_t *BitmapAllocate(uint32_t indexes) { | |
394 uint32_t byte_count = (indexes + kBitsPerByte - 1) / kBitsPerByte; | |
395 uint8_t *bitmap = malloc(byte_count); | |
396 if (bitmap != NULL) { | |
397 memset(bitmap, 0, byte_count); | |
398 } | |
399 return bitmap; | |
400 } | |
401 | |
402 static inline int BitmapIsBitSet(uint8_t *bitmap, uint32_t index) { | |
403 return (bitmap[index / kBitsPerByte] & (1 << (index % kBitsPerByte))) != 0; | |
404 } | |
405 | |
406 static inline void BitmapSetBit(uint8_t *bitmap, uint32_t index) { | |
407 bitmap[index / kBitsPerByte] |= 1 << (index % kBitsPerByte); | |
408 } | |
409 | |
410 static inline void BitmapClearBit(uint8_t *bitmap, uint32_t index) { | |
411 bitmap[index / kBitsPerByte] &= ~(1 << (index % kBitsPerByte)); | |
412 } | |
413 | |
414 static int CheckJumpTargets(uint8_t *valid_targets, uint8_t *jump_dests, | |
415 size_t size) { | |
416 size_t i; | |
417 for (i = 0; i < size / 32; i++) { | |
418 uint32_t jump_dest_mask = ((uint32_t *) jump_dests)[i]; | |
419 uint32_t valid_target_mask = ((uint32_t *) valid_targets)[i]; | |
420 if ((jump_dest_mask & ~valid_target_mask) != 0) { | |
421 printf("bad jump to around %x\n", (unsigned)(i * 32)); | |
422 return 1; | |
423 } | |
424 } | |
425 return 0; | |
426 } | |
427 | |
428 int ValidateChunkAMD64(const uint8_t *data, size_t size, | |
429 process_error_func process_error, void *userdata) { | |
430 const size_t bundle_size = 32; | |
431 const size_t bundle_mask = bundle_size - 1; | |
432 | |
433 uint8_t *valid_targets = BitmapAllocate(size); | |
434 uint8_t *jump_dests = BitmapAllocate(size); | |
435 | |
436 const uint8_t *p = data; | |
437 const uint8_t *begin; | |
438 | |
439 uint8_t rex_prefix, vex_prefix2, vex_prefix3; | |
440 struct Operand { | |
441 unsigned int name :5; | |
442 unsigned int type :2; | |
443 bool write :1; | |
444 } operands[5]; | |
445 enum register_name base, index; | |
446 uint8_t operands_count, i; | |
447 int result = 0; | |
448 /* | |
449 * These are borders of the appropriate instructions. Initialize them to make | |
450 * compiler happy: they are never used uninitialized even without explicit | |
451 * initialization but GCC is not sophysicated enough to prove that. | |
452 */ | |
453 const uint8_t *sandboxed_rsi = 0; | |
454 const uint8_t *sandboxed_rsi_restricted_rdi = 0; | |
455 const uint8_t *sandboxed_rdi = 0; | |
456 | |
457 assert(size % bundle_size == 0); | |
458 | |
459 while (p < data + size) { | |
460 const uint8_t *pe = p + bundle_size; | |
461 const uint8_t *eof = pe; | |
462 int cs; | |
463 enum { | |
464 kNoRestrictedReg = 32, | |
465 kSandboxedRsi, | |
466 kSandboxedRdi, | |
467 kSandboxedRsiRestrictedRdi, | |
468 kSandboxedRsiSandboxedRdi | |
469 }; uint8_t restricted_register = kNoRestrictedReg; | |
470 | |
471 %% write init; | |
472 %% write exec; | |
473 | |
474 if (restricted_register == REG_RBP) { | |
475 printf("Incorrectly sandboxed %%rbp at the %d%zx\n", *data, p - data); | |
476 exit(1); | |
477 } else if (restricted_register == REG_RSP) { | |
478 printf("Incorrectly sandboxed %%rbp at the %zx\n", p - data); | |
479 exit(1); | |
480 } | |
481 } | |
482 | |
483 if (CheckJumpTargets(valid_targets, jump_dests, size)) { | |
484 result = 1; | |
485 goto error_detected; | |
486 } | |
487 | |
488 error_detected: | |
489 return result; | |
490 } | |
OLD | NEW |