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

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: Trivial 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
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 /* Similar to NaClDfaRewriteUnsupportedInstruction(), we don't consider
Petr Hosek 2015/08/11 18:22:35 Nit: the comment body should only start on the sec
ruiq 2015/08/11 21:08:34 Done.
44 * DIRECT_JUMP_OUT_OF_RANGE as an error. But if we still get
45 * CPUID_UNSUPPORTED_INSTRUCTION or UNSUPPORTED_INSTRUCTION, the validation
46 * should fail, because these errors should have already been fixed
47 * by NaClDfaRewriteUnsupportedInstruction().
48 */
49 if ((info & VALIDATION_ERRORS_MASK) == DIRECT_JUMP_OUT_OF_RANGE)
50 return TRUE;
51 else
52 return FALSE;
53 }
54
55 #if NACL_BUILD_SUBARCH == 64
56 static Bool IsREX(uint8_t byte) {
57 return byte >= 0x40 && byte <= 0x4f;
58 }
59 #endif
60
61 static Bool NaClDfaRewriteUnsupportedInstruction(const uint8_t *begin,
62 const uint8_t *end,
63 uint32_t info,
64 void *callback_data) {
65 uint8_t *ptr = (uint8_t *) begin;
66 struct StubOutCallbackData *data = callback_data;
67 /* Clear DIRECT_JUMP_OUT_OF_RANGE error, because it may be introduced by
68 * validating a bundle which is smaller than the original chunk size. Even if
69 * the orignal chunk has this error, it can be detected when validating the
70 * whole chunk.
71 */
72 info &= ~DIRECT_JUMP_OUT_OF_RANGE;
73 if ((info & VALIDATION_ERRORS_MASK) == 0) {
74 return TRUE;
75 } else if ((info & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION) {
76 /* Stub-out instructions unsupported on this CPU, but valid on other CPUs.*/
77 data->did_rewrite = 1;
78 memset((uint8_t *)begin, NACL_HALT_OPCODE, end - begin);
79 return TRUE;
80 } else if ((info & VALIDATION_ERRORS_MASK) != UNSUPPORTED_INSTRUCTION) {
81 return FALSE;
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(begin, "\x0f\xe7", 2) == 0) {
Petr Hosek 2015/08/11 18:22:36 Since you already have a `ptr`, you could first ch
ruiq 2015/08/11 21:08:34 Separating these two cases were done on purpose. T
96 /* movntq => movq */
97 ptr[1] = 0x7f;
98 data->did_rewrite = 1;
99 return TRUE;
100 } else if (memcmp(begin, "\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(begin[0]) && (begin[1] == 0x0f)) {
Petr Hosek 2015/08/11 18:22:36 The same here, you can use `ptr` to simplify this
ruiq 2015/08/11 21:08:34 Same here.
108 uint8_t opcode_byte2 = begin[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 /* The rewriting for movnti is special because it changes instruction
119 * boundary: movnti is replaced by a mov and a nop so that the total
120 * size does not change. Therefore, special care needs to be taken:
121 * if restricted register is used in this instruction, we have to put
122 * nop at the end so that the rewritten restricted register consuming
123 * instruction follows closely with the restricted register producing
124 * instruction (if there is one).
125 */
126 ptr[1] = 0x89;
127 memmove(ptr + 2, ptr + 3, end - begin - 3);
128 ptr[end - begin - 1] = 0x90;
129 } else {
130 /* There are cases where we need to preserve instruction end position,
131 * for example, when RIP-relative address is used. Fortunately, RIP-
132 * relative addressing cannot use an index register, and therefore
133 * RESTRICTED_REGISTER_USED cannot be set. Therefore, no matter
134 * whether RIP-relative addressing is used, as long as restricted
135 * register is not used, we are safe to put nop in the beginning and
136 * preserve instruction end position.
137 */
138 ptr[2] = 0x89;
139 ptr[1] = ptr[0];
140 ptr[0] = 0x90;
141 }
142 data->did_rewrite = 1;
143 return TRUE;
144 case 0x18:
145 /* prefetchnta => nop...nop */
146 memset(ptr, 0x90, end - begin);
147 data->did_rewrite = 1;
148 return TRUE;
149 default:
150 return FALSE;
151 }
152 } else if (begin[0] == 0x66 && IsREX(begin[1]) &&
153 memcmp(begin + 2, "\x0f\xe7", 2) == 0) {
Petr Hosek 2015/08/11 18:22:35 This could be merged into the `if` case.
ruiq 2015/08/11 21:08:34 Same here.
ruiq 2015/08/12 05:37:50 The other point is that we don't want to overly ge
154 /* movntdq => movdqa */
155 ptr[3] = 0x7f;
156 data->did_rewrite = 1;
157 return TRUE;
158 }
159 #endif
160 return FALSE;
161 }
162
36 Bool NaClDfaStubOutUnsupportedInstruction(const uint8_t *begin, 163 Bool NaClDfaStubOutUnsupportedInstruction(const uint8_t *begin,
37 const uint8_t *end, 164 const uint8_t *end,
38 uint32_t info, 165 uint32_t info,
39 void *callback_data) { 166 void *callback_data) {
40 struct StubOutCallbackData *data = callback_data; 167 struct StubOutCallbackData *data = callback_data;
41 /* Stub-out instructions unsupported on this CPU, but valid on other CPUs. */ 168 uintptr_t temp_addr;
42 if ((info & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION) { 169 uint8_t *bundle_begin;
43 data->did_rewrite = 1; 170 Bool rc, rc2;
Petr Hosek 2015/08/11 18:22:36 You don't need two different variable for return v
ruiq 2015/08/11 21:08:33 Done.
44 memset((uint8_t *)begin, NACL_HALT_OPCODE, end - begin); 171 UNREFERENCED_PARAMETER(end);
45 return TRUE; 172
46 } else if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION) { 173 /* We can only handle two types of errors by rewriting:
47 if (data->flags & NACL_DISABLE_NONTEMPORALS_X86) { 174 * CPUID_UNSUPPORTED_INSTRUCTION and UNSUPPORTED_INSTRUCTION.
48 return FALSE; 175 */
49 } else { 176 if ((info & VALIDATION_ERRORS_MASK) != CPUID_UNSUPPORTED_INSTRUCTION &&
50 /* TODO(ruiq): rewrite instruction. For now, we keep the original 177 (info & VALIDATION_ERRORS_MASK) != UNSUPPORTED_INSTRUCTION)
51 * instruction and indicate validation success, which is consistent 178 return FALSE;
52 * with current validation results. */ 179 if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION &&
53 data->did_rewrite = 0; 180 (data->flags & NACL_DISABLE_NONTEMPORALS_X86))
54 return TRUE; 181 return FALSE;
55 } 182
56 } else { 183 CHECK(!data->chunk_processed_as_a_contiguous_stream);
57 return FALSE; 184 /* Compute bundle begin. Note that the validator does not enforce the
58 } 185 * validated chunk to start at a 32-byte aligned address, although it checks
186 * the chunk size to be a multiple of 32 (kBundleSize). Therefore, we cannot
187 * simply compute bundle_begin from "begin & ~kBundleMask", but have to
188 * consider to bundle_begin_offset. Alternatively, bundle begin can be passed
189 * to the user callback by the validator. However, the callback interface
190 * needs to be changed for this additional parameter, because "callback_data"
191 * is opaque to the validator and should not be used for this purpose.
192 */
193 temp_addr = ((uintptr_t) begin & ~kBundleMask) + data->bundle_begin_offset;
194 if (temp_addr <= (uintptr_t) begin)
195 bundle_begin = (uint8_t *) temp_addr;
196 else
197 bundle_begin = (uint8_t *) (temp_addr - kBundleSize);
198
199 /* Rewrite the bundle containing current instruction. We want to rewrite the
200 * whole bundle primarily because this eases revalidation. We should not
201 * revalidate a single instruction because it may use a restricted register,
202 * and is prefixed by a restricted register producing instruction in the
203 * bundle. If we just revalidate this restricted register consuming
204 * instruction, we will erroneously get an UNRESTRICTED_INDEX_REGISTER error.
205 * On the other hand, we don't want to revalidate the whole chunk because it
206 * can be expensive. Therefore, the only choice left is to revalidate the
207 * current bundle. This requires us to rewrite the whole bundle first, because
208 * there could be to-be-rewritten instructions after current instruction, and
209 * the revalidation of current bundle would fail if we just rewrite current
210 * instruction without rewriting those after.
211 */
212 #if NACL_BUILD_SUBARCH == 32
213 rc = ValidateChunkIA32(bundle_begin, kBundleSize, 0 /*options*/,
214 data->cpu_features,
215 NaClDfaRewriteUnsupportedInstruction,
216 callback_data);
217 #elif NACL_BUILD_SUBARCH == 64
218 rc = ValidateChunkAMD64(bundle_begin, kBundleSize, 0 /*options*/,
219 data->cpu_features,
220 NaClDfaRewriteUnsupportedInstruction,
221 callback_data);
222 #endif
223
224 if (!rc)
225 return FALSE;
226
227 /* Revalidate the bundle after rewriting. */
228 #if NACL_BUILD_SUBARCH == 32
229 rc2 = ValidateChunkIA32(bundle_begin, kBundleSize, 0 /*options*/,
230 data->cpu_features,
231 NaClDfaProcessPostRewriteValidationError,
232 NULL);
233 #elif NACL_BUILD_SUBARCH == 64
234 rc2 = ValidateChunkAMD64(bundle_begin, kBundleSize, 0 /*options*/,
235 data->cpu_features,
236 NaClDfaProcessPostRewriteValidationError,
237 NULL);
238 #endif
239 if (!rc2)
240 return FALSE;
241 return TRUE;
Petr Hosek 2015/08/11 18:22:36 Nit: you can just return `rc` here.
ruiq 2015/08/11 21:08:34 Done.
59 } 242 }
60 243
61 Bool NaClDfaProcessCodeCopyInstruction(const uint8_t *begin_new, 244 Bool NaClDfaProcessCodeCopyInstruction(const uint8_t *begin_new,
62 const uint8_t *end_new, 245 const uint8_t *end_new,
63 uint32_t info_new, 246 uint32_t info_new,
64 void *callback_data) { 247 void *callback_data) {
65 struct CodeCopyCallbackData *data = callback_data; 248 struct CodeCopyCallbackData *data = callback_data;
66 size_t instruction_length = end_new - begin_new; 249 size_t instruction_length = end_new - begin_new;
67 250
68 /* Sanity check: instruction must be no longer than 17 bytes. */ 251 /* Sanity check: instruction must be no longer than 17 bytes. */
69 CHECK(instruction_length <= MAX_INSTRUCTION_LENGTH); 252 CHECK(instruction_length <= MAX_INSTRUCTION_LENGTH);
70 253
71 return data->copy_func( 254 return data->copy_func(
72 (uint8_t *)begin_new + data->existing_minus_new, /* begin_existing */ 255 (uint8_t *)begin_new + data->existing_minus_new, /* begin_existing */
73 (info_new & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION ? 256 (info_new & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION ?
74 (uint8_t *)kStubOutMem : 257 (uint8_t *)kStubOutMem :
75 (uint8_t *)begin_new, 258 (uint8_t *)begin_new,
76 (uint8_t)instruction_length); 259 (uint8_t)instruction_length);
77 } 260 }
78 261
79 Bool NaClDfaCodeReplacementIsStubouted(const uint8_t *begin_existing, 262 Bool NaClDfaCodeReplacementIsStubouted(const uint8_t *begin_existing,
80 size_t instruction_length) { 263 size_t instruction_length) {
81 264
82 /* Unsupported instruction must have been replaced with HLTs. */ 265 /* Unsupported instruction must have been replaced with HLTs. */
83 if (memcmp(kStubOutMem, begin_existing, instruction_length) == 0) 266 if (memcmp(kStubOutMem, begin_existing, instruction_length) == 0)
84 return TRUE; 267 return TRUE;
85 else 268 else
86 return FALSE; 269 return FALSE;
87 } 270 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698