Home | History | Annotate | Download | only in error_handling
      1 /* Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file. */
      4 
      5 #include <assert.h>
      6 #include <inttypes.h>
      7 #include <pthread.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <sys/mman.h>
     12 #include <unistd.h>
     13 
     14 #ifdef __GLIBC__
     15 #include <elf.h>
     16 #include <link.h>
     17 #endif  /* __GLIBC__ */
     18 
     19 #include "irt.h"
     20 #include "nacl/nacl_exception.h"
     21 
     22 #include "error_handling/error_handling.h"
     23 #include "error_handling/string_stream.h"
     24 
     25 
     26 #define PAGE_CHUNK_SIZE (64 * 1024)
     27 #define PAGE_CHUNK_MASK (PAGE_CHUNK_SIZE - 1)
     28 #define STACK_SIZE_MIN (PAGE_CHUNK_SIZE * 4)
     29 
     30 #define MAX_FRAME_SIZE (10 * 1024)
     31 #define MAX_FRAME_CAP 128
     32 
     33 static pthread_key_t s_eh_stack_info_key;
     34 static EHJsonHandler s_eh_json_callback = NULL;
     35 static int s_eh_exception_enabled = 0;
     36 static struct nacl_irt_exception_handling s_exception_handling;
     37 
     38 
     39 typedef struct {
     40   void* stack;
     41   size_t size;
     42 } EHStackInfo;
     43 
     44 static uintptr_t EHReadPointer(uintptr_t offset) {
     45   return *((uintptr_t*) offset);
     46 }
     47 
     48 static void EHPrintArch(sstream_t* ss, struct NaClExceptionContext* context) {
     49 #if defined(__x86_64__)
     50   ssprintf(ss, "\"arch\": \"x86_64\",\n");
     51 #elif defined(__i386__)
     52   ssprintf(ss, "\"arch\": \"x86_32\",\n");
     53 #elif defined(__arm__)
     54   ssprintf(ss, "\"arch\": \"arm\",\n");
     55 #elif defined(__mips__)
     56   ssprintf(ss, "\"arch\": \"mips\",\n");
     57 #else
     58 #error Unknown ARCH
     59 #endif
     60 }
     61 
     62 static void EHPrintSegments(sstream_t* ss,
     63                             struct NaClExceptionContext* context) {
     64   ssprintf(ss, "\"segments\": [");
     65   ssprintf(ss, "],\n");
     66 }
     67 
     68 static void EHPrintFrame(sstream_t* ss, EHFrame* frame) {
     69   uintptr_t start;
     70   uintptr_t i;
     71 
     72   ssprintf(ss, "{\n");
     73   ssprintf(ss, "\"frame_ptr\": %u,\n", frame->frame_ptr);
     74   ssprintf(ss, "\"prog_ctr\": %u,\n", frame->prog_ctr);
     75   ssprintf(ss, "\"data\": [\n");
     76 
     77 #if defined(__x86_64__)
     78   start = frame->frame_ptr + 8;
     79 #else
     80   start = frame->frame_ptr + 16;
     81 #endif
     82   /* Capture the stack, no mare than 128 bytes to keep the size sane. */
     83   for (i = start; i < frame->next_ptr && i - start < MAX_FRAME_CAP; i += 4) {
     84     if (i != start) {
     85       ssprintf(ss, ",");
     86     }
     87     ssprintf(ss, "%u\n", EHReadPointer(i));
     88   }
     89   ssprintf(ss, "]\n}\n");
     90 }
     91 
     92 
     93 static void EHPrintMainContext(sstream_t* ss,
     94                                struct NaClExceptionContext* context) {
     95   struct NaClExceptionPortableContext* portable_context =
     96       nacl_exception_context_get_portable(context);
     97   ssprintf(ss, "\"handler\": {\n");
     98   ssprintf(ss, "\"prog_ctr\": %u,\n", portable_context->prog_ctr);
     99   ssprintf(ss, "\"stack_ptr\": %u,\n", portable_context->stack_ptr);
    100   ssprintf(ss, "\"frame_ptr\": %u\n", portable_context->frame_ptr);
    101   ssprintf(ss, "},\n");
    102 }
    103 
    104 
    105 int EHGetTopFrame(sstream_t* ss, struct NaClExceptionContext* context,
    106                   EHFrame* frame) {
    107   struct NaClExceptionPortableContext* portable_context =
    108       nacl_exception_context_get_portable(context);
    109 
    110   frame->prog_ctr = portable_context->prog_ctr;
    111   frame->frame_ptr = portable_context->frame_ptr;
    112   frame->next_ptr = EHReadPointer(portable_context->frame_ptr);
    113   return 1;
    114 }
    115 
    116 
    117 int EHUnwindFrame(EHFrame* frame) {
    118   uintptr_t frame_ptr;
    119   uintptr_t next;
    120 
    121   // Verify the current frame
    122   if (NULL == frame) return 0;
    123 
    124   frame_ptr = frame->frame_ptr;
    125   next = frame->next_ptr;
    126 
    127   // Abort if done or unwind moves us in the wrong direction
    128   if (next <= frame_ptr || next == 0) return 0;
    129 
    130   // Abort if frame is > 10K
    131   if (next - frame_ptr > MAX_FRAME_SIZE) return 0;
    132 
    133   // Unwind the frame
    134   frame->frame_ptr = next;
    135   frame->next_ptr = EHReadPointer(frame->frame_ptr);
    136 #if defined(__x86_64__)
    137   frame->prog_ctr = EHReadPointer(frame_ptr + 8);
    138 #else
    139   frame->prog_ctr = EHReadPointer(frame_ptr + 4);
    140 #endif
    141   return 1;
    142 }
    143 
    144 
    145 static void EHStackInfoDestructor(void *arg) {
    146   EHStackInfo* info = (EHStackInfo*) arg;
    147 
    148   if (info) {
    149     munmap(info->stack, info->size);
    150   }
    151   free(info);
    152 }
    153 
    154 
    155 void EHDefaultJsonHandler(struct NaClExceptionContext* context) {
    156   if (s_eh_json_callback) {
    157     EHFrame frame;
    158     sstream_t ss;
    159     ssinit(&ss);
    160 
    161     ssprintf(&ss, "{\n");
    162     EHPrintArch(&ss, context);
    163     EHPrintSegments(&ss, context);
    164     EHPrintMainContext(&ss, context);
    165 
    166     EHGetTopFrame(&ss, context, &frame);
    167     int first = 1;
    168 
    169     ssprintf(&ss, "\"frames\": [\n");
    170     do {
    171       if (!first) ssprintf(&ss, ",");
    172       EHPrintFrame(&ss, &frame);
    173       first = 0;
    174     } while (EHUnwindFrame(&frame));
    175 
    176     /* End frame LIST and context DICT */
    177     ssprintf(&ss, "]\n}\n");
    178     s_eh_json_callback(ss.data);
    179 
    180     ssfree(&ss);
    181     while(1) sleep(9999);
    182   }
    183 }
    184 
    185 
    186 void EHRequestExceptionsRaw(EHRawHandler callback) {
    187   size_t interface_size = sizeof(s_exception_handling);
    188   if (s_eh_exception_enabled) {
    189     fprintf(stderr, "ERROR: EHInit already initialized.\n");
    190     return;
    191   }
    192   if (NULL == callback) {
    193     fprintf(stderr, "ERROR: EHInit called with NULL callback.\n");
    194     return;
    195   }
    196 
    197   /* Expect an exact match on the interface structure size. */
    198   if (nacl_interface_query(NACL_IRT_EXCEPTION_HANDLING_v0_1,
    199                            &s_exception_handling,
    200                            interface_size) != interface_size) {
    201     fprintf(stderr, "ERROR: EHInit failed nacl_interface_query\n");
    202     return;
    203   }
    204 
    205   if (s_exception_handling.exception_handler(callback, NULL) != 0) {
    206     fprintf(stderr, "ERROR: EHInit failed to install exception_handler\n");
    207     return;
    208   }
    209 
    210   s_eh_exception_enabled = 1;
    211 
    212   // Create a TLS key for storing per thread stack info
    213   pthread_key_create(&s_eh_stack_info_key, EHStackInfoDestructor);
    214 }
    215 
    216 
    217 void *EHRequestExceptionStackOnThread(size_t stack_size) {
    218   void* stack;
    219   void* guard;
    220   EHStackInfo* stack_info;
    221 
    222   // Set the stack size
    223   stack_size = (stack_size + PAGE_CHUNK_MASK) & PAGE_CHUNK_MASK;
    224   if (stack_size < STACK_SIZE_MIN) stack_size = STACK_SIZE_MIN;
    225 
    226   // Allocate stack + guard page
    227   stack = mmap(NULL, stack_size + PAGE_CHUNK_SIZE,
    228       PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    229   if (MAP_FAILED == stack) return MAP_FAILED;
    230 
    231   // Remap to mprotect which may not be available
    232   guard = mmap(stack, PAGE_CHUNK_SIZE,
    233       PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    234   if (guard != stack) {
    235     fprintf(stderr, "WARNING: Failed to add guard page for alt stack.\n");
    236   }
    237 
    238   if (!s_exception_handling.exception_stack(stack, stack_size)) {
    239     fprintf(stderr, "ERROR: Failed to assign stack.\n");
    240     munmap(stack, stack_size);
    241     return MAP_FAILED;
    242   }
    243 
    244   // Allocate stack tracking information for this thread
    245   stack_info = (EHStackInfo*) malloc(sizeof(EHStackInfo));
    246   stack_info->stack = stack;
    247   stack_info->size = stack_size + PAGE_CHUNK_SIZE;
    248   pthread_setspecific(s_eh_stack_info_key, stack_info);
    249   return stack;
    250 }
    251 
    252 
    253 void EHRequestExceptionsJson(EHJsonHandler callback) {
    254   if (NULL == callback) return;
    255 
    256   EHRequestExceptionsRaw(EHDefaultJsonHandler);
    257   if (s_eh_exception_enabled) s_eh_json_callback = callback;
    258 }
    259 
    260 
    261 int EHHanderInstalled() {
    262   return s_eh_exception_enabled;
    263 }
    264