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 Validator API for the x86-64 architecture. */
8 : #include <errno.h>
9 : #include <string.h>
10 :
11 : #include "native_client/src/shared/platform/nacl_check.h"
12 : #include "native_client/src/trusted/validator/validation_cache.h"
13 : #include "native_client/src/trusted/validator_ragel/dfa_validate_common.h"
14 : #include "native_client/src/trusted/validator_ragel/validator.h"
15 :
16 : /*
17 : * Be sure the correct compile flags are defined for this.
18 : * TODO(khim): Try to figure out if these checks actually make any sense.
19 : * I don't foresee any use in cross-environment, but it should work
20 : * and may be useful in some case so why not?
21 : */
22 : #if NACL_ARCH(NACL_TARGET_ARCH) != NACL_x86 || NACL_TARGET_SUBARCH != 64
23 : # error "Can't compile, target is for x86-64"
24 : #endif
25 :
26 :
27 : static NaClValidationStatus ApplyDfaValidator_x86_64(
28 1414 : uintptr_t guest_addr,
29 1414 : uint8_t *data,
30 1414 : size_t size,
31 1414 : int stubout_mode,
32 1414 : int readonly_text,
33 1414 : const NaClCPUFeatures *f,
34 1414 : const struct NaClValidationMetadata *metadata,
35 1414 : struct NaClValidationCache *cache) {
36 : /* TODO(jfb) Use a safe cast here. */
37 1414 : NaClCPUFeaturesX86 *cpu_features = (NaClCPUFeaturesX86 *) f;
38 1414 : enum NaClValidationStatus status = NaClValidationFailed;
39 1414 : int did_stubout = 0;
40 1414 : void *query = NULL;
41 2828 : UNREFERENCED_PARAMETER(guest_addr);
42 :
43 1414 : if (stubout_mode)
44 0 : return NaClValidationFailedNotImplemented;
45 1414 : if (!NaClArchSupportedX86(cpu_features))
46 0 : return NaClValidationFailedCpuNotSupported;
47 1414 : if (size & kBundleMask)
48 0 : return NaClValidationFailed;
49 :
50 : /*
51 : * If the validation caching interface is available and it would be
52 : * inexpensive to do so, perform a query.
53 : */
54 1425 : if (cache != NULL && NaClCachingIsInexpensive(cache, metadata))
55 11 : query = cache->CreateQuery(cache->handle);
56 1414 : if (query != NULL) {
57 : const char validator_id[] = "x86-64 dfa";
58 11 : cache->AddData(query, (uint8_t *) validator_id, sizeof(validator_id));
59 11 : cache->AddData(query, (uint8_t *) cpu_features, sizeof(*cpu_features));
60 11 : NaClAddCodeIdentity(data, size, metadata, cache, query);
61 11 : if (cache->QueryKnownToValidate(query)) {
62 3 : cache->DestroyQuery(query);
63 3 : return NaClValidationSucceeded;
64 : }
65 8 : }
66 :
67 1411 : if (readonly_text) {
68 7 : if (ValidateChunkAMD64(data, size, 0 /*options*/, cpu_features,
69 : NaClDfaProcessValidationError,
70 : NULL))
71 7 : status = NaClValidationSucceeded;
72 7 : } else {
73 1404 : if (ValidateChunkAMD64(data, size, 0 /*options*/, cpu_features,
74 : NaClDfaStubOutCPUUnsupportedInstruction,
75 : &did_stubout))
76 1366 : status = NaClValidationSucceeded;
77 : }
78 :
79 1449 : if (status != NaClValidationSucceeded && errno == ENOMEM)
80 0 : status = NaClValidationFailedOutOfMemory;
81 :
82 : /* Cache the result if validation succeeded and the code was not modified. */
83 1411 : if (query != NULL) {
84 15 : if (status == NaClValidationSucceeded && did_stubout == 0)
85 6 : cache->SetKnownToValidate(query);
86 8 : cache->DestroyQuery(query);
87 8 : }
88 :
89 1411 : return status;
90 1414 : }
91 :
92 :
93 : static NaClValidationStatus ValidatorCodeCopy_x86_64(
94 7 : uintptr_t guest_addr,
95 7 : uint8_t *data_existing,
96 7 : uint8_t *data_new,
97 7 : size_t size,
98 7 : const NaClCPUFeatures *f,
99 7 : NaClCopyInstructionFunc copy_func) {
100 : /* TODO(jfb) Use a safe cast here. */
101 7 : NaClCPUFeaturesX86 *cpu_features = (NaClCPUFeaturesX86 *) f;
102 7 : struct CodeCopyCallbackData callback_data;
103 14 : UNREFERENCED_PARAMETER(guest_addr);
104 :
105 7 : if (size & kBundleMask)
106 0 : return NaClValidationFailed;
107 7 : callback_data.copy_func = copy_func;
108 7 : callback_data.existing_minus_new = data_existing - data_new;
109 7 : if (ValidateChunkAMD64(data_new, size, CALL_USER_CALLBACK_ON_EACH_INSTRUCTION,
110 : cpu_features, NaClDfaProcessCodeCopyInstruction,
111 : &callback_data))
112 7 : return NaClValidationSucceeded;
113 0 : if (errno == ENOMEM)
114 0 : return NaClValidationFailedOutOfMemory;
115 0 : return NaClValidationFailed;
116 7 : }
117 :
118 :
119 339 : static Bool ProcessCodeReplacementInstruction(const uint8_t *begin_new,
120 339 : const uint8_t *end_new,
121 339 : uint32_t info_new,
122 339 : void *callback_data) {
123 339 : ptrdiff_t existing_minus_new = (ptrdiff_t)callback_data;
124 339 : size_t instruction_length = end_new - begin_new;
125 339 : const uint8_t *begin_existing = begin_new + existing_minus_new;
126 339 : const uint8_t *end_existing = end_new + existing_minus_new;
127 :
128 : /* Sanity check: instruction must be no longer than 17 bytes. */
129 1017 : CHECK(instruction_length <= MAX_INSTRUCTION_LENGTH);
130 :
131 : /* Unsupported instruction must have been replaced with HLTs. */
132 339 : if ((info_new & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION)
133 0 : return NaClDfaCodeReplacementIsStubouted(begin_existing,
134 : instruction_length);
135 :
136 : /* If we have jump which jumps out of it's range... */
137 339 : if (info_new & DIRECT_JUMP_OUT_OF_RANGE) {
138 : /* then everything is fine if it's the only error and jump is unchanged! */
139 2 : if ((info_new & VALIDATION_ERRORS_MASK) == DIRECT_JUMP_OUT_OF_RANGE &&
140 2 : memcmp(begin_new, begin_existing, instruction_length) == 0)
141 1 : return TRUE;
142 1 : return FALSE;
143 : }
144 :
145 : /* If instruction is not accepted then we have nothing to do here. */
146 337 : if (info_new & (VALIDATION_ERRORS_MASK | BAD_JUMP_TARGET))
147 2 : return FALSE;
148 :
149 : /* Instruction is untouched: we are done. */
150 335 : if (memcmp(begin_new, begin_existing, instruction_length) == 0)
151 313 : return TRUE;
152 :
153 : /* Only some instructions can be modified. */
154 22 : if (!(info_new & MODIFIABLE_INSTRUCTION))
155 9 : return FALSE;
156 :
157 : /*
158 : * In order to understand where the following three cases came from you need
159 : * to understand that there are three kinds of instructions:
160 : * - "normal" x86 instructions
161 : * - x86 instructions which [ab]use "immediate" field in the instructions
162 : * - 3DNow! x86 instructions use it as an opcode extentions
163 : * - four-operand instructions encode fourth register in it
164 : * - five-operands instructions encode 2bit immediate and fourth register
165 : *
166 : * For the last two cases we need to keep either the whole last byte or at
167 : * least non-immediate part of it intact.
168 : *
169 : * See Figure 1-1 "Instruction Encoding Syntax" in AMDs third volume
170 : * "General-Purpose and System Instructions" for the information about
171 : * "normal" x86 instructions and 3DNow! instructions.
172 : *
173 : * See Figure 1-1 "Typical Descriptive Synopsis - Extended SSE Instructions"
174 : * in AMDs fourth volume "128-Bit and 256-Bit Media Instructions" and the
175 : * "Immediate Byte Usage Unique to the SSE instructions" for the information
176 : * about four-operand and five-operand instructions.
177 : */
178 :
179 : /*
180 : * Instruction with two-bit immediate can only change these two bits and
181 : * immediate/displacement.
182 : */
183 13 : if ((info_new & IMMEDIATE_2BIT) == IMMEDIATE_2BIT)
184 0 : return memcmp(begin_new, begin_existing,
185 : instruction_length -
186 : INFO_ANYFIELDS_SIZE(info_new) - 1) == 0 &&
187 : (end_new[-1] & 0xfc) == (end_existing[-1] & 0xfc);
188 :
189 : /* Instruction's last byte is not immediate, thus it must be unchanged. */
190 13 : if (info_new & LAST_BYTE_IS_NOT_IMMEDIATE)
191 0 : return memcmp(begin_new, begin_existing,
192 : instruction_length -
193 : INFO_ANYFIELDS_SIZE(info_new) - 1) == 0 &&
194 : end_new[-1] == end_existing[-1];
195 :
196 : /*
197 : * Normal instruction can only change an anyfied: immediate, displacement or
198 : * relative offset.
199 : */
200 13 : return memcmp(begin_new, begin_existing,
201 : instruction_length - INFO_ANYFIELDS_SIZE(info_new)) == 0;
202 339 : }
203 :
204 : static NaClValidationStatus ValidatorCodeReplacement_x86_64(
205 13 : uintptr_t guest_addr,
206 13 : uint8_t *data_existing,
207 13 : uint8_t *data_new,
208 13 : size_t size,
209 13 : const NaClCPUFeatures *f) {
210 : /* TODO(jfb) Use a safe cast here. */
211 13 : NaClCPUFeaturesX86 *cpu_features = (NaClCPUFeaturesX86 *) f;
212 26 : UNREFERENCED_PARAMETER(guest_addr);
213 :
214 13 : if (size & kBundleMask)
215 0 : return NaClValidationFailed;
216 13 : if (ValidateChunkAMD64(data_new, size, CALL_USER_CALLBACK_ON_EACH_INSTRUCTION,
217 : cpu_features, ProcessCodeReplacementInstruction,
218 : (void *)(data_existing - data_new)))
219 7 : return NaClValidationSucceeded;
220 6 : if (errno == ENOMEM)
221 0 : return NaClValidationFailedOutOfMemory;
222 6 : return NaClValidationFailed;
223 13 : }
224 :
225 : static const struct NaClValidatorInterface validator = {
226 : FALSE, /* Optional stubout_mode is not implemented. */
227 : TRUE, /* Optional readonly_text mode is implemented. */
228 : TRUE, /* Optional code replacement functions are implemented. */
229 : ApplyDfaValidator_x86_64,
230 : ValidatorCodeCopy_x86_64,
231 : ValidatorCodeReplacement_x86_64,
232 : sizeof(NaClCPUFeaturesX86),
233 : NaClSetAllCPUFeaturesX86,
234 : NaClGetCurrentCPUFeaturesX86,
235 : NaClFixCPUFeaturesX86,
236 : };
237 :
238 : const struct NaClValidatorInterface *NaClDfaValidatorCreate_x86_64(void) {
239 304 : return &validator;
240 : }
|