| 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 |