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 /* | |
8 * ncval.c - command line validator for NaCl. | |
9 * Mostly for testing. | |
10 */ | |
11 | |
12 | |
13 #ifndef NACL_TRUSTED_BUT_NOT_TCB | |
14 #error("This file is not meant for use in the TCB") | |
15 #endif | |
16 | |
17 #include "native_client/src/include/portability.h" | |
18 | |
19 #include <stdio.h> | |
20 #include <stdlib.h> | |
21 #include <stdarg.h> | |
22 #include <string.h> | |
23 #include <errno.h> | |
24 #include <sys/timeb.h> | |
25 #include <time.h> | |
26 #include "native_client/src/include/nacl_macros.h" | |
27 #include "native_client/src/shared/gio/gio.h" | |
28 #include "native_client/src/shared/platform/nacl_log.h" | |
29 #include "native_client/src/shared/utils/flags.h" | |
30 #include "native_client/src/trusted/validator/ncfileutil.h" | |
31 #include "native_client/src/trusted/cpu_features/arch/x86/cpu_x86.h" | |
32 #include "native_client/src/trusted/validator/x86/ncval_reg_sfi/ncvalidate_iter.
h" | |
33 #include "native_client/src/trusted/validator/x86/ncval_reg_sfi/ncvalidate_iter_
internal.h" | |
34 #include "native_client/src/trusted/validator/x86/ncval_reg_sfi/ncvalidate_iter_
detailed.h" | |
35 #include "native_client/src/trusted/validator/x86/ncval_reg_sfi/nc_jumps.h" | |
36 #include "native_client/src/trusted/validator/x86/ncval_reg_sfi/nc_opcode_histog
ram.h" | |
37 #include "native_client/src/trusted/validator/x86/ncval_reg_sfi/nc_memory_protec
t.h" | |
38 #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncdecode.h" | |
39 #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncdecode_verbose
.h" | |
40 #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.h" | |
41 #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate_detai
led.h" | |
42 #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate_inter
naltypes.h" | |
43 #include "native_client/src/trusted/validator_x86/nc_read_segment.h" | |
44 #include "native_client/src/trusted/validator_x86/ncdis_segments.h" | |
45 | |
46 #if NACL_TARGET_SUBARCH == 64 | |
47 #include "native_client/src/trusted/validator/x86/decoder/nc_decode_tables.h" | |
48 #include "native_client/src/trusted/validator/x86/ncval_reg_sfi/ncval_decode_tab
les.h" | |
49 #include "native_client/src/trusted/validator_x86/ncdis_decode_tables.h" | |
50 #endif | |
51 | |
52 /* To turn on debugging of instruction decoding, change value of | |
53 * DEBUGGING to 1. | |
54 */ | |
55 #define DEBUGGING 0 | |
56 | |
57 #include "native_client/src/shared/utils/debugging.h" | |
58 | |
59 /* Forward declarations. */ | |
60 static void usage(int exit_code); | |
61 | |
62 #if NACL_TARGET_SUBARCH == 32 | |
63 /* Flag defining if statistics should be printed for callback validator | |
64 * model. | |
65 */ | |
66 static Bool NACL_FLAGS_stats_print = FALSE; | |
67 #endif | |
68 | |
69 /* Flag defining if detailed error messages should be generated. When | |
70 * false, runs performance model as used by sel_ldr. | |
71 */ | |
72 static Bool NACL_FLAGS_detailed_errors = TRUE; | |
73 | |
74 /* Flag defining the name of a hex text to be used as the code segment. | |
75 */ | |
76 static char *NACL_FLAGS_hex_text = ""; | |
77 | |
78 /* Define if we should process segments (rather than sections) when applying SFI | |
79 * validator. | |
80 */ | |
81 static Bool NACL_FLAGS_analyze_segments = FALSE; | |
82 | |
83 /* Define how many times we will analyze the code segment. | |
84 * Note: Values, other than 1, is only used for profiling. | |
85 */ | |
86 static int NACL_FLAGS_validate_attempts = 1; | |
87 | |
88 /* Define the set of CPU features to use while validating. */ | |
89 static NaClCPUFeaturesX86 ncval_cpu_features; | |
90 | |
91 /* Define whether timing should be applied when running the validator. */ | |
92 static Bool NACL_FLAGS_print_timing = FALSE; | |
93 | |
94 /* Define what level of errors will be printed. | |
95 * Note: If multiple flags are true, the one with | |
96 * the highest severity will be selected. | |
97 */ | |
98 static Bool NACL_FLAGS_warnings = FALSE; | |
99 static Bool NACL_FLAGS_errors = FALSE; | |
100 static Bool NACL_FLAGS_fatal = FALSE; | |
101 | |
102 /* Define if special stubout tests should be run. Such | |
103 * tests apply stubout, and then print out the modified | |
104 * disassembled code. | |
105 */ | |
106 static Bool NACL_FLAGS_stubout_memory = FALSE; | |
107 | |
108 #if NACL_TARGET_SUBARCH == 64 | |
109 /* Flag that allows validator decoder to be used in place of full decoder. | |
110 */ | |
111 static Bool NACL_FLAGS_validator_decoder = FALSE; | |
112 #endif | |
113 | |
114 /* Generates NaClErrorMapping for error level suffix. */ | |
115 #define NaClError(s) { #s , LOG_## s} | |
116 | |
117 /************************************************** | |
118 * Driver to apply the validator to a code segment | |
119 *************************************************/ | |
120 | |
121 /* Reports if named module is safe. */ | |
122 static void NaClReportFileSafety(Bool success, const char *fname) { | |
123 if (NACL_FLAGS_stubout_memory) { | |
124 /* The validator has been run to test stubbing out. Stubbing out, | |
125 * in this tool, means replacing instructions (modeled using hex | |
126 * text) that are unsafe and rejected by the validator, and are | |
127 * replaced with HALT instructions. | |
128 */ | |
129 NaClLog(LOG_INFO, "STUBBED OUT as follows:\n"); | |
130 } else { | |
131 #ifndef NCVAL_TESTING | |
132 if (success) { | |
133 NaClLog(LOG_INFO, "*** %s is safe ***\n", fname); | |
134 } else { | |
135 NaClLog(LOG_INFO, "*** %s IS UNSAFE ***\n", fname); | |
136 } | |
137 #endif | |
138 } | |
139 } | |
140 | |
141 /* Reports if module is safe. */ | |
142 static void NaClReportSafety(Bool success, | |
143 const char* filename) { | |
144 if (0 != strcmp(NACL_FLAGS_hex_text, "")) { | |
145 /* Special hex text processing, rather than filename. */ | |
146 if (0 == strcmp(NACL_FLAGS_hex_text, "-")) { | |
147 NaClReportFileSafety(success, "<input>"); | |
148 return; | |
149 } | |
150 } | |
151 NaClReportFileSafety(success, filename); | |
152 } | |
153 | |
154 /* The model of data to be passed to the load/analyze steps. */ | |
155 typedef void* NaClRunValidatorData; | |
156 | |
157 /* The routine that loads the code segment(s) into memory (within | |
158 * the data arg). Returns true iff load was successful. | |
159 */ | |
160 typedef Bool (*NaClValidateLoad)(int argc, const char* argv[], | |
161 NaClRunValidatorData data); | |
162 | |
163 /* The actual validation analysis, applied to the data returned by | |
164 * ValidateLoad. Assume that this function also deallocates any memory | |
165 * in loaded_data. Returns true iff analysis doesn't find any problems. | |
166 */ | |
167 typedef Bool (*NaClValidateAnalyze)(NaClRunValidatorData data); | |
168 | |
169 /* Runs the validator using the given (command line) arguments. | |
170 * | |
171 * Parameters: | |
172 * data - The model of data to be passed to the load/analyze steps. | |
173 * Allows top-level call to pass control information | |
174 * to the load and analyze functions, and between these | |
175 * two functions. | |
176 * load - The function to load in the data needed to validate. | |
177 * analyze - The function to call to do validator analysis once | |
178 * the data has been read in. | |
179 */ | |
180 static Bool NaClRunValidator(int argc, const char* argv[], | |
181 NaClRunValidatorData data, | |
182 NaClValidateLoad load, | |
183 NaClValidateAnalyze analyze) { | |
184 clock_t clock_0; | |
185 clock_t clock_l; | |
186 clock_t clock_v; | |
187 Bool return_value; | |
188 | |
189 clock_0 = clock(); | |
190 return_value = load(argc, argv, data); | |
191 if (!return_value) { | |
192 NaClValidatorMessage(LOG_ERROR, NULL, "Unable to load code to validate\n"); | |
193 return FALSE; | |
194 } | |
195 clock_l = clock(); | |
196 return_value = analyze(data); | |
197 clock_v = clock(); | |
198 | |
199 if (NACL_FLAGS_print_timing) { | |
200 NaClValidatorMessage( | |
201 LOG_INFO, NULL, | |
202 "load time: %0.6f analysis time: %0.6f\n", | |
203 (double)(clock_l - clock_0) / (double)CLOCKS_PER_SEC, | |
204 (double)(clock_v - clock_l) / (double)CLOCKS_PER_SEC); | |
205 } | |
206 return return_value; | |
207 } | |
208 | |
209 /* Default loader that does nothing. Typically this is because | |
210 * the data argument already contains the bytes to validate. | |
211 */ | |
212 Bool NaClValidateNoLoad(int argc, const char* argv[], | |
213 NaClRunValidatorData data) { | |
214 return TRUE; | |
215 } | |
216 | |
217 /* Local file data for validator run. */ | |
218 typedef struct ValidateData { | |
219 /* The name of the elf file to validate. */ | |
220 const char *fname; | |
221 /* The elf file to validate. */ | |
222 ncfile *ncf; | |
223 } ValidateData; | |
224 | |
225 /* Load the elf file and return the loaded elf file. */ | |
226 static Bool ValidateElfLoad(int argc, const char *argv[], | |
227 ValidateData *data) { | |
228 if (argc != 2) { | |
229 NaClLog(LOG_ERROR, "expected: %s file\n", argv[0]); | |
230 usage(2); | |
231 } | |
232 data->fname = argv[1]; | |
233 | |
234 /* TODO(karl): Once we fix elf values put in by compiler, so that | |
235 * we no longer get load errors from ncfilutil.c, find a way to | |
236 * terminate early if errors occur during loading. | |
237 */ | |
238 data->ncf = nc_loadfile(data->fname); | |
239 if (data->ncf == NULL) { | |
240 NaClLog(LOG_ERROR, "nc_loadfile(%s): %s\n", data->fname, strerror(errno)); | |
241 usage(2); | |
242 } | |
243 return NULL != data->ncf; | |
244 } | |
245 | |
246 /*************************************************** | |
247 * Code to run SFI validator on hex text examples. * | |
248 ***************************************************/ | |
249 | |
250 /* Defines the maximum number of characters allowed on an input line | |
251 * of the input text defined by the commands command line option. | |
252 */ | |
253 #define NACL_MAX_INPUT_LINE 4096 | |
254 | |
255 typedef struct NaClValidatorByteArray { | |
256 uint8_t bytes[NACL_MAX_INPUT_LINE]; | |
257 NaClPcAddress base; | |
258 NaClMemorySize num_bytes; | |
259 } NaClValidatorByteArray; | |
260 | |
261 static void HexFatal(const char *format, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); | |
262 | |
263 /* Print out given error message about the hex input file, and then exit. */ | |
264 static void HexFatal(const char *format, ...) { | |
265 va_list ap; | |
266 va_start(ap, format); | |
267 NaClLogV(LOG_ERROR, format, ap); | |
268 va_end(ap); | |
269 /* always succed, so that the testing framework works. */ | |
270 exit(0); | |
271 } | |
272 | |
273 /* Load the hex input file from the given command line arguments, | |
274 * and put it into the given data structure. | |
275 */ | |
276 static int ValidateHexLoad(int argc, const char *argv[], | |
277 NaClValidatorByteArray *data) { | |
278 if (argc != 1) { | |
279 HexFatal("expected: %s <options>\n", argv[0]); | |
280 } | |
281 if (0 == strcmp(NACL_FLAGS_hex_text, "-")) { | |
282 data->num_bytes = (NaClMemorySize) | |
283 NaClReadHexTextWithPc(stdin, &data->base, data->bytes, | |
284 NACL_MAX_INPUT_LINE); | |
285 } else { | |
286 FILE *input = fopen(NACL_FLAGS_hex_text, "r"); | |
287 if (NULL == input) { | |
288 HexFatal("Can't open hex text file: %s\n", NACL_FLAGS_hex_text); | |
289 } | |
290 data->num_bytes = (NaClMemorySize) | |
291 NaClReadHexTextWithPc(input, &data->base, data->bytes, | |
292 NACL_MAX_INPUT_LINE); | |
293 fclose(input); | |
294 --argc; | |
295 } | |
296 return argc; | |
297 } | |
298 | |
299 #if NACL_TARGET_SUBARCH == 32 | |
300 /*************************************** | |
301 * Code to run segment based validator.* | |
302 ***************************************/ | |
303 | |
304 int AnalyzeSegmentSections(ncfile *ncf, struct NCValidatorState *vstate) { | |
305 int badsections = 0; | |
306 int ii; | |
307 const Elf_Phdr *phdr = ncf->pheaders; | |
308 | |
309 for (ii = 0; ii < ncf->phnum; ii++) { | |
310 NaClLog(LOG_INFO, | |
311 "segment[%d] p_type %"NACL_PRIdElf_Word | |
312 " p_offset %"NACL_PRIxElf_Off" vaddr %"NACL_PRIxElf_Addr | |
313 " paddr %"NACL_PRIxElf_Addr" align %"NACL_PRIuElf_Xword"\n", | |
314 ii, phdr[ii].p_type, phdr[ii].p_offset, | |
315 phdr[ii].p_vaddr, phdr[ii].p_paddr, | |
316 phdr[ii].p_align); | |
317 | |
318 NaClLog(LOG_INFO, | |
319 " filesz %"NACL_PRIxElf_Xword" memsz %"NACL_PRIxElf_Xword | |
320 " flags %"NACL_PRIxElf_Word"\n", | |
321 phdr[ii].p_filesz, phdr[ii].p_memsz, phdr[ii].p_flags); | |
322 if ((PT_LOAD != phdr[ii].p_type) || | |
323 (0 == (phdr[ii].p_flags & PF_X))) | |
324 continue; | |
325 NaClLog(LOG_INFO, "parsing segment %d\n", ii); | |
326 NCValidateSegment(ncf->data + (phdr[ii].p_vaddr - ncf->vbase), | |
327 phdr[ii].p_vaddr, phdr[ii].p_memsz, vstate); | |
328 } | |
329 return -badsections; | |
330 } | |
331 | |
332 /* Initialize segment validator, using detailed (summary) error | |
333 * messages if selected. | |
334 */ | |
335 struct NCValidatorState* NCValInit(const NaClPcAddress vbase, | |
336 const NaClMemorySize codesize) { | |
337 return NACL_FLAGS_detailed_errors | |
338 ? NCValidateInitDetailed(vbase, codesize, &ncval_cpu_features) | |
339 : NCValidateInit(vbase, codesize, FALSE, &ncval_cpu_features); | |
340 } | |
341 | |
342 | |
343 static Bool AnalyzeSegmentCodeSegments(ncfile *ncf, const char *fname) { | |
344 NaClPcAddress vbase, vlimit; | |
345 struct NCValidatorState *vstate; | |
346 Bool result; | |
347 | |
348 GetVBaseAndLimit(ncf, &vbase, &vlimit); | |
349 vstate = NCValInit(vbase, vlimit - vbase); | |
350 if (vstate == NULL) return FALSE; | |
351 NCValidateSetErrorReporter(vstate, &kNCVerboseErrorReporter); | |
352 if (AnalyzeSegmentSections(ncf, vstate) < 0) { | |
353 NaClLog(LOG_INFO, "%s: text validate failed\n", fname); | |
354 } | |
355 result = (0 == NCValidateFinish(vstate)) ? TRUE : FALSE; | |
356 NaClReportSafety(result, fname); | |
357 if (NACL_FLAGS_stats_print) NCStatsPrint(vstate); | |
358 NCValidateFreeState(&vstate); | |
359 NaClLog(LOG_INFO, "Validated %s\n", fname); | |
360 return result; | |
361 } | |
362 #endif | |
363 | |
364 #if NACL_TARGET_SUBARCH == 64 | |
365 /****************************** | |
366 * Code to run SFI validator. * | |
367 ******************************/ | |
368 | |
369 /* Define what should be used as the base register for | |
370 * memory accesses. | |
371 */ | |
372 static NaClOpKind nacl_base_register = | |
373 (64 == NACL_TARGET_SUBARCH ? RegR15 : RegUnknown); | |
374 | |
375 /* Create validator state using detailed (summary) error messages | |
376 * if selected. | |
377 */ | |
378 struct NaClValidatorState* NaClValStateCreate( | |
379 const NaClPcAddress vbase, | |
380 const NaClMemorySize sz, | |
381 const NaClOpKind base_register) { | |
382 return | |
383 NACL_FLAGS_detailed_errors | |
384 ? NaClValidatorStateCreateDetailed(vbase, sz, base_register, | |
385 &ncval_cpu_features) | |
386 : NaClValidatorStateCreate(vbase, sz, base_register, FALSE, | |
387 &ncval_cpu_features); | |
388 } | |
389 | |
390 /* Returns the decoder tables to use. */ | |
391 static const NaClDecodeTables* NaClGetDecoderTables(void) { | |
392 return NACL_FLAGS_validator_decoder | |
393 ? kNaClValDecoderTables | |
394 : kNaClDecoderTables; | |
395 } | |
396 | |
397 /* Analyze each section in the given elf file, using the given validator | |
398 * state. | |
399 */ | |
400 static void AnalyzeSfiSections(ncfile *ncf, struct NaClValidatorState *vstate) { | |
401 int ii; | |
402 const Elf_Phdr *phdr = ncf->pheaders; | |
403 | |
404 for (ii = 0; ii < ncf->phnum; ii++) { | |
405 /* TODO(karl) fix types for this? */ | |
406 NaClValidatorMessage( | |
407 LOG_INFO, vstate, | |
408 "segment[%d] p_type %d p_offset %"NACL_PRIxElf_Off | |
409 " vaddr %"NACL_PRIxElf_Addr | |
410 " paddr %"NACL_PRIxElf_Addr | |
411 " align %"NACL_PRIuElf_Xword"\n", | |
412 ii, phdr[ii].p_type, phdr[ii].p_offset, | |
413 phdr[ii].p_vaddr, phdr[ii].p_paddr, | |
414 phdr[ii].p_align); | |
415 NaClValidatorMessage( | |
416 LOG_INFO, vstate, | |
417 " filesz %"NACL_PRIxElf_Xword | |
418 " memsz %"NACL_PRIxElf_Xword | |
419 " flags %"NACL_PRIxElf_Word"\n", | |
420 phdr[ii].p_filesz, phdr[ii].p_memsz, | |
421 phdr[ii].p_flags); | |
422 if ((PT_LOAD != phdr[ii].p_type) || | |
423 (0 == (phdr[ii].p_flags & PF_X))) | |
424 continue; | |
425 NaClValidatorMessage(LOG_INFO, vstate, "parsing segment %d\n", ii); | |
426 NaClValidateSegmentUsingTables(ncf->data + (phdr[ii].p_vaddr - ncf->vbase), | |
427 phdr[ii].p_vaddr, phdr[ii].p_memsz, vstate, | |
428 NaClGetDecoderTables()); | |
429 } | |
430 } | |
431 | |
432 static void AnalyzeSfiSegments(ncfile *ncf, NaClValidatorState *state) { | |
433 int ii; | |
434 const Elf_Shdr *shdr = ncf->sheaders; | |
435 | |
436 for (ii = 0; ii < ncf->shnum; ii++) { | |
437 NaClValidatorMessage( | |
438 LOG_INFO, state, | |
439 "section %d sh_addr %"NACL_PRIxElf_Addr" offset %"NACL_PRIxElf_Off | |
440 " flags %"NACL_PRIxElf_Xword"\n", | |
441 ii, shdr[ii].sh_addr, shdr[ii].sh_offset, shdr[ii].sh_flags); | |
442 if ((shdr[ii].sh_flags & SHF_EXECINSTR) != SHF_EXECINSTR) | |
443 continue; | |
444 NaClValidatorMessage(LOG_INFO, state, "parsing section %d\n", ii); | |
445 NaClValidateSegmentUsingTables(ncf->data + (shdr[ii].sh_addr - ncf->vbase), | |
446 shdr[ii].sh_addr, shdr[ii].sh_size, state, | |
447 NaClGetDecoderTables()); | |
448 } | |
449 } | |
450 | |
451 /* Analyze each code segment in the given elf file, stored in the | |
452 * file with the given path fname. | |
453 */ | |
454 static Bool AnalyzeSfiCodeSegments(ncfile *ncf, const char *fname) { | |
455 /* TODO(karl) convert these to be PcAddress and MemorySize */ | |
456 NaClPcAddress vbase, vlimit; | |
457 NaClValidatorState *vstate; | |
458 Bool return_value = TRUE; | |
459 | |
460 GetVBaseAndLimit(ncf, &vbase, &vlimit); | |
461 vstate = NaClValStateCreate(vbase, vlimit - vbase, nacl_base_register); | |
462 if (vstate == NULL) { | |
463 NaClValidatorMessage(LOG_ERROR, vstate, "Unable to create validator state"); | |
464 return FALSE; | |
465 } | |
466 NaClValidatorStateSetErrorReporter(vstate, &kNaClVerboseErrorReporter); | |
467 if (NACL_FLAGS_analyze_segments) { | |
468 AnalyzeSfiSegments(ncf, vstate); | |
469 } else { | |
470 AnalyzeSfiSections(ncf, vstate); | |
471 } | |
472 return_value = NaClValidatesOk(vstate); | |
473 NaClReportSafety(return_value, fname); | |
474 NaClValidatorStateDestroy(vstate); | |
475 return return_value; | |
476 } | |
477 #endif | |
478 | |
479 /*************************** | |
480 * Top-level driver code. * | |
481 **************************/ | |
482 | |
483 /* Analyze the code segments of the elf file in the validator date. */ | |
484 static Bool ValidateAnalyze(ValidateData *data) { | |
485 int i; | |
486 Bool results = TRUE; | |
487 for (i = 0; i < NACL_FLAGS_validate_attempts; ++i) { | |
488 Bool return_value = | |
489 #if NACL_TARGET_SUBARCH == 64 | |
490 AnalyzeSfiCodeSegments(data->ncf, data->fname); | |
491 #else | |
492 AnalyzeSegmentCodeSegments(data->ncf, data->fname); | |
493 #endif | |
494 if (!return_value) { | |
495 results = FALSE; | |
496 } | |
497 } | |
498 nc_freefile(data->ncf); | |
499 return results; | |
500 } | |
501 | |
502 /* Define a sequence of bytes to validate whose virtual address begins at the | |
503 * given base. | |
504 */ | |
505 typedef struct NaClValidateBytes { | |
506 /* The sequence of bytes to validate. */ | |
507 uint8_t* bytes; | |
508 /* The number of bytes in the sequence. */ | |
509 NaClMemorySize num_bytes; | |
510 /* The virtual base adddress associated with the first byte in the | |
511 * sequence. | |
512 */ | |
513 NaClPcAddress base; | |
514 } NaClValidateBytes; | |
515 | |
516 /* Apply the validator to the sequence of bytes in the given data. */ | |
517 static Bool NaClValidateAnalyzeBytes(NaClValidateBytes* data) { | |
518 Bool return_value = FALSE; | |
519 #if NACL_TARGET_SUBARCH == 64 | |
520 NaClValidatorState* state; | |
521 state = NaClValStateCreate(data->base, | |
522 data->num_bytes, | |
523 nacl_base_register); | |
524 if (NULL == state) { | |
525 NaClValidatorMessage(LOG_ERROR, NULL, "Unable to create validator state"); | |
526 return FALSE; | |
527 } | |
528 if (NACL_FLAGS_stubout_memory) { | |
529 NaClValidatorStateSetDoStubOut(state, TRUE); | |
530 } | |
531 NaClValidatorStateSetErrorReporter(state, &kNaClVerboseErrorReporter); | |
532 NaClValidateSegmentUsingTables(data->bytes, data->base, data->num_bytes, | |
533 state, NaClGetDecoderTables()); | |
534 return_value = NaClValidatesOk(state); | |
535 if (state->did_stub_out) { | |
536 /* Used for golden file testing. */ | |
537 printf("Some instructions were replaced with HLTs.\n"); | |
538 } | |
539 NaClValidatorStateDestroy(state); | |
540 NaClReportSafety(return_value, ""); | |
541 #else | |
542 struct NCValidatorState *vstate; | |
543 vstate = NCValInit(data->base, data->num_bytes); | |
544 if (vstate == NULL) { | |
545 printf("Unable to create validator state, quitting!\n"); | |
546 } else { | |
547 if (NACL_FLAGS_stubout_memory) { | |
548 NCValidateSetStubOutMode(vstate, 1); | |
549 } | |
550 NCValidateSetErrorReporter(vstate, &kNCVerboseErrorReporter); | |
551 NCValidateSegment(&data->bytes[0], data->base, data->num_bytes, vstate); | |
552 return_value = (0 == NCValidateFinish(vstate)) ? TRUE : FALSE; | |
553 if (vstate->stats.didstubout) { | |
554 /* Used for golden file testing. */ | |
555 printf("Some instructions were replaced with HLTs.\n"); | |
556 } | |
557 NaClReportSafety(return_value, ""); | |
558 if (NACL_FLAGS_stats_print) NCStatsPrint(vstate); | |
559 NCValidateFreeState(&vstate); | |
560 } | |
561 #endif | |
562 return return_value; | |
563 } | |
564 | |
565 /* Given the command line arguments in argc/argv, and the sequence of bytes | |
566 * in the given data, apply the validator to the given bytes. | |
567 */ | |
568 Bool NaClRunValidatorBytes(int argc, | |
569 const char* argv[], | |
570 uint8_t* bytes, | |
571 NaClMemorySize num_bytes, | |
572 NaClPcAddress base) { | |
573 NaClValidateBytes data; | |
574 int i; | |
575 Bool results = TRUE; | |
576 data.bytes = bytes; | |
577 data.num_bytes = num_bytes; | |
578 data.base = base; | |
579 for (i = 0; i < NACL_FLAGS_validate_attempts; ++i) { | |
580 if (!NaClRunValidator(argc, argv, &data, | |
581 (NaClValidateLoad) NaClValidateNoLoad, | |
582 (NaClValidateAnalyze) NaClValidateAnalyzeBytes)) { | |
583 results = FALSE; | |
584 } | |
585 } | |
586 return results; | |
587 } | |
588 | |
589 | |
590 static const char usage_str[] = | |
591 "usage: ncval [options] file\n" | |
592 "\n" | |
593 "\tValidates an x86-%d nexe file.\n" | |
594 "\n" | |
595 "Options are:\n" | |
596 "\n" | |
597 #ifdef NCVAL_TESTING | |
598 "--print_conditions\n" | |
599 "\tPrint all pre/post conditions, including NaCl illegal instructions.\n" | |
600 #endif | |
601 "--annotate\n" | |
602 "\tRun validator using annotations that will be understood\n" | |
603 "\tby ncval_annotate.py.\n" | |
604 "--attempts=N\n" | |
605 "\tRun the validator on the nexe file (after loading) N times.\n" | |
606 "\tNote: this flag should only be used for profiling.\n" | |
607 "--CLFLUSH\n" | |
608 "\tModel a CPU that supports the clflush instruction.\n" | |
609 "--CMOV\n" | |
610 "\tModel a CPU that supports the cmov instructions.\n" | |
611 #ifdef NCVAL_TESTING | |
612 "--conds\n" | |
613 "\tPrint out pre/post conditions associated with each instruction.\n" | |
614 #endif | |
615 "--cpuid-all\n" | |
616 "\tModel a CPU that supports all available features.\n" | |
617 "--cpuid-none\n" | |
618 "\tModel a CPU that supports no avaliable features.\n" | |
619 "--CX8\n" | |
620 "\tModel a CPU that supports the cmpxchg8b instruction.\n" | |
621 "--detailed\n" | |
622 "\tPrint out detailed error messages, rather than use performant\n" | |
623 "\tcode used by sel_ldr\n" | |
624 "--errors\n" | |
625 "\tPrint out error and fatal error messages, but not\n" | |
626 "\tinformative and warning messages\n" | |
627 "--fatal\n" | |
628 "\tOnly print out fatal error messages.\n" | |
629 "--FXSR\n" | |
630 "\tModel a CPU that supports the sfence instructions.\n" | |
631 "--help\n" | |
632 "\tPrint this message, and then exit.\n" | |
633 "--hex_text=<file>\n" | |
634 "\tRead text file of hex bytes, and use that\n" | |
635 "\tas the definition of the code segment. Note: -hex_text=- will\n" | |
636 "\tread from stdin instead of a file.\n" | |
637 #if NACL_TARGET_SUBARCH == 64 | |
638 "--histogram\n" | |
639 "\tPrint out a histogram of found opcodes.\n" | |
640 "--identity_mask\n" | |
641 "\tMask jumps using 0xFF instead of one matching\n" | |
642 "\tthe block alignment.\n" | |
643 #endif | |
644 "--local_cpuid\n" | |
645 "\tSet cpuid to values defined by local host this command is run on.\n" | |
646 "--LZCNT\n" | |
647 "\tModel a CPU that supports the lzcnt instruction.\n" | |
648 #if NACL_TARGET_SUBARCH == 64 | |
649 "--max_errors=N\n" | |
650 "\tPrint out at most N error messages. If N is zero,\n" | |
651 "\tno messages are printed. If N is < 0, all messages are printed.\n" | |
652 #endif | |
653 "--MMX\n" | |
654 "\tModel a CPU that supports MMX instructions.\n" | |
655 "--MOVBE\n" | |
656 "\tModel a CPU that supports the movbe instruction.\n" | |
657 "--POPCNT\n" | |
658 "\tModel a CPU that supports the popcnt instruction.\n" | |
659 #if NACL_TARGET_SUBARCH == 64 | |
660 "--readwrite_sfi\n" | |
661 "\tCheck for memory read and write software fault isolation.\n" | |
662 "--segments\n" | |
663 "\tAnalyze code in segments in elf file, instead of headers.\n" | |
664 #endif | |
665 "--SSE\n" | |
666 "\tModel a CPU that supports SSE instructions.\n" | |
667 "--SSE2\n" | |
668 "\tModel a CPU that supports SSE 2 instructions.\n" | |
669 "--SSE3\n" | |
670 "\tModel a CPU that supports SSE 3 instructions.\n" | |
671 "--SSSE3\n" | |
672 "\tModel a CPU that supports SSSE 3 instructions.\n" | |
673 "--SSE41\n" | |
674 "\tModel a CPU that supports SSE 4.1 instructions.\n" | |
675 "--SSE42\n" | |
676 "\tModel a CPU that supports SSE 4.2 instructions.\n" | |
677 "--SSE4A\n" | |
678 "\tModel a CPU that supports SSE 4A instructions.\n" | |
679 #if NACL_TARGET_SUBARCH == 32 | |
680 "--stats\n" | |
681 "\tPrint statistics collected by the validator.\n" | |
682 #endif | |
683 "--stubout\n" | |
684 "\tRun using stubout mode, replacing bad instructions with halts.\n" | |
685 "\tStubbed out disassembly will be printed after validator\n" | |
686 "\terror messages. Note: Only applied if --hex_text option is\n" | |
687 "\talso specified\n" | |
688 "-t\n" | |
689 "\tTime the validator and print out results.\n" | |
690 "--TSC\n" | |
691 "\tModel a CPU that supports the rdtsc instruction.\n" | |
692 "--x87\n" | |
693 "\tModel a CPU that supports x87 instructions.\n" | |
694 #if NACL_TARGET_SUBARCH == 64 | |
695 "--validator_decoder\n" | |
696 "\tUse validator (partial) decoder instead of full decoder.\n" | |
697 #endif | |
698 "--3DNOW\n" | |
699 "\tModel a CPU that supports 3DNOW instructions.\n" | |
700 "--E3DNOW\n" | |
701 "\tModel a CPU that supports E3DNOW instructions.\n" | |
702 "\n" | |
703 "--time\n" | |
704 "\tTime the validator and print out results. Same as option -t.\n" | |
705 #if NACL_TARGET_SUBARCH == 64 | |
706 "--trace_insts\n" | |
707 "\tTurn on validator trace of instructions, as processed..\n" | |
708 "--trace_verbose\n" | |
709 "\tTurn on all trace validator messages. Note: this\n" | |
710 "\tflag also implies --trace.\n" | |
711 #endif | |
712 "--warnings\n" | |
713 "\tPrint out warnings, errors, and fatal errors, but not\n" | |
714 "\tinformative messages.\n" | |
715 #if NACL_TARGET_SUBARCH == 64 | |
716 "--write_sfi\n" | |
717 "\tOnly check for memory write software fault isolation.\n" | |
718 #endif | |
719 "\n"; | |
720 | |
721 static void usage(int exit_code) { | |
722 printf(usage_str, NACL_TARGET_SUBARCH); | |
723 exit(exit_code); | |
724 } | |
725 | |
726 /* Checks if arg is one of the expected "Bool" flags, and if so, sets | |
727 * the corresponding flag and returns true. | |
728 */ | |
729 static Bool GrokABoolFlag(const char *arg) { | |
730 /* A set of boolean flags to be checked */ | |
731 static struct { | |
732 const char *flag_name; | |
733 Bool *flag_ptr; | |
734 } flags[] = { | |
735 { "--segments" , &NACL_FLAGS_analyze_segments }, | |
736 #ifdef NCVAL_TESTING | |
737 { "--print_all_conds", &NACL_FLAGS_report_conditions_on_all }, | |
738 #else | |
739 { "--detailed", &NACL_FLAGS_detailed_errors }, | |
740 #endif | |
741 { "--stubout", &NACL_FLAGS_stubout_memory }, | |
742 #if NACL_TARGET_SUBARCH == 64 | |
743 { "--trace_insts", &NACL_FLAGS_validator_trace_instructions }, | |
744 #endif | |
745 { "-t", &NACL_FLAGS_print_timing }, | |
746 #if NACL_TARGET_SUBARCH == 32 | |
747 { "--stats", &NACL_FLAGS_stats_print }, | |
748 #endif | |
749 { "--annotate", &NACL_FLAGS_ncval_annotate }, | |
750 #if NACL_TARGET_SUBARCH == 64 | |
751 { "--histogram", &NACL_FLAGS_opcode_histogram }, | |
752 #endif | |
753 { "--time" , &NACL_FLAGS_print_timing }, | |
754 #if NACL_TARGET_SUBARCH == 64 | |
755 { "--warnings", &NACL_FLAGS_warnings }, | |
756 { "--errors" , &NACL_FLAGS_errors }, | |
757 { "--fatal" , &NACL_FLAGS_fatal }, | |
758 { "--validator_decoder", &NACL_FLAGS_validator_decoder }, | |
759 #endif | |
760 { "--identity_mask", &NACL_FLAGS_identity_mask }, | |
761 }; | |
762 | |
763 /* A set of CPU features to check. */ | |
764 static struct { | |
765 const char *feature_name; | |
766 NaClCPUFeatureX86ID feature; | |
767 } features[] = { | |
768 { "--x87" , NaClCPUFeatureX86_x87 }, | |
769 { "--MMX" , NaClCPUFeatureX86_MMX }, | |
770 { "--SSE" , NaClCPUFeatureX86_SSE }, | |
771 { "--SSE2" , NaClCPUFeatureX86_SSE2 }, | |
772 { "--SSE3" , NaClCPUFeatureX86_SSE3 }, | |
773 { "--SSSE3" , NaClCPUFeatureX86_SSSE3 }, | |
774 { "--SSE41" , NaClCPUFeatureX86_SSE41 }, | |
775 { "--SSE42" , NaClCPUFeatureX86_SSE42 }, | |
776 { "--MOVBE" , NaClCPUFeatureX86_MOVBE }, | |
777 { "--POPCNT" , NaClCPUFeatureX86_POPCNT }, | |
778 { "--CX8" , NaClCPUFeatureX86_CX8 }, | |
779 { "--CX16" , NaClCPUFeatureX86_CX16 }, | |
780 { "--CMOV" , NaClCPUFeatureX86_CMOV }, | |
781 { "--MON" , NaClCPUFeatureX86_MON }, | |
782 { "--FXSR" , NaClCPUFeatureX86_FXSR }, | |
783 { "--CLFLUSH", NaClCPUFeatureX86_CLFLUSH }, | |
784 { "--TSC" , NaClCPUFeatureX86_TSC }, | |
785 { "--3DNOW" , NaClCPUFeatureX86_3DNOW }, | |
786 { "--EMMX" , NaClCPUFeatureX86_EMMX }, | |
787 { "--E3DNOW" , NaClCPUFeatureX86_E3DNOW }, | |
788 { "--LZCNT" , NaClCPUFeatureX86_LZCNT }, | |
789 { "--SSE4A" , NaClCPUFeatureX86_SSE4A }, | |
790 { "--LM" , NaClCPUFeatureX86_LM }, | |
791 }; | |
792 | |
793 int i; | |
794 Bool flag_state; | |
795 for (i = 0; i < NACL_ARRAY_SIZE(flags); ++i) { | |
796 if (GrokBoolFlag(flags[i].flag_name, arg, flags[i].flag_ptr)) { | |
797 return TRUE; | |
798 } | |
799 } | |
800 for (i = 0; i < NACL_ARRAY_SIZE(features); ++i) { | |
801 if (GrokBoolFlag(features[i].feature_name, arg, &flag_state)) { | |
802 NaClSetCPUFeatureX86(&ncval_cpu_features, features[i].feature, | |
803 flag_state); | |
804 return TRUE; | |
805 } | |
806 } | |
807 | |
808 return FALSE; | |
809 } | |
810 | |
811 /* Checks if arg is one of the expected "int" flags, and if so, sets | |
812 * the corresponding flag and returns true. | |
813 */ | |
814 static Bool GrokAnIntFlag(const char *arg) { | |
815 /* A set of boolean flags to be checked */ | |
816 static struct { | |
817 const char *flag_name; | |
818 int *flag_ptr; | |
819 } flags[] = { | |
820 { "--max_errors", &NACL_FLAGS_max_reported_errors}, | |
821 { "--attempts" , &NACL_FLAGS_validate_attempts }, | |
822 }; | |
823 int i; | |
824 for (i = 0; i < NACL_ARRAY_SIZE(flags); ++i) { | |
825 if (GrokIntFlag(flags[i].flag_name, arg, flags[i].flag_ptr)) { | |
826 return TRUE; | |
827 } | |
828 } | |
829 return FALSE; | |
830 } | |
831 | |
832 static int GrokFlags(int argc, const char *argv[]) { | |
833 int i; | |
834 int new_argc; | |
835 Bool help = FALSE; | |
836 #if NACL_TARGET_SUBARCH == 64 | |
837 Bool only_write_sandbox; | |
838 #endif | |
839 if (argc == 0) return 0; | |
840 new_argc = 1; | |
841 for (i = 1; i < argc; ++i) { | |
842 Bool flag; | |
843 const char *arg = argv[i]; | |
844 if (GrokABoolFlag(arg) || | |
845 GrokAnIntFlag(arg) || | |
846 GrokCstringFlag("--hex_text", arg, &NACL_FLAGS_hex_text)) { | |
847 /* Valid processed flag, continue to next flag. */ | |
848 } else if (GrokBoolFlag("--help", arg, &help)) { | |
849 usage(0); | |
850 } | |
851 #if NACL_TARGET_SUBARCH == 64 | |
852 else if (0 == strcmp("--trace_verbose", arg)) { | |
853 NaClValidatorFlagsSetTraceVerbose(); | |
854 } else if (GrokBoolFlag("--write_sfi", arg, &only_write_sandbox)) { | |
855 NACL_FLAGS_read_sandbox = !only_write_sandbox; | |
856 } else if (GrokBoolFlag("--readwrite_sfi", arg, | |
857 &NACL_FLAGS_read_sandbox)) { | |
858 } | |
859 #endif | |
860 else if (0 == strcmp("--cpuid-all", arg)) { | |
861 NaClSetAllCPUFeaturesX86((NaClCPUFeatures *) &ncval_cpu_features); | |
862 } else if (0 == strcmp("--cpuid-none", arg)) { | |
863 NaClClearCPUFeaturesX86(&ncval_cpu_features); | |
864 } else if (GrokBoolFlag("--local_cpuid", arg, &flag)) { | |
865 NaClGetCurrentCPUFeaturesX86((NaClCPUFeatures *) &ncval_cpu_features); | |
866 } else { | |
867 argv[new_argc++] = argv[i]; | |
868 } | |
869 } | |
870 | |
871 /* Before returning, update internals to match command line | |
872 * values found. | |
873 */ | |
874 if (NACL_FLAGS_warnings) { | |
875 NaClLogSetVerbosity(LOG_WARNING); | |
876 } | |
877 if (NACL_FLAGS_errors) { | |
878 NaClLogSetVerbosity(LOG_ERROR); | |
879 } | |
880 if (NACL_FLAGS_fatal) { | |
881 NaClLogSetVerbosity(LOG_FATAL); | |
882 } | |
883 NCValidatorSetMaxDiagnostics(NACL_FLAGS_max_reported_errors); | |
884 if (NACL_FLAGS_stubout_memory && (NACL_FLAGS_validate_attempts != 1)) { | |
885 fprintf(stderr, "Can't specify --stubout when --attempts!=1\n"); | |
886 } | |
887 | |
888 return new_argc; | |
889 } | |
890 | |
891 /* Decode and print out code segment if stubout memory is specified | |
892 * command line. | |
893 */ | |
894 static void NaClMaybeDecodeDataSegment( | |
895 uint8_t *mbase, NaClPcAddress vbase, NaClMemorySize size) { | |
896 if (NACL_FLAGS_stubout_memory) { | |
897 /* Disassemble data segment to see how halts were inserted. | |
898 * Note: We use the full decoder (rather than the validator decoder) | |
899 * because the validator decoders are partial decodings, and can be | |
900 * confusing to the reader. | |
901 */ | |
902 NaClDisassembleSegment(mbase, vbase, size, | |
903 NACL_DISASSEMBLE_FLAG(NaClDisassembleFull)); | |
904 } | |
905 } | |
906 | |
907 int main(int argc, const char *argv[]) { | |
908 int result = 0; | |
909 struct GioFile gio_out_stream; | |
910 struct Gio *gout = (struct Gio*) &gio_out_stream; | |
911 | |
912 if (argc < 2) { | |
913 fprintf(stderr, "expected: %s file\n", argv[0]); | |
914 usage(1); | |
915 } | |
916 | |
917 /* Set up logging. */ | |
918 if (!GioFileRefCtor(&gio_out_stream, stdout)) { | |
919 fprintf(stderr, "Unable to create gio file for stdout!\n"); | |
920 return 1; | |
921 } | |
922 NaClLogModuleInitExtended(LOG_INFO, gout); | |
923 NaClLogDisableTimestamp(); | |
924 | |
925 /* By default, assume no local cpu features are available. */ | |
926 NaClClearCPUFeaturesX86(&ncval_cpu_features); | |
927 | |
928 /* Validate the specified input. */ | |
929 argc = GrokFlags(argc, argv); | |
930 if (0 == strcmp(NACL_FLAGS_hex_text, "")) { | |
931 /* Run validator on elf file. */ | |
932 ValidateData data; | |
933 Bool success = NaClRunValidator( | |
934 argc, argv, &data, | |
935 (NaClValidateLoad) ValidateElfLoad, | |
936 (NaClValidateAnalyze) ValidateAnalyze); | |
937 NaClReportSafety(success, argv[1]); | |
938 result = (success ? 0 : 1); | |
939 } else { | |
940 /* Run validator on hex text file. */ | |
941 NaClValidatorByteArray data; | |
942 argc = ValidateHexLoad(argc, argv, &data); | |
943 NaClRunValidatorBytes( | |
944 argc, argv, (uint8_t*) &data.bytes, | |
945 data.num_bytes, data.base); | |
946 NaClMaybeDecodeDataSegment(&data.bytes[0], data.base, data.num_bytes); | |
947 /* always succeed, so that the testing framework works. */ | |
948 result = 0; | |
949 } | |
950 | |
951 NaClLogModuleFini(); | |
952 GioFileDtor(gout); | |
953 return result; | |
954 } | |
OLD | NEW |