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 : #include <stddef.h>
8 : #include <stdio.h>
9 : #include <stdlib.h>
10 : #include <string.h>
11 :
12 : #include <set>
13 : #include <string>
14 : #include <vector>
15 :
16 : #include "native_client/src/include/elf.h"
17 : #include "native_client/src/include/elf_constants.h"
18 : #include "native_client/src/include/nacl_macros.h"
19 : #include "native_client/src/include/portability_io.h"
20 : #include "native_client/src/shared/platform/nacl_check.h"
21 : #include "native_client/src/shared/utils/types.h"
22 : #include "native_client/src/trusted/cpu_features/arch/arm/cpu_arm.h"
23 : #include "native_client/src/trusted/validator/driver/elf_load.h"
24 : #include "native_client/src/trusted/validator_arm/problem_reporter.h"
25 : #include "native_client/src/trusted/validator_arm/validator.h"
26 : #include "native_client/src/trusted/validator_ragel/validator.h"
27 :
28 : using std::set;
29 : using std::string;
30 : using std::vector;
31 :
32 : using nacl_arm_val::CodeSegment;
33 : using nacl_arm_val::ProblemReporter;
34 : using nacl_arm_val::SfiValidator;
35 :
36 : using elf_load::Segment;
37 :
38 :
39 : struct Jump {
40 : uint32_t from;
41 : uint32_t to;
42 1 : Jump(uint32_t from, uint32_t to) : from(from), to(to) {}
43 : };
44 :
45 :
46 : struct Error {
47 : uint32_t offset;
48 : string message;
49 1 : Error(uint32_t offset, string message) : offset(offset), message(message) {}
50 : };
51 :
52 :
53 : struct UserData {
54 : Segment segment;
55 : vector<Error> *errors;
56 : vector<Jump> *jumps;
57 : set<uint32_t> *bad_jump_targets;
58 : UserData(Segment segment,
59 : vector<Error> *errors,
60 : vector<Jump> *jumps,
61 : set<uint32_t> *bad_jump_targets)
62 : : segment(segment),
63 : errors(errors),
64 : jumps(jumps),
65 1 : bad_jump_targets(bad_jump_targets) {
66 1 : }
67 : };
68 :
69 :
70 : Bool ProcessInstruction(
71 : const uint8_t *begin, const uint8_t *end,
72 1 : uint32_t validation_info, void *user_data_ptr) {
73 :
74 1 : UserData &user_data = *reinterpret_cast<UserData *>(user_data_ptr);
75 1 : vector<Error> &errors = *user_data.errors;
76 :
77 : uint32_t offset = user_data.segment.vaddr +
78 1 : static_cast<uint32_t>(begin - user_data.segment.data);
79 :
80 : // Presence of RELATIVE_8BIT or RELATIVE_32BIT indicates that instruction is
81 : // immediate jump or call.
82 : // We only record jumps that are in range, so we don't have to worry
83 : // about overflows.
84 1 : if ((validation_info & DIRECT_JUMP_OUT_OF_RANGE) == 0) {
85 : uint32_t program_counter = user_data.segment.vaddr +
86 : static_cast<uint32_t>(end -
87 1 : user_data.segment.data);
88 1 : if (validation_info & RELATIVE_8BIT) {
89 1 : program_counter += reinterpret_cast<const int8_t *>(end)[-1];
90 1 : user_data.jumps->push_back(Jump(offset, program_counter));
91 1 : } else if (validation_info & RELATIVE_32BIT) {
92 1 : program_counter += reinterpret_cast<const int32_t *>(end)[-1];
93 1 : user_data.jumps->push_back(Jump(offset, program_counter));
94 : }
95 : }
96 :
97 : Bool result = (validation_info & (VALIDATION_ERRORS_MASK | BAD_JUMP_TARGET))
98 : ? FALSE
99 1 : : TRUE;
100 :
101 : // We clear bit for each processed error, and in the end if there are still
102 : // errors we did not report, we produce generic error message.
103 : // This way, if validator interface was changed (new error bits were
104 : // introduced), we at least wouldn't ignore them completely.
105 :
106 : // TODO(shcherbina): deal with code duplication somehow.
107 :
108 1 : if (validation_info & UNRECOGNIZED_INSTRUCTION) {
109 1 : validation_info &= ~UNRECOGNIZED_INSTRUCTION;
110 1 : errors.push_back(Error(offset, "unrecognized instruction"));
111 : }
112 :
113 1 : if (validation_info & DIRECT_JUMP_OUT_OF_RANGE) {
114 1 : validation_info &= ~DIRECT_JUMP_OUT_OF_RANGE;
115 1 : errors.push_back(Error(offset, "direct jump out of range"));
116 : }
117 :
118 1 : if (validation_info & CPUID_UNSUPPORTED_INSTRUCTION) {
119 0 : validation_info &= ~CPUID_UNSUPPORTED_INSTRUCTION;
120 0 : errors.push_back(Error(offset, "required CPU feature not found"));
121 : }
122 :
123 1 : if (validation_info & FORBIDDEN_BASE_REGISTER) {
124 1 : validation_info &= ~FORBIDDEN_BASE_REGISTER;
125 1 : errors.push_back(Error(offset, "improper memory address - bad base"));
126 : }
127 :
128 1 : if (validation_info & UNRESTRICTED_INDEX_REGISTER) {
129 1 : validation_info &= ~UNRESTRICTED_INDEX_REGISTER;
130 1 : errors.push_back(Error(offset, "improper memory address - bad index"));
131 : }
132 :
133 : if ((validation_info & BAD_RSP_RBP_PROCESSING_MASK) ==
134 1 : RESTRICTED_RBP_UNPROCESSED) {
135 1 : validation_info &= ~BAD_RSP_RBP_PROCESSING_MASK;
136 1 : errors.push_back(Error(offset, "improper %rbp sandboxing"));
137 : }
138 :
139 : if ((validation_info & BAD_RSP_RBP_PROCESSING_MASK) ==
140 1 : UNRESTRICTED_RBP_PROCESSED) {
141 1 : validation_info &= ~BAD_RSP_RBP_PROCESSING_MASK;
142 1 : errors.push_back(Error(offset, "improper %rbp sandboxing"));
143 : }
144 :
145 : if ((validation_info & BAD_RSP_RBP_PROCESSING_MASK) ==
146 1 : RESTRICTED_RSP_UNPROCESSED) {
147 1 : validation_info &= ~BAD_RSP_RBP_PROCESSING_MASK;
148 1 : errors.push_back(Error(offset, "improper %rsp sandboxing"));
149 : }
150 :
151 : if ((validation_info & BAD_RSP_RBP_PROCESSING_MASK) ==
152 1 : UNRESTRICTED_RSP_PROCESSED) {
153 1 : validation_info &= ~BAD_RSP_RBP_PROCESSING_MASK;
154 1 : errors.push_back(Error(offset, "improper %rsp sandboxing"));
155 : }
156 :
157 1 : if (validation_info & R15_MODIFIED) {
158 1 : validation_info &= ~R15_MODIFIED;
159 1 : errors.push_back(Error(offset, "error - %r15 is changed"));
160 : }
161 :
162 1 : if (validation_info & BP_MODIFIED) {
163 1 : validation_info &= ~BP_MODIFIED;
164 1 : errors.push_back(Error(offset, "error - %bpl or %bp is changed"));
165 : }
166 :
167 1 : if (validation_info & SP_MODIFIED) {
168 1 : validation_info &= ~SP_MODIFIED;
169 1 : errors.push_back(Error(offset, "error - %spl or %sp is changed"));
170 : }
171 :
172 1 : if (validation_info & BAD_CALL_ALIGNMENT) {
173 1 : validation_info &= ~BAD_CALL_ALIGNMENT;
174 1 : errors.push_back(Error(offset, "warning - bad call alignment"));
175 : }
176 :
177 1 : if (validation_info & BAD_JUMP_TARGET) {
178 1 : validation_info &= ~BAD_JUMP_TARGET;
179 1 : user_data.bad_jump_targets->insert(offset);
180 : }
181 :
182 1 : if (validation_info & VALIDATION_ERRORS_MASK) {
183 0 : errors.push_back(Error(offset, "<some other error>"));
184 : }
185 :
186 1 : return result;
187 1 : }
188 :
189 :
190 : Bool ProcessError(
191 : const uint8_t *begin, const uint8_t *end,
192 1 : uint32_t validation_info, void *user_data_ptr) {
193 : UNREFERENCED_PARAMETER(begin);
194 : UNREFERENCED_PARAMETER(end);
195 : UNREFERENCED_PARAMETER(validation_info);
196 : UNREFERENCED_PARAMETER(user_data_ptr);
197 1 : return FALSE;
198 1 : }
199 :
200 :
201 : typedef Bool ValidateChunkFunc(
202 : const uint8_t *data, size_t size,
203 : uint32_t options,
204 : const NaClCPUFeaturesX86 *cpu_features,
205 : ValidationCallbackFunc user_callback,
206 : void *callback_data);
207 :
208 :
209 : bool ValidateX86(
210 : const Segment &segment,
211 : ValidateChunkFunc validate_chunk,
212 1 : vector<Error> *errors) {
213 :
214 1 : errors->clear();
215 :
216 1 : if (segment.vaddr % kBundleSize != 0) {
217 : errors->push_back(Error(
218 : segment.vaddr,
219 0 : "Text segment offset in memory is not bundle-aligned."));
220 0 : return false;
221 : }
222 :
223 1 : if (segment.size % kBundleSize != 0) {
224 : char buf[100];
225 : SNPRINTF(buf, sizeof buf,
226 : "Text segment size (0x%" NACL_PRIx32 ") is not "
227 : "multiple of bundle size.",
228 0 : segment.size);
229 0 : errors->push_back(Error(segment.vaddr + segment.size, buf));
230 0 : return false;
231 : }
232 :
233 1 : vector<Jump> jumps;
234 1 : set<uint32_t> bad_jump_targets;
235 :
236 1 : UserData user_data(segment, errors, &jumps, &bad_jump_targets);
237 :
238 : // TODO(shcherbina): customize from command line
239 :
240 : // We collect all errors except bad jump targets, and we separately
241 : // memoize all direct jumps (or calls) and bad jump targets.
242 : Bool result = validate_chunk(
243 : segment.data, segment.size,
244 : CALL_USER_CALLBACK_ON_EACH_INSTRUCTION, &kFullCPUIDFeatures,
245 1 : ProcessInstruction, &user_data);
246 :
247 : // Report destinations of jumps that lead to bad targets.
248 1 : for (size_t i = 0; i < jumps.size(); i++) {
249 1 : const Jump &jump = jumps[i];
250 1 : if (bad_jump_targets.count(jump.to) > 0)
251 1 : errors->push_back(Error(jump.from, "bad jump target"));
252 1 : }
253 :
254 : // Run validator as it is run in loader, without callback on each instruction,
255 : // and ensure that results match. If ncval is guaranteed to return the same
256 : // result as actual validator, it can be reliably used as validator wrapper
257 : // for testing.
258 : CHECK(result == validate_chunk(
259 : segment.data, segment.size,
260 : 0, &kFullCPUIDFeatures,
261 1 : ProcessError, NULL));
262 :
263 1 : return static_cast<bool>(result);
264 1 : }
265 :
266 :
267 : class NcvalArmProblemReporter : public ProblemReporter {
268 : public:
269 0 : explicit NcvalArmProblemReporter(vector<Error> *errors) : errors(errors) {}
270 :
271 : protected:
272 : void ReportProblemMessage(nacl_arm_dec::Violation violation,
273 : uint32_t vaddr,
274 0 : const char* message) {
275 : UNREFERENCED_PARAMETER(violation);
276 0 : if (errors->size() < max_errors) {
277 0 : errors->push_back(Error(vaddr, message));
278 0 : } else if (errors->size() == max_errors) {
279 : errors->push_back(
280 0 : Error(vaddr, "Too may errors: supressing remaining errors."));
281 : }
282 0 : }
283 :
284 : private:
285 : vector<Error> *errors;
286 : // Maximum number of errors to generate.
287 : static const size_t max_errors = 5000;
288 : };
289 :
290 :
291 0 : bool ValidateArm(const Segment &segment, vector<Error> *errors) {
292 0 : errors->clear();
293 :
294 0 : vector<CodeSegment> segments;
295 0 : segments.push_back(CodeSegment(segment.data, segment.vaddr, segment.size));
296 :
297 : NaClCPUFeaturesArm cpu_features;
298 0 : NaClClearCPUFeaturesArm(&cpu_features);
299 : // TODO(shcherbina): add support for '--allow-conditional-memory-access' flag
300 : // with the effect of
301 : // NaClSetCPUFeatureArm(&cpu_features, NaClCPUFeatureArm_CanUseTstMem, 1);
302 : // Alternatively, use approach described in
303 : // http://code.google.com/p/nativeclient/issues/detail?id=3254
304 :
305 : SfiValidator validator(
306 : 16, // bytes per bundle
307 : // TODO(cbiffle): maybe check region sizes from ELF headers?
308 : // verify that instructions are in right region
309 : 1U << 30, // code region size
310 : 1U << 30, // data region size
311 : nacl_arm_dec::RegisterList(nacl_arm_dec::Register::Tp()),
312 : nacl_arm_dec::RegisterList(nacl_arm_dec::Register::Sp()),
313 0 : &cpu_features);
314 :
315 0 : NcvalArmProblemReporter reporter(errors);
316 0 : return validator.validate(segments, &reporter);
317 0 : }
318 :
319 :
320 0 : void Usage() {
321 0 : printf("Usage:\n");
322 0 : printf(" ncval <ELF file>\n");
323 0 : exit(1);
324 : }
325 :
326 :
327 : struct Options {
328 : const char *input_file;
329 : };
330 :
331 :
332 1 : void ParseOptions(size_t argc, const char * const *argv, Options *options) {
333 1 : if (argc != 2)
334 0 : Usage();
335 :
336 1 : options->input_file = argv[argc - 1];
337 1 : }
338 :
339 :
340 1 : int main(int argc, char **argv) {
341 : Options options;
342 1 : ParseOptions(argc, argv, &options);
343 :
344 1 : elf_load::Image image;
345 1 : elf_load::ReadImage(options.input_file, &image);
346 :
347 1 : elf_load::Architecture architecture = elf_load::GetElfArch(image);
348 1 : Segment segment = elf_load::GetElfTextSegment(image);
349 :
350 1 : vector<Error> errors;
351 1 : bool result = false;
352 1 : switch (architecture) {
353 : case elf_load::X86_32:
354 1 : result = ValidateX86(segment, ValidateChunkIA32, &errors);
355 1 : break;
356 : case elf_load::X86_64:
357 1 : result = ValidateX86(segment, ValidateChunkAMD64, &errors);
358 1 : break;
359 : case elf_load::ARM:
360 0 : result = ValidateArm(segment, &errors);
361 0 : break;
362 : default:
363 0 : CHECK(false);
364 : }
365 :
366 1 : for (size_t i = 0; i < errors.size(); i++) {
367 1 : const Error &e = errors[i];
368 1 : printf("%8" NACL_PRIx32 ": %s\n", e.offset, e.message.c_str());
369 1 : }
370 :
371 1 : if (result) {
372 1 : printf("Valid.\n");
373 1 : return 0;
374 : } else {
375 1 : printf("Invalid.\n");
376 1 : return 1;
377 : }
378 1 : }
|