| 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);
|
| +}
|
|
|