OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2012 The Native Client Authors. All rights reserved. | 2 * Copyright (c) 2012 The Native Client Authors. All rights reserved. |
3 * Use of this source code is governed by a BSD-style license that can be | 3 * Use of this source code is governed by a BSD-style license that can be |
4 * found in the LICENSE file. | 4 * found in the LICENSE file. |
5 */ | 5 */ |
6 | 6 |
7 /* Implement the functions common for ia32 and x86-64 architectures. */ | 7 /* Implement the functions common for ia32 and x86-64 architectures. */ |
8 #include "native_client/src/trusted/validator_ragel/dfa_validate_common.h" | 8 #include "native_client/src/trusted/validator_ragel/dfa_validate_common.h" |
9 | 9 |
10 #include <string.h> | 10 #include <string.h> |
11 | 11 |
| 12 #include "native_client/src/include/build_config.h" |
12 #include "native_client/src/shared/platform/nacl_check.h" | 13 #include "native_client/src/shared/platform/nacl_check.h" |
13 #include "native_client/src/trusted/service_runtime/nacl_config.h" | 14 #include "native_client/src/trusted/service_runtime/nacl_config.h" |
14 #include "native_client/src/trusted/validator_ragel/validator.h" | 15 #include "native_client/src/trusted/validator_ragel/validator.h" |
15 | 16 |
16 /* Used as an argument to copy_func when unsupported instruction must be | 17 /* Used as an argument to copy_func when unsupported instruction must be |
17 replaced with HLTs. */ | 18 replaced with HLTs. */ |
18 static const uint8_t kStubOutMem[MAX_INSTRUCTION_LENGTH] = { | 19 static const uint8_t kStubOutMem[MAX_INSTRUCTION_LENGTH] = { |
19 NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, | 20 NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, |
20 NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, | 21 NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, |
21 NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, | 22 NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, |
22 NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, | 23 NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, |
23 NACL_HALT_OPCODE | 24 NACL_HALT_OPCODE |
24 }; | 25 }; |
25 | 26 |
26 Bool NaClDfaProcessValidationError(const uint8_t *begin, const uint8_t *end, | 27 Bool NaClDfaProcessValidationError(const uint8_t *begin, const uint8_t *end, |
27 uint32_t info, void *callback_data) { | 28 uint32_t info, void *callback_data) { |
28 UNREFERENCED_PARAMETER(begin); | 29 UNREFERENCED_PARAMETER(begin); |
29 UNREFERENCED_PARAMETER(end); | 30 UNREFERENCED_PARAMETER(end); |
30 UNREFERENCED_PARAMETER(info); | 31 UNREFERENCED_PARAMETER(info); |
31 UNREFERENCED_PARAMETER(callback_data); | 32 UNREFERENCED_PARAMETER(callback_data); |
32 | 33 |
33 return FALSE; | 34 return FALSE; |
34 } | 35 } |
35 | 36 |
36 /* | 37 /* |
37 * Returns whether a validation error should be ignored by | 38 * Returns whether a validation error should be ignored by |
38 * RewriteAndRevalidateBundle()'s two validation passes, which | 39 * RewriteAndRevalidateBundle()'s two validation passes, which |
39 * validate individual instruction bundles. | 40 * validate individual instruction bundles. |
40 */ | 41 */ |
41 static Bool AllowErrorDuringBundleValidation( | 42 static Bool AllowErrorDuringBundleValidation(uint32_t info) { |
42 uint32_t info, struct StubOutCallbackData *data) { | |
43 if ((info & VALIDATION_ERRORS_MASK) == DIRECT_JUMP_OUT_OF_RANGE) { | 43 if ((info & VALIDATION_ERRORS_MASK) == DIRECT_JUMP_OUT_OF_RANGE) { |
44 /* | 44 /* |
45 * This error can occur on valid jumps because we are validating | 45 * This error can occur on valid jumps because we are validating |
46 * an instruction bundle that is a subset of a code chunk. | 46 * an instruction bundle that is a subset of a code chunk. |
47 */ | 47 */ |
48 return TRUE; | 48 return TRUE; |
49 } | 49 } |
50 if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION) { | |
51 return (data->flags & NACL_DISABLE_NONTEMPORALS_X86) == 0; | |
52 } | |
53 return FALSE; | 50 return FALSE; |
54 } | 51 } |
55 | 52 |
| 53 #if NACL_BUILD_SUBARCH == 64 |
| 54 static Bool IsREXPrefix(uint8_t byte) { |
| 55 return byte >= 0x40 && byte <= 0x4f; |
| 56 } |
| 57 #endif |
| 58 |
| 59 static Bool RewriteNonTemporal(uint8_t *ptr, uint8_t *end, uint32_t info) { |
| 60 /* |
| 61 * Instruction rewriting. Note that we only rewrite non-temporal |
| 62 * instructions found in nexes and DSOs that are currently found in the |
| 63 * Chrome Web Store. If future nexes use other non-temporal |
| 64 * instructions, they will fail validation. |
| 65 * |
| 66 * We usually only check and rewrite the first few bytes without |
| 67 * examining further because this function is only called when the |
| 68 * validator tells us that it is an 'unsupported instruction' and there |
| 69 * are no other validation failures. |
| 70 */ |
| 71 ptrdiff_t size = end - ptr; |
| 72 #if NACL_BUILD_SUBARCH == 32 |
| 73 UNREFERENCED_PARAMETER(end); |
| 74 UNREFERENCED_PARAMETER(info); |
| 75 |
| 76 if (size >= 2 && memcmp(ptr, "\x0f\xe7", 2) == 0) { |
| 77 /* movntq => movq */ |
| 78 ptr[1] = 0x7f; |
| 79 return TRUE; |
| 80 } else if (size >= 3 && memcmp(ptr, "\x66\x0f\xe7", 3) == 0) { |
| 81 /* movntdq => movdqa */ |
| 82 ptr[2] = 0x7f; |
| 83 return TRUE; |
| 84 } |
| 85 #elif NACL_BUILD_SUBARCH == 64 |
| 86 if (size >= 3 && IsREXPrefix(ptr[0]) && ptr[1] == 0x0f) { |
| 87 uint8_t opcode_byte2 = ptr[2]; |
| 88 switch (opcode_byte2) { |
| 89 case 0x2b: |
| 90 /* movntps => movaps */ |
| 91 ptr[2] = 0x29; |
| 92 return TRUE; |
| 93 case 0xc3: |
| 94 /* movnti => mov, nop */ |
| 95 if ((info & RESTRICTED_REGISTER_USED) != 0) { |
| 96 /* |
| 97 * The rewriting for movnti is special because it changes |
| 98 * instruction boundary: movnti is replaced by a mov and a nop so |
| 99 * that the total size does not change. Therefore, special care |
| 100 * needs to be taken: if restricted register is used in this |
| 101 * instruction, we have to put nop at the end so that the |
| 102 * rewritten restricted register consuming instruction follows |
| 103 * closely with the restricted register producing instruction (if |
| 104 * there is one). |
| 105 */ |
| 106 ptr[1] = 0x89; |
| 107 memmove(ptr + 2, ptr + 3, size - 3); |
| 108 ptr[size - 1] = 0x90; /* NOP */ |
| 109 } else { |
| 110 /* |
| 111 * There are cases where we need to preserve instruction end |
| 112 * position, for example, when RIP-relative address is used. |
| 113 * Fortunately, RIP-relative addressing cannot use an index |
| 114 * register, and therefore RESTRICTED_REGISTER_USED cannot be |
| 115 * set. Therefore, no matter whether RIP-relative addressing is |
| 116 * used, as long as restricted register is not used, we are safe |
| 117 * to put nop in the beginning and preserve instruction end |
| 118 * position. |
| 119 */ |
| 120 ptr[2] = 0x89; |
| 121 ptr[1] = ptr[0]; |
| 122 ptr[0] = 0x90; /* NOP */ |
| 123 } |
| 124 return TRUE; |
| 125 case 0x18: |
| 126 /* prefetchnta => nop...nop */ |
| 127 memset(ptr, 0x90, size); |
| 128 return TRUE; |
| 129 default: |
| 130 return FALSE; |
| 131 } |
| 132 } else if (size >= 4 && |
| 133 ptr[0] == 0x66 && |
| 134 IsREXPrefix(ptr[1]) && |
| 135 memcmp(ptr + 2, "\x0f\xe7", 2) == 0) { |
| 136 /* movntdq => movdqa */ |
| 137 ptr[3] = 0x7f; |
| 138 return TRUE; |
| 139 } |
| 140 #else |
| 141 # error "Unknown architecture" |
| 142 #endif |
| 143 return FALSE; |
| 144 } |
| 145 |
56 /* | 146 /* |
57 * First pass of RewriteAndRevalidateBundle(): Rewrite any | 147 * First pass of RewriteAndRevalidateBundle(): Rewrite any |
58 * instructions that need rewriting. | 148 * instructions that need rewriting. |
59 */ | 149 */ |
60 static Bool BundleValidationApplyRewrite(const uint8_t *begin, | 150 static Bool BundleValidationApplyRewrite(const uint8_t *begin, |
61 const uint8_t *end, | 151 const uint8_t *end, |
62 uint32_t info, | 152 uint32_t info, |
63 void *callback_data) { | 153 void *callback_data) { |
64 struct StubOutCallbackData *data = callback_data; | 154 struct StubOutCallbackData *data = callback_data; |
65 | 155 |
66 if ((info & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION) { | 156 if ((info & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION) { |
67 /* Stub-out instructions unsupported on this CPU, but valid on other CPUs.*/ | 157 /* Stub-out instructions unsupported on this CPU, but valid on other CPUs.*/ |
68 data->did_rewrite = 1; | 158 data->did_rewrite = 1; |
69 memset((uint8_t *) begin, NACL_HALT_OPCODE, end - begin); | 159 memset((uint8_t *) begin, NACL_HALT_OPCODE, end - begin); |
70 return TRUE; | 160 return TRUE; |
71 } | 161 } |
72 return AllowErrorDuringBundleValidation(info, data); | 162 if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION && |
| 163 (data->flags & NACL_DISABLE_NONTEMPORALS_X86) == 0) { |
| 164 if (RewriteNonTemporal((uint8_t *) begin, (uint8_t *) end, info)) { |
| 165 data->did_rewrite = 1; |
| 166 return TRUE; |
| 167 } |
| 168 return FALSE; |
| 169 } |
| 170 return AllowErrorDuringBundleValidation(info); |
73 } | 171 } |
74 | 172 |
75 /* | 173 /* |
76 * Second pass of RewriteAndRevalidateBundle(): Revalidate, checking | 174 * Second pass of RewriteAndRevalidateBundle(): Revalidate, checking |
77 * that no further instruction rewrites are needed. | 175 * that no further instruction rewrites are needed. |
78 */ | 176 */ |
79 static Bool BundleValidationCheckAfterRewrite(const uint8_t *begin, | 177 static Bool BundleValidationCheckAfterRewrite(const uint8_t *begin, |
80 const uint8_t *end, | 178 const uint8_t *end, |
81 uint32_t info, | 179 uint32_t info, |
82 void *callback_data) { | 180 void *callback_data) { |
83 struct StubOutCallbackData *data = callback_data; | |
84 UNREFERENCED_PARAMETER(begin); | 181 UNREFERENCED_PARAMETER(begin); |
85 UNREFERENCED_PARAMETER(end); | 182 UNREFERENCED_PARAMETER(end); |
| 183 UNREFERENCED_PARAMETER(callback_data); |
86 | 184 |
87 return AllowErrorDuringBundleValidation(info, data); | 185 return AllowErrorDuringBundleValidation(info); |
88 } | 186 } |
89 | 187 |
90 /* | 188 /* |
91 * As an extra safety check, when the validator modifies an | 189 * As an extra safety check, when the validator modifies an |
92 * instruction, we want to revalidate the rewritten code. | 190 * instruction, we want to revalidate the rewritten code. |
93 * | 191 * |
94 * For performance, we don't want to revalidate the whole code chunk, | 192 * For performance, we don't want to revalidate the whole code chunk, |
95 * because that would double the overall validation time. However, | 193 * because that would double the overall validation time. However, |
96 * it's not practical to revalidate the individual rewritten | 194 * it's not practical to revalidate the individual rewritten |
97 * instruction, because for x86-64 we need to account for previous | 195 * instruction, because for x86-64 we need to account for previous |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
136 } | 234 } |
137 | 235 |
138 Bool NaClDfaStubOutUnsupportedInstruction(const uint8_t *begin, | 236 Bool NaClDfaStubOutUnsupportedInstruction(const uint8_t *begin, |
139 const uint8_t *end, | 237 const uint8_t *end, |
140 uint32_t info, | 238 uint32_t info, |
141 void *callback_data) { | 239 void *callback_data) { |
142 struct StubOutCallbackData *data = callback_data; | 240 struct StubOutCallbackData *data = callback_data; |
143 /* Stub-out instructions unsupported on this CPU, but valid on other CPUs. */ | 241 /* Stub-out instructions unsupported on this CPU, but valid on other CPUs. */ |
144 if ((info & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION) { | 242 if ((info & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION) { |
145 return RewriteAndRevalidateBundle(begin, end, data); | 243 return RewriteAndRevalidateBundle(begin, end, data); |
146 } else if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION) { | |
147 if (data->flags & NACL_DISABLE_NONTEMPORALS_X86) { | |
148 return FALSE; | |
149 } else { | |
150 /* TODO(ruiq): rewrite instruction. For now, we keep the original | |
151 * instruction and indicate validation success, which is consistent | |
152 * with current validation results. */ | |
153 return TRUE; | |
154 } | |
155 } else { | |
156 return FALSE; | |
157 } | 244 } |
| 245 if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION && |
| 246 (data->flags & NACL_DISABLE_NONTEMPORALS_X86) == 0) { |
| 247 return RewriteAndRevalidateBundle(begin, end, data); |
| 248 } |
| 249 return FALSE; |
158 } | 250 } |
159 | 251 |
160 Bool NaClDfaProcessCodeCopyInstruction(const uint8_t *begin_new, | 252 Bool NaClDfaProcessCodeCopyInstruction(const uint8_t *begin_new, |
161 const uint8_t *end_new, | 253 const uint8_t *end_new, |
162 uint32_t info_new, | 254 uint32_t info_new, |
163 void *callback_data) { | 255 void *callback_data) { |
164 struct CodeCopyCallbackData *data = callback_data; | 256 struct CodeCopyCallbackData *data = callback_data; |
165 size_t instruction_length = end_new - begin_new; | 257 size_t instruction_length = end_new - begin_new; |
166 | 258 |
167 /* Sanity check: instruction must be no longer than 17 bytes. */ | 259 /* Sanity check: instruction must be no longer than 17 bytes. */ |
168 CHECK(instruction_length <= MAX_INSTRUCTION_LENGTH); | 260 CHECK(instruction_length <= MAX_INSTRUCTION_LENGTH); |
169 | 261 |
170 return data->copy_func( | 262 return data->copy_func( |
171 (uint8_t *)begin_new + data->existing_minus_new, /* begin_existing */ | 263 (uint8_t *)begin_new + data->existing_minus_new, /* begin_existing */ |
172 (info_new & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION ? | 264 (info_new & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION ? |
173 (uint8_t *)kStubOutMem : | 265 (uint8_t *)kStubOutMem : |
174 (uint8_t *)begin_new, | 266 (uint8_t *)begin_new, |
175 (uint8_t)instruction_length); | 267 (uint8_t)instruction_length); |
176 } | 268 } |
177 | 269 |
178 Bool NaClDfaCodeReplacementIsStubouted(const uint8_t *begin_existing, | 270 Bool NaClDfaCodeReplacementIsStubouted(const uint8_t *begin_existing, |
179 size_t instruction_length) { | 271 size_t instruction_length) { |
180 | 272 |
181 /* Unsupported instruction must have been replaced with HLTs. */ | 273 /* Unsupported instruction must have been replaced with HLTs. */ |
182 if (memcmp(kStubOutMem, begin_existing, instruction_length) == 0) | 274 if (memcmp(kStubOutMem, begin_existing, instruction_length) == 0) |
183 return TRUE; | 275 return TRUE; |
184 else | 276 else |
185 return FALSE; | 277 return FALSE; |
186 } | 278 } |
OLD | NEW |