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