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..86f3aa061507ded7d3620cd7ec4dc4eaaad3ed6f |
--- /dev/null |
+++ b/tests/untrusted_crash_dump/untrusted_crash_dump.c |
@@ -0,0 +1,270 @@ |
+/* |
+ * 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; |
+ |
+ 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) { |
+ struct ProgramTableData data; |
+ data.core = core; |
+ data.first = 1; |
+ dl_iterate_phdr(PrintSectionsOne, &data); |
+} |
+ |
+static int PrintMappedAddressOne( |
+ 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) { |
+ return 0; |
+ } |
+ return *(uintptr_t*)a; |
+} |
+ |
+static void StackWalk(FILE *core, uintptr_t ip, uintptr_t sp) { |
+ uintptr_t next; |
+ uintptr_t i; |
+ int first = 1; |
+ |
+ fprintf(core, "\"frames\": [\n"); |
+ for (;;) { |
+ next = SafeRead(sp); |
+ if (next <= sp || next == 0) { |
+ break; |
+ } |
+ if (first) { |
+ first = 0; |
+ } else { |
+ fprintf(core, ","); |
+ } |
+ fprintf(core, "{\n"); |
+ fprintf(core, "\"sp\": %"PRIuPTR",\n", sp); |
+ fprintf(core, "\"ip\": %"PRIuPTR",\n", ip); |
+ fprintf(core, "\"ip_mapped\": "); |
+ PrintMappedAddress(core, ip); |
+ fprintf(core, ",\n"); |
+ fprintf(core, "\"data\": [\n"); |
+ for (i = sp + 8; i < next; i += 4) { |
+ if (i != sp + 8) { |
+ fprintf(core, ","); |
+ } |
+ fprintf(core, "%"PRIuPTR"\n", SafeRead(i)); |
+ } |
+ fprintf(core, "]\n"); |
+ fprintf(core, "}\n"); |
+ |
+ ip = SafeRead(sp + 4); |
+ sp = next; |
+ } |
+ |
+ fprintf(core, "]\n"); |
+} |
+ |
+static void CrashHandler(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, "},\n"); |
+ StackWalk(core, (uintptr_t) prog_ctr, (uintptr_t) stack_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); |
+ result = NACL_SYSCALL(exception_handler)(CrashHandler, &g_PrevHandler); |
+ assert(result == 0); |
+ NaClCrashDumpInitThread(); |
+} |
+ |
+void NaClCrashDumpDestroy(void) { |
+ int result; |
+ result = NACL_SYSCALL(exception_handler)(g_PrevHandler, NULL); |
+ assert(result == 0); |
+ result = pthread_key_delete(g_CrashStackKey); |
+ assert(result == 0); |
+ g_ProgramName = 0; |
+} |
+ |
+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); |
+ pthread_setspecific(g_CrashStackKey, stack); |
+ result = NACL_SYSCALL(exception_stack)(stack, CRASH_STACK_COMPLETE_SIZE); |
+ assert(result == 0); |
+} |