OLD | NEW |
| (Empty) |
1 /* | |
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 | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 /* Implement the ApplyValidator API for the x86-64 architecture. */ | |
8 #include <assert.h> | |
9 #include "native_client/src/shared/platform/nacl_log.h" | |
10 #include "native_client/src/trusted/validator/ncvalidate.h" | |
11 #include "native_client/src/trusted/validator/validation_cache.h" | |
12 #include "native_client/src/trusted/validator/x86/decoder/nc_inst_iter.h" | |
13 #include "native_client/src/trusted/validator/x86/decoder/nc_inst_state_internal
.h" | |
14 #include "native_client/src/trusted/cpu_features/arch/x86/cpu_x86.h" | |
15 #include "native_client/src/trusted/validator/x86/nc_segment.h" | |
16 #include "native_client/src/trusted/validator/x86/ncval_reg_sfi/ncval_decode_tab
les.h" | |
17 #include "native_client/src/trusted/validator/x86/ncval_reg_sfi/ncvalidate_iter.
h" | |
18 | |
19 /* Be sure the correct compile flags are defined for this. */ | |
20 #if NACL_ARCH(NACL_TARGET_ARCH) != NACL_x86 | |
21 # error("Can't compile, target is for x86-64") | |
22 #else | |
23 # if NACL_TARGET_SUBARCH != 64 | |
24 # error("Can't compile, target is for x86-64") | |
25 # endif | |
26 #endif | |
27 | |
28 NaClValidationStatus NaClValidatorSetup_x86_64( | |
29 intptr_t guest_addr, | |
30 size_t size, | |
31 int readonly_text, | |
32 const NaClCPUFeaturesX86 *cpu_features, | |
33 struct NaClValidatorState** vstate_ptr) { | |
34 *vstate_ptr = NaClValidatorStateCreate(guest_addr, size, RegR15, | |
35 readonly_text, cpu_features); | |
36 return (*vstate_ptr == NULL) | |
37 ? NaClValidationFailedOutOfMemory | |
38 : NaClValidationSucceeded; /* or at least to this point! */ | |
39 } | |
40 | |
41 static NaClValidationStatus ApplyValidator_x86_64( | |
42 uintptr_t guest_addr, | |
43 uint8_t *data, | |
44 size_t size, | |
45 int stubout_mode, | |
46 int readonly_text, | |
47 const NaClCPUFeatures *f, | |
48 const struct NaClValidationMetadata *metadata, | |
49 struct NaClValidationCache *cache) { | |
50 /* TODO(jfb) Use a safe cast here. */ | |
51 const NaClCPUFeaturesX86 *cpu_features = (NaClCPUFeaturesX86 *) f; | |
52 struct NaClValidatorState *vstate; | |
53 NaClValidationStatus status; | |
54 void *query = NULL; | |
55 | |
56 /* Check that the given parameter values are supported. */ | |
57 if (stubout_mode && readonly_text) | |
58 return NaClValidationFailedNotImplemented; | |
59 | |
60 if (!NaClArchSupportedX86(cpu_features)) | |
61 return NaClValidationFailedCpuNotSupported; | |
62 | |
63 /* Don't cache in stubout mode. */ | |
64 if (stubout_mode) | |
65 cache = NULL; | |
66 | |
67 /* If the validation caching interface is available, perform a query. */ | |
68 if (cache != NULL) | |
69 query = cache->CreateQuery(cache->handle); | |
70 if (query != NULL) { | |
71 const char validator_id[] = "x86-64"; | |
72 cache->AddData(query, (uint8_t *) validator_id, sizeof(validator_id)); | |
73 cache->AddData(query, (uint8_t *) cpu_features, sizeof(*cpu_features)); | |
74 NaClAddCodeIdentity(data, size, metadata, cache, query); | |
75 if (cache->QueryKnownToValidate(query)) { | |
76 cache->DestroyQuery(query); | |
77 return NaClValidationSucceeded; | |
78 } | |
79 } | |
80 | |
81 /* Init then validator state. */ | |
82 status = NaClValidatorSetup_x86_64( | |
83 guest_addr, size, readonly_text, cpu_features, &vstate); | |
84 if (status != NaClValidationSucceeded) { | |
85 if (query != NULL) | |
86 cache->DestroyQuery(query); | |
87 return status; | |
88 } | |
89 NaClValidatorStateSetLogVerbosity(vstate, LOG_ERROR); | |
90 NaClValidatorStateSetDoStubOut(vstate, stubout_mode); | |
91 | |
92 /* Validate. */ | |
93 NaClValidateSegment(data, guest_addr, size, vstate); | |
94 status = (NaClValidatesOk(vstate) || stubout_mode) ? | |
95 NaClValidationSucceeded : NaClValidationFailed; | |
96 | |
97 /* Cache the result if validation succeded and the code was not modified. */ | |
98 if (query != NULL) { | |
99 /* Don't cache the result if the code is modified. */ | |
100 if (status == NaClValidationSucceeded && !NaClValidatorDidStubOut(vstate)) | |
101 cache->SetKnownToValidate(query); | |
102 cache->DestroyQuery(query); | |
103 } | |
104 | |
105 NaClValidatorStateDestroy(vstate); | |
106 return status; | |
107 } | |
108 | |
109 static NaClValidationStatus ApplyValidatorCodeReplacement_x86_64( | |
110 uintptr_t guest_addr, | |
111 uint8_t *data_old, | |
112 uint8_t *data_new, | |
113 size_t size, | |
114 const NaClCPUFeatures *f) { | |
115 /* TODO(jfb) Use a safe cast here. */ | |
116 const NaClCPUFeaturesX86 *cpu_features = (NaClCPUFeaturesX86 *) f; | |
117 NaClValidationStatus status; | |
118 struct NaClValidatorState *vstate; | |
119 | |
120 /* Check that the given parameter values are supported. */ | |
121 if (!NaClArchSupportedX86(cpu_features)) | |
122 return NaClValidationFailedCpuNotSupported; | |
123 | |
124 /* Init then validator state. */ | |
125 status = NaClValidatorSetup_x86_64(guest_addr, size, FALSE, | |
126 cpu_features, &vstate); | |
127 if (status != NaClValidationSucceeded) | |
128 return status; | |
129 NaClValidatorStateSetLogVerbosity(vstate, LOG_ERROR); | |
130 | |
131 /* Validate. */ | |
132 NaClValidateSegmentPair(data_old, data_new, guest_addr, size, vstate); | |
133 status = NaClValidatesOk(vstate) ? | |
134 NaClValidationSucceeded : NaClValidationFailed; | |
135 | |
136 NaClValidatorStateDestroy(vstate); | |
137 return status; | |
138 } | |
139 | |
140 /* Copies code from src to dest in a thread safe way, returns 1 on success, | |
141 * returns 0 on error. This will likely assert on error to avoid partially | |
142 * copied code or undefined state. | |
143 */ | |
144 static int CopyCodeIter(uint8_t *dst, uint8_t *src, | |
145 NaClPcAddress vbase, size_t size, | |
146 NaClCopyInstructionFunc copy_func) { | |
147 NaClSegment segment_old; | |
148 NaClSegment segment_new; | |
149 NaClInstIter *iter_old; | |
150 NaClInstIter *iter_new; | |
151 NaClInstState *istate_old; | |
152 NaClInstState *istate_new; | |
153 int still_good = 1; | |
154 | |
155 NaClSegmentInitialize(dst, vbase, size, &segment_old); | |
156 NaClSegmentInitialize(src, vbase, size, &segment_new); | |
157 | |
158 iter_old = NaClInstIterCreate(kNaClValDecoderTables, &segment_old); | |
159 if (NULL == iter_old) return 0; | |
160 iter_new = NaClInstIterCreate(kNaClValDecoderTables, &segment_new); | |
161 if (NULL == iter_new) { | |
162 NaClInstIterDestroy(iter_old); | |
163 return 0; | |
164 } | |
165 while (1) { | |
166 /* March over every instruction, which means NaCl pseudo-instructions are | |
167 * treated as multiple instructions. Checks in NaClValidateCodeReplacement | |
168 * guarantee that only valid replacements will happen, and no pseudo- | |
169 * instructions should be touched. | |
170 */ | |
171 if (!(NaClInstIterHasNext(iter_old) && NaClInstIterHasNext(iter_new))) { | |
172 if (NaClInstIterHasNext(iter_old) || NaClInstIterHasNext(iter_new)) { | |
173 NaClLog(LOG_ERROR, | |
174 "Segment replacement: copy failed: iterators " | |
175 "length mismatch\n"); | |
176 still_good = 0; | |
177 } | |
178 break; | |
179 } | |
180 istate_old = NaClInstIterGetState(iter_old); | |
181 istate_new = NaClInstIterGetState(iter_new); | |
182 if (istate_old->bytes.length != istate_new->bytes.length || | |
183 iter_old->memory.read_length != iter_new->memory.read_length || | |
184 istate_new->inst_addr != istate_old->inst_addr) { | |
185 /* Sanity check: this should never happen based on checks in | |
186 * NaClValidateInstReplacement. | |
187 */ | |
188 NaClLog(LOG_ERROR, | |
189 "Segment replacement: copied instructions misaligned\n"); | |
190 still_good = 0; | |
191 break; | |
192 } | |
193 /* Replacing all modified instructions at once could yield a speedup here | |
194 * as every time we modify instructions we must serialize all processors | |
195 * twice. Re-evaluate if code modification performance is an issue. | |
196 */ | |
197 if (!copy_func(iter_old->memory.mpc, iter_new->memory.mpc, | |
198 iter_old->memory.read_length)) { | |
199 NaClLog(LOG_ERROR, | |
200 "Segment replacement: copy failed: unable to copy instruction\n"); | |
201 still_good = 0; | |
202 break; | |
203 } | |
204 NaClInstIterAdvance(iter_old); | |
205 NaClInstIterAdvance(iter_new); | |
206 } | |
207 | |
208 NaClInstIterDestroy(iter_old); | |
209 NaClInstIterDestroy(iter_new); | |
210 return still_good; | |
211 } | |
212 | |
213 static NaClValidationStatus ApplyValidatorCopy_x86_64( | |
214 uintptr_t guest_addr, | |
215 uint8_t *data_old, | |
216 uint8_t *data_new, | |
217 size_t size, | |
218 const NaClCPUFeatures *f, | |
219 NaClCopyInstructionFunc copy_func) { | |
220 /* TODO(jfb) Use a safe cast here. */ | |
221 const NaClCPUFeaturesX86 *cpu_features = (NaClCPUFeaturesX86 *) f; | |
222 if (!NaClArchSupportedX86(cpu_features)) | |
223 return NaClValidationFailedCpuNotSupported; | |
224 | |
225 return (0 == CopyCodeIter(data_old, data_new, guest_addr, size, copy_func)) | |
226 ? NaClValidationFailed : NaClValidationSucceeded; | |
227 } | |
228 | |
229 static const struct NaClValidatorInterface validator = { | |
230 TRUE, /* Optional stubout_mode is implemented. */ | |
231 TRUE, /* Optional readonly_text is implemented. */ | |
232 TRUE, /* Optional code replacement functions are implemented. */ | |
233 ApplyValidator_x86_64, | |
234 ApplyValidatorCopy_x86_64, | |
235 ApplyValidatorCodeReplacement_x86_64, | |
236 sizeof(NaClCPUFeaturesX86), | |
237 NaClSetAllCPUFeaturesX86, | |
238 NaClGetCurrentCPUFeaturesX86, | |
239 NaClFixCPUFeaturesX86, | |
240 }; | |
241 | |
242 const struct NaClValidatorInterface *NaClValidatorCreate_x86_64(void) { | |
243 return &validator; | |
244 } | |
OLD | NEW |