Chromium Code Reviews| Index: tests/untrusted_crash_dump/untrusted_crash_dump.c |
| diff --git a/tests/untrusted_crash_dump/untrusted_crash_dump.c b/tests/untrusted_crash_dump/untrusted_crash_dump.c |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d53e3d4f1ebcf88a169e135842e9cbf861227cc1 |
| --- /dev/null |
| +++ b/tests/untrusted_crash_dump/untrusted_crash_dump.c |
| @@ -0,0 +1,284 @@ |
| +/* |
| + * Copyright (c) 2012 The Native Client Authors. All rights reserved. |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +#include <assert.h> |
| +#include <inttypes.h> |
| +#include <pthread.h> |
| +#include <stdio.h> |
| +#include <stdlib.h> |
| +#include <string.h> |
| +#include <sys/mman.h> |
| +#include <sys/nacl_syscalls.h> |
| + |
| +#ifdef __GLIBC__ |
| +#include <elf.h> |
| +#include <link.h> |
| +#endif /* __GLIBC__ */ |
| + |
| +#include "native_client/src/untrusted/nacl/syscall_bindings_trampoline.h" |
| +#include "native_client/tests/untrusted_crash_dump/untrusted_crash_dump.h" |
| + |
| + |
| +#define CRASH_PAGE_CHUNK (64 * 1024) |
| +#define CRASH_STACK_SIZE (CRASH_PAGE_CHUNK * 4) |
| +#define CRASH_STACK_GUARD_SIZE CRASH_PAGE_CHUNK |
| +#define CRASH_STACK_COMPLETE_SIZE (CRASH_STACK_GUARD_SIZE + CRASH_STACK_SIZE) |
| + |
| + |
| +static void (*g_PrevHandler)(int prog_ctr, int stack_ptr) = 0; |
| +static pthread_key_t g_CrashStackKey; |
| +static char *g_ProgramName = 0; |
| + |
| + |
| +#ifdef __GLIBC__ |
| + |
| +struct ProgramTableData { |
| + FILE *core; |
| + uintptr_t addr; |
| + int first; |
| +}; |
| + |
| + |
| +static int PrintSectionsOne( |
| + struct dl_phdr_info *info, size_t size, void *data) { |
| + int i; |
| + struct ProgramTableData *ptd = (struct ProgramTableData*)data; |
|
Mark Seaborn
2012/02/13 19:04:08
Nit: spacing style
bradn
2012/02/13 23:42:03
Done.
|
| + |
| + if (ptd->first) { |
| + ptd->first = 0; |
| + } else { |
| + fprintf(ptd->core, ",\n"); |
| + } |
| + fprintf(ptd->core, "{\n"); |
| + fprintf(ptd->core, "\"dlpi_name\": \"%s\",\n", info->dlpi_name); |
| + fprintf(ptd->core, "\"dlpi_addr\": %"PRIuPTR",\n", info->dlpi_addr); |
| + fprintf(ptd->core, "\"dlpi_phdr\": [\n"); |
| + for (i = 0; i < info->dlpi_phnum; i++) { |
| + /* Skip non-LOAD type segments. */ |
| + if (info->dlpi_phdr[i].p_type != PT_LOAD) { |
| + continue; |
| + } |
| + if (i != 0) { |
| + fprintf(ptd->core, ",\n"); |
| + } |
| + fprintf(ptd->core, "{\n"); |
| + fprintf(ptd->core, "\"p_vaddr\": %"PRIuPTR",\n", |
| + info->dlpi_phdr[i].p_vaddr); |
| + fprintf(ptd->core, "\"p_memsz\": %"PRIuPTR"\n", |
| + info->dlpi_phdr[i].p_memsz); |
| + fprintf(ptd->core, "}\n"); |
| + } |
| + fprintf(ptd->core, "]\n"); |
| + fprintf(ptd->core, "}\n"); |
| + return 0; |
| +} |
| + |
| +static void PrintSections(FILE *core) { |
|
Mark Seaborn
2012/02/13 19:04:08
'Sections' -> 'Segments'
bradn
2012/02/13 23:42:03
Done.
|
| + struct ProgramTableData data; |
| + data.core = core; |
| + data.first = 1; |
| + dl_iterate_phdr(PrintSectionsOne, &data); |
| +} |
| + |
| +static int PrintMappedAddressOne( |
|
Mark Seaborn
2012/02/13 19:04:08
So you have two dl_iterate_phdr() loops. One list
bradn
2012/02/13 23:42:03
I had originally though we might have something in
|
| + struct dl_phdr_info *info, size_t size, void *data) { |
| + int i; |
| + struct ProgramTableData *ptd = (struct ProgramTableData *) data; |
| + uintptr_t addr = ptd->addr; |
| + uintptr_t start; |
| + uintptr_t end; |
| + |
| + /* Consider relative to phdr base addr. */ |
| + addr -= info->dlpi_addr; |
| + |
| + for (i = 0; i < info->dlpi_phnum; i++) { |
| + /* Skip non-LOAD type segments. */ |
| + if (info->dlpi_phdr[i].p_type != PT_LOAD) { |
| + continue; |
| + } |
| + /* See if it's in range for this part. */ |
| + start = info->dlpi_phdr[i].p_vaddr; |
| + end = start + info->dlpi_phdr[i].p_memsz; |
| + if (addr >= start && addr < end) { |
| + if (info->dlpi_name[0] == '\0') { |
| + fprintf(ptd->core, "{\"file\": \"%s\", \"addr\": %"PRIuPTR"}", |
| + g_ProgramName, addr); |
| + } else { |
| + fprintf(ptd->core, "{\"file\": \"%s\", \"addr\": %"PRIuPTR"}", |
| + info->dlpi_name, addr); |
| + } |
| + return 1; |
| + } |
| + } |
| + return 0; |
| +} |
| + |
| +static void PrintMappedAddress(FILE *core, uintptr_t addr) { |
| + struct ProgramTableData ptd; |
| + int result; |
| + |
| + ptd.core = core; |
| + ptd.addr = addr; |
| + result = dl_iterate_phdr(PrintMappedAddressOne, &ptd); |
| + if (result == 0) { |
| + fprintf(core, "{\"file\": \"%s\", \"addr\": %"PRIuPTR"}", |
| + g_ProgramName, addr); |
| + } |
| +} |
| + |
| +#else /* __GLIBC__ */ |
| + |
| +static void PrintSections(FILE *core) { |
| +} |
| + |
| +static void PrintMappedAddress(FILE *core, uintptr_t addr) { |
| + fprintf(core, "{\"file\": \"%s\", \"addr\": %"PRIuPTR"}", |
| + g_ProgramName, addr); |
| +} |
| + |
| +#endif /* __GLIBC__ */ |
| + |
| +uintptr_t SafeRead(uintptr_t a) { |
| + /* TODO(bradnelson): make this safer + more architecture general. */ |
| + /* Only read areas plausibly on the stack. */ |
| + if (a < 0x30000000 || a > 0x3fffffff) { |
|
Mark Seaborn
2012/02/13 19:04:08
I don't think this is kicking in during your test,
bradn
2012/02/13 23:42:03
Taken out.
This raises an interesting question.
If
|
| + return 0; |
| + } |
| + return *(uintptr_t*)a; |
| +} |
| + |
| +static void StackWalk(FILE *core, uintptr_t ip, uintptr_t fp) { |
| + uintptr_t next; |
| + uintptr_t i; |
| + int first = 1; |
| + |
| + fprintf(core, "\"frames\": [\n"); |
| + for (;;) { |
| + next = SafeRead(fp); |
| + if (next <= fp || next == 0) { |
| + break; |
| + } |
| + if (first) { |
| + first = 0; |
| + } else { |
| + fprintf(core, ","); |
| + } |
| + fprintf(core, "{\n"); |
| + fprintf(core, "\"fp\": %"PRIuPTR",\n", fp); |
| + fprintf(core, "\"ip\": %"PRIuPTR",\n", ip); |
| + fprintf(core, "\"ip_mapped\": "); |
| + PrintMappedAddress(core, ip); |
| + fprintf(core, ",\n"); |
| + fprintf(core, "\"data\": [\n"); |
| + for (i = fp + 8; i < next; i += 4) { |
| + if (i != fp + 8) { |
| + fprintf(core, ","); |
| + } |
| + fprintf(core, "%"PRIuPTR"\n", SafeRead(i)); |
| + } |
| + fprintf(core, "]\n"); |
| + fprintf(core, "}\n"); |
| + |
| + ip = SafeRead(fp + 4); |
| + fp = next; |
| + } |
| + |
| + fprintf(core, "]\n"); |
| +} |
| + |
| +void CrashHandlerWrapper(int prog_ctr, int stack_ptr); |
| +asm(".pushsection .text, \"ax\", @progbits\n" |
| + ".p2align NACLENTRYALIGN\n" |
| + "CrashHandlerWrapper:\n" |
| + "popl %eax\n" |
| + "pushl %ebp\n" |
| + "call CrashHandler\n" |
| + ".popsection\n"); |
| + |
| +void CrashHandler(int frame_ptr, int prog_ctr, int stack_ptr) { |
| + FILE *core; |
| + const char *core_filename; |
| + |
| + /* Pick core file name. */ |
| + core_filename = getenv("NACLCOREFILE"); |
| + if (core_filename == NULL) { |
| + core_filename = "naclcore.json"; |
| + } |
| + |
| + /* Attempt to open core file, otherwise use stdout. */ |
| + core = fopen(core_filename, "w"); |
| + if (core == NULL) { |
| + core = stdout; |
| + } |
| + |
| + fprintf(core, "{\n"); |
| + |
| + fprintf(core, "\"sections\": ["); |
| + PrintSections(core); |
| + fprintf(core, "],\n"); |
| + |
| + fprintf(core, "\"handler\": {\n"); |
| + fprintf(core, "\"prog_ctr\": %"PRIuPTR",\n", prog_ctr); |
| + fprintf(core, "\"stack_ptr\": %"PRIuPTR",\n", stack_ptr); |
| + fprintf(core, "\"frame_ptr\": %"PRIuPTR"\n", frame_ptr); |
| + fprintf(core, "},\n"); |
| + |
| + StackWalk(core, (uintptr_t) prog_ctr, (uintptr_t) frame_ptr); |
| + |
| + fprintf(core, "}\n"); |
| + |
| + if (core != stdout) { |
| + fclose(core); |
| + } |
| + |
| + exit(166); |
| +} |
| + |
| +void NaClCrashDumpThreadDestructor(void *arg) { |
| + munmap(arg, CRASH_STACK_COMPLETE_SIZE); |
| +} |
| + |
| +void NaClCrashDumpInit(char *program_name) { |
| + int result; |
| + g_ProgramName = program_name; |
| + result = pthread_key_create(&g_CrashStackKey, NaClCrashDumpThreadDestructor); |
| + assert(result == 0); |
| + fprintf(stderr, "ADDR %x\n", (unsigned int)CrashHandlerWrapper); |
|
Mark Seaborn
2012/02/13 19:04:08
Remove debugging code
bradn
2012/02/13 23:42:03
Done.
|
| + fprintf(stderr, "ADDR %x\n", (unsigned int)CrashHandler); |
| + result = NACL_SYSCALL(exception_handler)(CrashHandlerWrapper, |
| + &g_PrevHandler); |
| + assert(result == 0); |
| + NaClCrashDumpInitThread(); |
| +} |
| + |
| +void NaClCrashDumpDestroy(void) { |
| + int result; |
| + result = NACL_SYSCALL(exception_handler)(g_PrevHandler, NULL); |
|
Mark Seaborn
2012/02/13 19:04:08
FWIW, I'm not sure that attempting to restore this
bradn
2012/02/13 23:42:03
Done.
|
| + assert(result == 0); |
| + result = pthread_key_delete(g_CrashStackKey); |
| + assert(result == 0); |
| + g_ProgramName = 0; |
|
Mark Seaborn
2012/02/13 19:04:08
0 -> NULL
bradn
2012/02/13 23:42:03
Moot, gone.
|
| +} |
| + |
| +void NaClCrashDumpInitThread(void) { |
| + void *stack; |
| + void *guard; |
| + int result; |
| + /* |
| + * NOTE: Setting up a per thread stack is only particularly interesting |
| + * for stack overflow. |
| + */ |
| + stack = mmap(NULL, CRASH_STACK_COMPLETE_SIZE, |
| + PROT_READ | PROT_WRITE, |
| + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
| + assert(stack != MAP_FAILED); |
| + guard = mmap(stack, CRASH_STACK_GUARD_SIZE, |
| + PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
| + assert(guard != MAP_FAILED); |
|
Mark Seaborn
2012/02/13 19:04:08
A stricter assertion would be
assert(guard == st
bradn
2012/02/13 23:42:03
Done.
|
| + pthread_setspecific(g_CrashStackKey, stack); |
| + result = NACL_SYSCALL(exception_stack)(stack, CRASH_STACK_COMPLETE_SIZE); |
| + assert(result == 0); |
| +} |