| 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 |