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 #include <assert.h> | |
8 #include <stdio.h> | |
9 #include <stddef.h> | |
10 #include <stdlib.h> | |
11 #include <string.h> | |
12 | |
13 #include "native_client/src/include/elf32.h" | |
14 #include "native_client/src/include/elf64.h" | |
15 #include "native_client/src/shared/platform/nacl_check.h" | |
16 #include "native_client/src/shared/utils/types.h" | |
17 #include "native_client/src/trusted/validator_ragel/unreviewed/validator_interna
l.h" | |
18 | |
19 /* This is a copy of NaClLog from shared/platform/nacl_log.c to avoid | |
20 * linking in code in NaCl shared code in the unreviewed/Makefile and be able to | |
21 * use CHECK(). | |
22 | |
23 * TODO(khim): remove the copy of NaClLog implementation as soon as | |
24 * unreviewed/Makefile is eliminated. | |
25 */ | |
26 void NaClLog(int detail_level, char const *fmt, ...) { | |
27 va_list ap; | |
28 | |
29 UNREFERENCED_PARAMETER(detail_level); | |
30 va_start(ap, fmt); | |
31 vfprintf(stderr, fmt, ap); | |
32 exit(1); | |
33 } | |
34 | |
35 static void CheckBounds(const unsigned char *data, size_t data_size, | |
36 const void *ptr, size_t inside_size) { | |
37 CHECK(data <= (const unsigned char *) ptr); | |
38 CHECK((const unsigned char *) ptr + inside_size <= data + data_size); | |
39 } | |
40 | |
41 void ReadImage(const char *filename, uint8_t **result, size_t *result_size) { | |
42 FILE *fp; | |
43 uint8_t *data; | |
44 size_t file_size; | |
45 size_t got; | |
46 | |
47 fp = fopen(filename, "rb"); | |
48 if (fp == NULL) { | |
49 fprintf(stderr, "Failed to open input file: %s\n", filename); | |
50 exit(1); | |
51 } | |
52 /* Find the file size. */ | |
53 fseek(fp, 0, SEEK_END); | |
54 file_size = ftell(fp); | |
55 data = malloc(file_size); | |
56 if (data == NULL) { | |
57 fprintf(stderr, "Unable to create memory image of input file: %s\n", | |
58 filename); | |
59 exit(1); | |
60 } | |
61 fseek(fp, 0, SEEK_SET); | |
62 got = fread(data, 1, file_size, fp); | |
63 if (got != file_size) { | |
64 fprintf(stderr, "Unable to read data from input file: %s\n", | |
65 filename); | |
66 exit(1); | |
67 } | |
68 fclose(fp); | |
69 | |
70 *result = data; | |
71 *result_size = file_size; | |
72 } | |
73 | |
74 struct ValidateState { | |
75 uint8_t width; | |
76 const uint8_t *offset; | |
77 }; | |
78 | |
79 Bool ProcessError(const uint8_t *begin, const uint8_t *end, | |
80 uint32_t error_code, void *userdata) { | |
81 size_t offset = begin - (((struct ValidateState *)userdata)->offset); | |
82 UNREFERENCED_PARAMETER(end); | |
83 | |
84 if (error_code & UNRECOGNIZED_INSTRUCTION) | |
85 printf("offset 0x%"NACL_PRIxS": unrecognized instruction\n", offset); | |
86 if (error_code & DIRECT_JUMP_OUT_OF_RANGE) | |
87 printf("offset 0x%"NACL_PRIxS": direct jump out of range\n", offset); | |
88 if (error_code & CPUID_UNSUPPORTED_INSTRUCTION) | |
89 printf("offset 0x%"NACL_PRIxS": required CPU feature not found\n", offset); | |
90 if (error_code & FORBIDDEN_BASE_REGISTER) | |
91 printf("offset 0x%"NACL_PRIxS": improper memory address - bad base\n", | |
92 offset); | |
93 if (error_code & UNRESTRICTED_INDEX_REGISTER) | |
94 printf("offset 0x%"NACL_PRIxS": improper memory address - bad index\n", | |
95 offset); | |
96 if ((error_code & BAD_RSP_RBP_PROCESSING_MASK) == RESTRICTED_RBP_UNPROCESSED) | |
97 printf("offset 0x%"NACL_PRIxS": improper %%rbp sandboxing\n", offset); | |
98 if ((error_code & BAD_RSP_RBP_PROCESSING_MASK) == UNRESTRICTED_RBP_PROCESSED) | |
99 printf("offset 0x%"NACL_PRIxS": improper %%rbp sandboxing\n", offset); | |
100 if ((error_code & BAD_RSP_RBP_PROCESSING_MASK) == RESTRICTED_RSP_UNPROCESSED) | |
101 printf("offset 0x%"NACL_PRIxS": improper %%rsp sandboxing\n", offset); | |
102 if ((error_code & BAD_RSP_RBP_PROCESSING_MASK) == UNRESTRICTED_RSP_PROCESSED) | |
103 printf("offset 0x%"NACL_PRIxS": improper %%rsp sandboxing\n", offset); | |
104 if (error_code & R15_MODIFIED) | |
105 printf("offset 0x%"NACL_PRIxS": error - %%r15 is changed\n", offset); | |
106 if (error_code & BPL_MODIFIED) | |
107 printf("offset 0x%"NACL_PRIxS": error - %%bpl or %%bp is changed\n", | |
108 offset); | |
109 if (error_code & SPL_MODIFIED) | |
110 printf("offset 0x%"NACL_PRIxS": error - %%spl or %%sp is changed\n", | |
111 offset); | |
112 if (error_code & BAD_JUMP_TARGET) | |
113 printf("bad jump to around 0x%"NACL_PRIxS"\n", offset); | |
114 if (error_code & (VALIDATION_ERRORS_MASK | BAD_JUMP_TARGET)) | |
115 return FALSE; | |
116 else | |
117 return TRUE; | |
118 } | |
119 | |
120 Bool ProcessErrorOrWarning(const uint8_t *begin, const uint8_t *end, | |
121 uint32_t error_code, void *userdata) { | |
122 size_t offset = begin - (((struct ValidateState *)userdata)->offset); | |
123 UNREFERENCED_PARAMETER(end); | |
124 | |
125 if (error_code & BAD_CALL_ALIGNMENT) | |
126 printf("offset 0x%"NACL_PRIxS": warning - bad call alignment\n", offset); | |
127 return ProcessError(begin, end, error_code, userdata); | |
128 } | |
129 | |
130 Bool ValidateElf32(const uint8_t *data, size_t data_size, | |
131 Bool warnings, | |
132 uint32_t options, | |
133 const NaClCPUFeaturesX86 *cpu_features) { | |
134 Elf32_Ehdr *header; | |
135 int index; | |
136 | |
137 header = (Elf32_Ehdr *) data; | |
138 CheckBounds(data, data_size, header, sizeof *header); | |
139 assert(memcmp(header->e_ident, ELFMAG, strlen(ELFMAG)) == 0); | |
140 | |
141 for (index = 0; index < header->e_shnum; ++index) { | |
142 Elf32_Shdr *section = (Elf32_Shdr *) (data + header->e_shoff + | |
143 header->e_shentsize * index); | |
144 CheckBounds(data, data_size, section, sizeof *section); | |
145 | |
146 if ((section->sh_flags & SHF_EXECINSTR) != 0) { | |
147 struct ValidateState state; | |
148 Bool res; | |
149 | |
150 state.offset = data + section->sh_offset - section->sh_addr; | |
151 if (section->sh_size <= 0xfff) { | |
152 state.width = 4; | |
153 } else if (section->sh_size <= 0xfffffff) { | |
154 state.width = 8; | |
155 } else { | |
156 state.width = 12; | |
157 } | |
158 CheckBounds(data, data_size, | |
159 data + section->sh_offset, section->sh_size); | |
160 res = ValidateChunkIA32(data + section->sh_offset, section->sh_size, | |
161 options, cpu_features, | |
162 warnings ? ProcessErrorOrWarning : ProcessError, | |
163 &state); | |
164 if (!res) | |
165 return res; | |
166 } | |
167 } | |
168 return TRUE; | |
169 } | |
170 | |
171 Bool ValidateElf64(const uint8_t *data, size_t data_size, | |
172 Bool warnings, | |
173 uint32_t options, | |
174 const NaClCPUFeaturesX86 *cpu_features) { | |
175 Elf64_Ehdr *header; | |
176 int index; | |
177 | |
178 header = (Elf64_Ehdr *) data; | |
179 CheckBounds(data, data_size, header, sizeof *header); | |
180 assert(memcmp(header->e_ident, ELFMAG, strlen(ELFMAG)) == 0); | |
181 | |
182 for (index = 0; index < header->e_shnum; ++index) { | |
183 Elf64_Shdr *section = (Elf64_Shdr *) (data + header->e_shoff + | |
184 header->e_shentsize * index); | |
185 CheckBounds(data, data_size, section, sizeof *section); | |
186 | |
187 if ((section->sh_flags & SHF_EXECINSTR) != 0) { | |
188 struct ValidateState state; | |
189 Bool res; | |
190 | |
191 state.offset = data + section->sh_offset - section->sh_addr; | |
192 if (section->sh_size <= 0xfff) { | |
193 state.width = 4; | |
194 } else if (section->sh_size <= 0xfffffff) { | |
195 state.width = 8; | |
196 } else if (section->sh_size <= 0xfffffffffffLL) { | |
197 state.width = 12; | |
198 } else { | |
199 state.width = 16; | |
200 } | |
201 CheckBounds(data, data_size, | |
202 data + section->sh_offset, (size_t)section->sh_size); | |
203 res = ValidateChunkAMD64(data + section->sh_offset, | |
204 (size_t)section->sh_size, | |
205 options, cpu_features, | |
206 warnings ? ProcessErrorOrWarning : ProcessError, | |
207 &state); | |
208 if (!res) | |
209 return res; | |
210 } | |
211 } | |
212 return TRUE; | |
213 } | |
214 | |
215 Bool ValidateElf(const char *filename, | |
216 const uint8_t *data, size_t data_size, | |
217 Bool warnings, | |
218 uint32_t options, | |
219 const NaClCPUFeaturesX86 *cpu_features) { | |
220 if (data[4] == 1) { | |
221 return ValidateElf32(data, data_size, warnings, options, cpu_features); | |
222 } else if (data[4] == 2) { | |
223 return ValidateElf64(data, data_size, warnings, options, cpu_features); | |
224 } else { | |
225 printf("Unknown ELF class: %s\n", filename); | |
226 exit(1); | |
227 } | |
228 } | |
229 | |
230 void ProcessFile(const char *filename, | |
231 int repeat_count, | |
232 int raw_bitness, | |
233 Bool warnings, | |
234 uint32_t options, | |
235 const NaClCPUFeaturesX86 *cpu_features) { | |
236 size_t data_size; | |
237 uint8_t *data; | |
238 int count; | |
239 | |
240 ReadImage(filename, &data, &data_size); | |
241 | |
242 for (count = 0; count < repeat_count; ++count) { | |
243 Bool rc = FALSE; | |
244 if (raw_bitness == 0) | |
245 rc = ValidateElf(filename, | |
246 data, data_size, | |
247 warnings, | |
248 options, | |
249 cpu_features); | |
250 else if (raw_bitness == 32) { | |
251 struct ValidateState state; | |
252 state.offset = data; | |
253 CHECK(data_size % kBundleSize == 0); | |
254 rc = ValidateChunkIA32(data, data_size, | |
255 options, cpu_features, | |
256 warnings ? ProcessErrorOrWarning : ProcessError, | |
257 &state); | |
258 } | |
259 else if (raw_bitness == 64) { | |
260 struct ValidateState state; | |
261 state.offset = data; | |
262 CHECK(data_size % kBundleSize == 0); | |
263 rc = ValidateChunkAMD64(data, data_size, | |
264 options, cpu_features, | |
265 warnings ? ProcessErrorOrWarning : ProcessError, | |
266 &state); | |
267 } | |
268 if (!rc) { | |
269 printf("file '%s' can not be fully validated\n", filename); | |
270 exit(1); | |
271 } | |
272 } | |
273 } | |
274 | |
275 int main(int argc, char **argv) { | |
276 int index, initial_index = 1, repeat_count = 1; | |
277 const NaClCPUFeaturesX86 *cpu_features = &kFullCPUIDFeatures; | |
278 int raw_bitness = 0; | |
279 Bool warnings = FALSE; | |
280 uint32_t options = 0; | |
281 | |
282 if (argc == 1) { | |
283 printf("%s: no input files\n", argv[0]); | |
284 return 2; | |
285 } | |
286 while (initial_index < argc) { | |
287 char *arg = argv[initial_index]; | |
288 if (!strcmp(arg, "--compatible")) { | |
289 cpu_features = &kValidatorCPUIDFeatures; | |
290 initial_index++; | |
291 } else if (!strcmp(argv[initial_index], "--warnings")) { | |
292 warnings = TRUE; | |
293 options |= CALL_USER_CALLBACK_ON_EACH_INSTRUCTION; | |
294 initial_index++; | |
295 } else if (!strcmp(arg, "--nobundles")) { | |
296 options |= PROCESS_CHUNK_AS_A_CONTIGUOUS_STREAM; | |
297 initial_index++; | |
298 } else if (!strcmp(arg, "--repeat")) { | |
299 if (initial_index+1 >= argc) { | |
300 printf("%s: no integer after --repeat\n", argv[0]); | |
301 return 2; | |
302 } | |
303 repeat_count = atoi(argv[initial_index + 1]); | |
304 initial_index += 2; | |
305 } else if (!strcmp(argv[initial_index], "--raw32")) { | |
306 raw_bitness = 32; | |
307 initial_index++; | |
308 } else if (!strcmp(argv[initial_index], "--raw64")) { | |
309 raw_bitness = 64; | |
310 initial_index++; | |
311 } else { | |
312 break; | |
313 } | |
314 } | |
315 for (index = initial_index; index < argc; ++index) { | |
316 const char *filename = argv[index]; | |
317 ProcessFile(filename, repeat_count, raw_bitness, warnings, options, | |
318 cpu_features); | |
319 } | |
320 return 0; | |
321 } | |
OLD | NEW |