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. */
17 #endif /* __GLIBC__ */
20 #include "nacl/nacl_exception.h"
22 #include "error_handling/error_handling.h"
23 #include "error_handling/string_stream.h"
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)
30 #define MAX_FRAME_SIZE (10 * 1024)
31 #define MAX_FRAME_CAP 128
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
;
44 static uintptr_t EHReadPointer(uintptr_t offset
) {
45 return *((uintptr_t*) offset
);
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");
62 static void EHPrintSegments(sstream_t
* ss
,
63 struct NaClExceptionContext
* context
) {
64 ssprintf(ss
, "\"segments\": [");
68 static void EHPrintFrame(sstream_t
* ss
, EHFrame
* frame
) {
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");
77 #if defined(__x86_64__)
78 start
= frame
->frame_ptr
+ 8;
80 start
= frame
->frame_ptr
+ 16;
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) {
87 ssprintf(ss
, "%u\n", EHReadPointer(i
));
89 ssprintf(ss
, "]\n}\n");
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");
105 int EHGetTopFrame(sstream_t
* ss
, struct NaClExceptionContext
* context
,
107 struct NaClExceptionPortableContext
* portable_context
=
108 nacl_exception_context_get_portable(context
);
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
);
117 int EHUnwindFrame(EHFrame
* frame
) {
121 // Verify the current frame
122 if (NULL
== frame
) return 0;
124 frame_ptr
= frame
->frame_ptr
;
125 next
= frame
->next_ptr
;
127 // Abort if done or unwind moves us in the wrong direction
128 if (next
<= frame_ptr
|| next
== 0) return 0;
130 // Abort if frame is > 10K
131 if (next
- frame_ptr
> MAX_FRAME_SIZE
) return 0;
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);
139 frame
->prog_ctr
= EHReadPointer(frame_ptr
+ 4);
145 static void EHStackInfoDestructor(void *arg
) {
146 EHStackInfo
* info
= (EHStackInfo
*) arg
;
149 munmap(info
->stack
, info
->size
);
155 void EHDefaultJsonHandler(struct NaClExceptionContext
* context
) {
156 if (s_eh_json_callback
) {
161 ssprintf(&ss
, "{\n");
162 EHPrintArch(&ss
, context
);
163 EHPrintSegments(&ss
, context
);
164 EHPrintMainContext(&ss
, context
);
166 EHGetTopFrame(&ss
, context
, &frame
);
169 ssprintf(&ss
, "\"frames\": [\n");
171 if (!first
) ssprintf(&ss
, ",");
172 EHPrintFrame(&ss
, &frame
);
174 } while (EHUnwindFrame(&frame
));
176 /* End frame LIST and context DICT */
177 ssprintf(&ss
, "]\n}\n");
178 s_eh_json_callback(ss
.data
);
181 while(1) sleep(9999);
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");
192 if (NULL
== callback
) {
193 fprintf(stderr
, "ERROR: EHInit called with NULL callback.\n");
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");
205 if (s_exception_handling
.exception_handler(callback
, NULL
) != 0) {
206 fprintf(stderr
, "ERROR: EHInit failed to install exception_handler\n");
210 s_eh_exception_enabled
= 1;
212 // Create a TLS key for storing per thread stack info
213 pthread_key_create(&s_eh_stack_info_key
, EHStackInfoDestructor
);
217 void *EHRequestExceptionStackOnThread(size_t stack_size
) {
220 EHStackInfo
* stack_info
;
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
;
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
;
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");
238 if (!s_exception_handling
.exception_stack(stack
, stack_size
)) {
239 fprintf(stderr
, "ERROR: Failed to assign stack.\n");
240 munmap(stack
, stack_size
);
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
);
253 void EHRequestExceptionsJson(EHJsonHandler callback
) {
254 if (NULL
== callback
) return;
256 EHRequestExceptionsRaw(EHDefaultJsonHandler
);
257 if (s_eh_exception_enabled
) s_eh_json_callback
= callback
;
261 int EHHanderInstalled() {
262 return s_eh_exception_enabled
;