Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: src/trusted/validator_ragel/dfa_validate_common.c

Issue 1269113003: Rewrite non-temporal instructions Base URL: https://chromium.googlesource.com/native_client/src/native_client.git@master
Patch Set: Format Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/trusted/validator_ragel/dfa_validate_common.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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/shared/platform/nacl_check.h" 12 #include "native_client/src/shared/platform/nacl_check.h"
13 #include "native_client/src/trusted/service_runtime/nacl_config.h" 13 #include "native_client/src/trusted/service_runtime/nacl_config.h"
14 #include "native_client/src/trusted/validator_ragel/validator.h" 14 #include "native_client/src/include/build_config.h"
15 15
16 /* Used as an argument to copy_func when unsupported instruction must be 16 /* Used as an argument to copy_func when unsupported instruction must be
17 replaced with HLTs. */ 17 replaced with HLTs. */
18 static const uint8_t kStubOutMem[MAX_INSTRUCTION_LENGTH] = { 18 static const uint8_t kStubOutMem[MAX_INSTRUCTION_LENGTH] = {
19 NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, NACL_HALT_OPCODE, 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 23 NACL_HALT_OPCODE
24 }; 24 };
25 25
26 Bool NaClDfaProcessValidationError(const uint8_t *begin, const uint8_t *end, 26 Bool NaClDfaProcessValidationError(const uint8_t *begin, const uint8_t *end,
27 uint32_t info, void *callback_data) { 27 uint32_t info, void *callback_data) {
28 UNREFERENCED_PARAMETER(begin); 28 UNREFERENCED_PARAMETER(begin);
29 UNREFERENCED_PARAMETER(end); 29 UNREFERENCED_PARAMETER(end);
30 UNREFERENCED_PARAMETER(info); 30 UNREFERENCED_PARAMETER(info);
31 UNREFERENCED_PARAMETER(callback_data); 31 UNREFERENCED_PARAMETER(callback_data);
32 32
33 return FALSE; 33 return FALSE;
34 } 34 }
35 35
36 Bool NaClDfaProcessPostRewriteValidationError(const uint8_t *begin,
37 const uint8_t *end,
38 uint32_t info,
39 void *callback_data) {
40 UNREFERENCED_PARAMETER(begin);
41 UNREFERENCED_PARAMETER(end);
42 UNREFERENCED_PARAMETER(callback_data);
43 /*
44 * Similar to NaClDfaRewriteUnsupportedInstruction(), we don't consider
45 * DIRECT_JUMP_OUT_OF_RANGE as an error. But if we still get
46 * CPUID_UNSUPPORTED_INSTRUCTION or UNSUPPORTED_INSTRUCTION, the validation
47 * should fail, because these errors should have already been fixed
48 * by NaClDfaRewriteUnsupportedInstruction().
49 */
50 return (info & VALIDATION_ERRORS_MASK) == DIRECT_JUMP_OUT_OF_RANGE;
51 }
52
53 #if NACL_BUILD_SUBARCH == 64
54 static Bool IsREX(uint8_t byte) {
55 return byte >= 0x40 && byte <= 0x4f;
56 }
57 #endif
58
59 static Bool NaClDfaRewriteUnsupportedInstruction(const uint8_t *begin,
60 const uint8_t *end,
61 uint32_t info,
62 void *callback_data) {
63 uint8_t *ptr = (uint8_t *) begin;
64 struct StubOutCallbackData *data = callback_data;
65 /*
66 * Clear DIRECT_JUMP_OUT_OF_RANGE error, because it may be introduced by
67 * validating a bundle which is smaller than the original chunk size. Even if
68 * the orignal chunk has this error, it can be detected when validating the
69 * whole chunk.
70 */
71 info &= ~DIRECT_JUMP_OUT_OF_RANGE;
72 if ((info & VALIDATION_ERRORS_MASK) == 0) {
73 return TRUE;
74 } else if ((info & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION) {
75 /* Stub-out instructions unsupported on this CPU, but valid on other CPUs.*/
76 data->did_rewrite = 1;
77 memset((uint8_t *)begin, NACL_HALT_OPCODE, end - begin);
78 return TRUE;
79 } else if ((info & VALIDATION_ERRORS_MASK) != UNSUPPORTED_INSTRUCTION) {
80 return FALSE;
81 }
82 /*
83 * Instruction rewriting. Note that we only rewrite non-temporal instructions
84 * found in current webstore nexes so that validation succeeds and we don't
85 * break them. If future nexes use other non-temporal instructions, they will
86 * fail validation.
87 *
88 * We usually only check and rewrite the first few bytes without examining
89 * further because this function is only called when the validator tells us
90 * that it is an 'unsupported instruction' and there are no other validation
91 * failures.
92 */
93 #if NACL_BUILD_SUBARCH == 32
94 UNREFERENCED_PARAMETER(end);
95 if (memcmp(ptr, "\x0f\xe7", 2) == 0) {
96 /* movntq => movq */
97 ptr[1] = 0x7f;
98 data->did_rewrite = 1;
99 return TRUE;
100 } else if (memcmp(ptr, "\x66\x0f\xe7", 3) == 0) {
101 /* movntdq => movdqa */
102 ptr[2] = 0x7f;
103 data->did_rewrite = 1;
104 return TRUE;
105 }
106 #elif NACL_BUILD_SUBARCH == 64
107 if (IsREX(ptr[0]) && ptr[1] == 0x0f) {
108 uint8_t opcode_byte2 = ptr[2];
109 switch (opcode_byte2) {
110 case 0x2b:
111 /* movntps => movaps */
112 ptr[2] = 0x29;
113 data->did_rewrite = 1;
114 return TRUE;
115 case 0xc3:
116 /* movnti => mov, nop */
117 if (info & RESTRICTED_REGISTER_USED) {
118 /*
119 * The rewriting for movnti is special because it changes instruction
120 * boundary: movnti is replaced by a mov and a nop so that the total
121 * size does not change. Therefore, special care needs to be taken:
122 * if restricted register is used in this instruction, we have to put
123 * nop at the end so that the rewritten restricted register consuming
124 * instruction follows closely with the restricted register producing
125 * instruction (if there is one).
126 */
127 ptr[1] = 0x89;
128 memmove(ptr + 2, ptr + 3, end - ptr - 3);
129 ptr[end - ptr - 1] = 0x90; /* NOP */
130 } else {
131 /*
132 * There are cases where we need to preserve instruction end position,
133 * for example, when RIP-relative address is used. Fortunately, RIP-
134 * relative addressing cannot use an index register, and therefore
135 * RESTRICTED_REGISTER_USED cannot be set. Therefore, no matter
136 * whether RIP-relative addressing is used, as long as restricted
137 * register is not used, we are safe to put nop in the beginning and
138 * preserve instruction end position.
139 */
140 ptr[2] = 0x89;
141 ptr[1] = ptr[0];
142 ptr[0] = 0x90; /* NOP */
143 }
144 data->did_rewrite = 1;
145 return TRUE;
146 case 0x18:
147 /* prefetchnta => nop...nop */
148 memset(ptr, 0x90, end - ptr);
149 data->did_rewrite = 1;
150 return TRUE;
151 default:
152 return FALSE;
153 }
154 } else if (ptr[0] == 0x66 && IsREX(ptr[1]) &&
155 memcmp(ptr + 2, "\x0f\xe7", 2) == 0) {
156 /* movntdq => movdqa */
157 ptr[3] = 0x7f;
158 data->did_rewrite = 1;
159 return TRUE;
160 }
161 #else
162 #error "Unknown architecture"
163 #endif
164 return FALSE;
165 }
166
36 Bool NaClDfaStubOutUnsupportedInstruction(const uint8_t *begin, 167 Bool NaClDfaStubOutUnsupportedInstruction(const uint8_t *begin,
37 const uint8_t *end, 168 const uint8_t *end,
38 uint32_t info, 169 uint32_t info,
39 void *callback_data) { 170 void *callback_data) {
40 struct StubOutCallbackData *data = callback_data; 171 struct StubOutCallbackData *data = callback_data;
41 /* Stub-out instructions unsupported on this CPU, but valid on other CPUs. */ 172 uintptr_t temp_addr;
42 if ((info & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION) { 173 uint8_t *bundle_begin;
43 data->did_rewrite = 1; 174 Bool rc;
44 memset((uint8_t *)begin, NACL_HALT_OPCODE, end - begin); 175 UNREFERENCED_PARAMETER(end);
45 return TRUE; 176
46 } else if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION) { 177 /*
47 if (data->flags & NACL_DISABLE_NONTEMPORALS_X86) { 178 * We can only handle two types of errors by rewriting:
48 return FALSE; 179 * CPUID_UNSUPPORTED_INSTRUCTION and UNSUPPORTED_INSTRUCTION.
49 } else { 180 */
50 /* TODO(ruiq): rewrite instruction. For now, we keep the original 181 if ((info & VALIDATION_ERRORS_MASK) != CPUID_UNSUPPORTED_INSTRUCTION &&
51 * instruction and indicate validation success, which is consistent 182 (info & VALIDATION_ERRORS_MASK) != UNSUPPORTED_INSTRUCTION)
52 * with current validation results. */ 183 return FALSE;
53 data->did_rewrite = 0; 184 if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION &&
54 return TRUE; 185 (data->flags & NACL_DISABLE_NONTEMPORALS_X86))
55 } 186 return FALSE;
56 } else { 187
57 return FALSE; 188 CHECK(!data->chunk_processed_as_a_contiguous_stream);
58 } 189 /*
190 * Compute bundle begin. Note that the validator does not enforce the
191 * validated chunk to start at a 32-byte aligned address, although it checks
192 * the chunk size to be a multiple of 32 (kBundleSize). Therefore, we cannot
193 * simply compute bundle_begin from "begin & ~kBundleMask", but have to
194 * consider to bundle_begin_offset. Alternatively, bundle begin can be passed
195 * to the user callback by the validator. However, the callback interface
196 * needs to be changed for this additional parameter, because "callback_data"
197 * is opaque to the validator and should not be used for this purpose.
198 */
199 temp_addr = ((uintptr_t) begin & ~kBundleMask) + data->bundle_begin_offset;
200 if (temp_addr <= (uintptr_t) begin)
201 bundle_begin = (uint8_t *) temp_addr;
202 else
203 bundle_begin = (uint8_t *) (temp_addr - kBundleSize);
204
205 /*
206 * Rewrite the bundle containing current instruction. We want to rewrite the
207 * whole bundle primarily because this eases revalidation. We should not
208 * revalidate a single instruction because it may use a restricted register,
209 * and is prefixed by a restricted register producing instruction in the
210 * bundle. If we just revalidate this restricted register consuming
211 * instruction, we will erroneously get an UNRESTRICTED_INDEX_REGISTER error.
212 * On the other hand, we don't want to revalidate the whole chunk because it
213 * can be expensive. Therefore, the only choice left is to revalidate the
214 * current bundle. This requires us to rewrite the whole bundle first, because
215 * there could be to-be-rewritten instructions after current instruction, and
216 * the revalidation of current bundle would fail if we just rewrite current
217 * instruction without rewriting those after.
218 */
219 #if NACL_BUILD_SUBARCH == 32
220 rc = ValidateChunkIA32(bundle_begin, kBundleSize, 0 /*options*/,
221 data->cpu_features,
222 NaClDfaRewriteUnsupportedInstruction,
223 callback_data);
224 #elif NACL_BUILD_SUBARCH == 64
225 rc = ValidateChunkAMD64(bundle_begin, kBundleSize, 0 /*options*/,
226 data->cpu_features,
227 NaClDfaRewriteUnsupportedInstruction,
228 callback_data);
229 #endif
230
231 if (!rc)
232 return FALSE;
233
234 /* Revalidate the bundle after rewriting. */
235 #if NACL_BUILD_SUBARCH == 32
236 rc = ValidateChunkIA32(bundle_begin, kBundleSize, 0 /*options*/,
237 data->cpu_features,
238 NaClDfaProcessPostRewriteValidationError,
239 NULL);
240 #elif NACL_BUILD_SUBARCH == 64
241 rc = ValidateChunkAMD64(bundle_begin, kBundleSize, 0 /*options*/,
242 data->cpu_features,
243 NaClDfaProcessPostRewriteValidationError,
244 NULL);
245 #endif
246 return rc;
59 } 247 }
60 248
61 Bool NaClDfaProcessCodeCopyInstruction(const uint8_t *begin_new, 249 Bool NaClDfaProcessCodeCopyInstruction(const uint8_t *begin_new,
62 const uint8_t *end_new, 250 const uint8_t *end_new,
63 uint32_t info_new, 251 uint32_t info_new,
64 void *callback_data) { 252 void *callback_data) {
65 struct CodeCopyCallbackData *data = callback_data; 253 struct CodeCopyCallbackData *data = callback_data;
66 size_t instruction_length = end_new - begin_new; 254 size_t instruction_length = end_new - begin_new;
67 255
68 /* Sanity check: instruction must be no longer than 17 bytes. */ 256 /* Sanity check: instruction must be no longer than 17 bytes. */
69 CHECK(instruction_length <= MAX_INSTRUCTION_LENGTH); 257 CHECK(instruction_length <= MAX_INSTRUCTION_LENGTH);
70 258
71 return data->copy_func( 259 return data->copy_func(
72 (uint8_t *)begin_new + data->existing_minus_new, /* begin_existing */ 260 (uint8_t *)begin_new + data->existing_minus_new, /* begin_existing */
73 (info_new & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION ? 261 (info_new & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION ?
74 (uint8_t *)kStubOutMem : 262 (uint8_t *)kStubOutMem :
75 (uint8_t *)begin_new, 263 (uint8_t *)begin_new,
76 (uint8_t)instruction_length); 264 (uint8_t)instruction_length);
77 } 265 }
78 266
79 Bool NaClDfaCodeReplacementIsStubouted(const uint8_t *begin_existing, 267 Bool NaClDfaCodeReplacementIsStubouted(const uint8_t *begin_existing,
80 size_t instruction_length) { 268 size_t instruction_length) {
81 269
82 /* Unsupported instruction must have been replaced with HLTs. */ 270 /* Unsupported instruction must have been replaced with HLTs. */
83 if (memcmp(kStubOutMem, begin_existing, instruction_length) == 0) 271 if (memcmp(kStubOutMem, begin_existing, instruction_length) == 0)
84 return TRUE; 272 return TRUE;
85 else 273 else
86 return FALSE; 274 return FALSE;
87 } 275 }
OLDNEW
« no previous file with comments | « src/trusted/validator_ragel/dfa_validate_common.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698