/src/wireshark/epan/except.c
Line | Count | Source |
1 | | /* |
2 | | * Portable Exception Handling for ANSI C. |
3 | | * Copyright (C) 1999 Kaz Kylheku <kaz@ashi.footprints.net> |
4 | | * |
5 | | * Free Software License: |
6 | | * |
7 | | * All rights are reserved by the author, with the following exceptions: |
8 | | * Permission is granted to freely reproduce and distribute this software, |
9 | | * possibly in exchange for a fee, provided that this copyright notice appears |
10 | | * intact. Permission is also granted to adapt this software to produce |
11 | | * derivative works, as long as the modified versions carry this copyright |
12 | | * notice and additional notices stating that the work has been modified. |
13 | | * This source code may be translated into executable form and incorporated |
14 | | * into proprietary software; there is no requirement for such software to |
15 | | * contain a copyright notice related to this source. |
16 | | */ |
17 | | |
18 | | /* |
19 | | * Modified to support throwing an exception with a null message pointer, |
20 | | * and to have the message not be const (as we generate messages with |
21 | | * "ws_strdup_printf()", which means they need to be freed; using |
22 | | * a null message means that we don't have to use a special string |
23 | | * for exceptions with no message, and don't have to worry about |
24 | | * not freeing that). |
25 | | */ |
26 | | |
27 | | #include "config.h" |
28 | 0 | #define WS_LOG_DOMAIN "Epan" |
29 | | #include <wsutil/wslog.h> |
30 | | #include <assert.h> |
31 | | #include <stdlib.h> |
32 | | #include <stdio.h> |
33 | | #include <stdarg.h> |
34 | | #include <limits.h> |
35 | | |
36 | | #include <glib.h> |
37 | | #include <wsutil/ws_assert.h> |
38 | | |
39 | | #include "except.h" |
40 | | |
41 | | #ifdef _WIN32 |
42 | | #include <windows.h> |
43 | | #include "exceptions.h" |
44 | | #endif |
45 | | |
46 | 564 | #define XCEPT_BUFFER_SIZE 1024 |
47 | | |
48 | | #ifdef KAZLIB_POSIX_THREADS |
49 | | |
50 | | #include <pthread.h> |
51 | | |
52 | | static pthread_mutex_t init_mtx = PTHREAD_MUTEX_INITIALIZER; |
53 | | static int init_counter; |
54 | | static pthread_key_t top_key; |
55 | | static pthread_key_t uh_key; |
56 | | static pthread_key_t alloc_key; |
57 | | static pthread_key_t dealloc_key; |
58 | | static void unhandled_catcher(except_t *); |
59 | | |
60 | | #define get_top() ((struct except_stacknode *) pthread_getspecific(top_key)) |
61 | | #define set_top(T) (pthread_setspecific(top_key, (T)), (void)((T) == (struct except_stacknode *) 0)) |
62 | | #define set_catcher(C) (pthread_setspecific(uh_key, (void *) (C)), (void)((C) == (void (*)(except_t *)) 0)) |
63 | | #define set_alloc(A) (pthread_setspecific(alloc_key, (void *) (A)), (void)((A) == (void *(*)(size_t)) 0)) |
64 | | #define set_dealloc(D) (pthread_setspecific(dealloc_key, (void *) (D)), (void)((D) == (void (*)(void *)) 0)) |
65 | | |
66 | | static void (*get_catcher(void))(except_t *) |
67 | | { |
68 | | void (*catcher)(except_t *) = (void (*)(except_t *)) pthread_getspecific(uh_key); |
69 | | return (catcher == 0) ? unhandled_catcher : catcher; |
70 | | } |
71 | | |
72 | | static void *(*get_alloc(void))(size_t) |
73 | | { |
74 | | void *(*alloc)(size_t) = (void *(*)(size_t)) pthread_getspecific(alloc_key); |
75 | | return (alloc == 0) ? malloc : alloc; |
76 | | } |
77 | | |
78 | | static void (*get_dealloc(void))(void *) |
79 | | { |
80 | | void (*dealloc)(void *) = (void (*)(void *)) pthread_getspecific(dealloc_key); |
81 | | return (dealloc == 0) ? free : dealloc; |
82 | | } |
83 | | |
84 | | int except_init(void) |
85 | | { |
86 | | int retval = 1; |
87 | | |
88 | | pthread_mutex_lock(&init_mtx); |
89 | | |
90 | | assert (init_counter < INT_MAX); |
91 | | |
92 | | if (init_counter++ == 0) { |
93 | | int top_ok = (pthread_key_create(&top_key, 0) == 0); |
94 | | int uh_ok = (pthread_key_create(&uh_key, 0) == 0); |
95 | | int alloc_ok = (pthread_key_create(&alloc_key, 0) == 0); |
96 | | int dealloc_ok = (pthread_key_create(&dealloc_key, 0) == 0); |
97 | | |
98 | | if (!top_ok || !uh_ok || !alloc_ok || !dealloc_ok) { |
99 | | retval = 0; |
100 | | init_counter = 0; |
101 | | if (top_ok) |
102 | | pthread_key_delete(top_key); |
103 | | if (uh_ok) |
104 | | pthread_key_delete(uh_key); |
105 | | if (alloc_ok) |
106 | | pthread_key_delete(alloc_key); |
107 | | if (dealloc_ok) |
108 | | pthread_key_delete(dealloc_key); |
109 | | } |
110 | | } |
111 | | |
112 | | pthread_mutex_unlock(&init_mtx); |
113 | | |
114 | | return retval; |
115 | | } |
116 | | |
117 | | void except_deinit(void) |
118 | | { |
119 | | pthread_mutex_lock(&init_mtx); |
120 | | |
121 | | assert (init_counter > 0); |
122 | | |
123 | | if (--init_counter == 0) { |
124 | | pthread_key_delete(top_key); |
125 | | pthread_key_delete(uh_key); |
126 | | pthread_key_delete(alloc_key); |
127 | | pthread_key_delete(dealloc_key); |
128 | | } |
129 | | |
130 | | pthread_mutex_unlock(&init_mtx); |
131 | | } |
132 | | |
133 | | #else /* not using POSIX thread support */ |
134 | | |
135 | | /* |
136 | | * We make the catcher stack per-thread, because we must. |
137 | | * |
138 | | * We don't make the unhandled-exception-catcher, the allocator, or the |
139 | | * deallocator thread-specific, as we don't need to. |
140 | | * |
141 | | * We don't protext the init level with a mutex, as we only initialize |
142 | | * it and de-initialize it once. |
143 | | */ |
144 | | static int init_counter; |
145 | | static void unhandled_catcher(except_t *); |
146 | | static void (*uh_catcher_ptr)(except_t *) = unhandled_catcher; |
147 | | /* We need this 'size_t' cast due to a glitch in GLib where g_malloc was prototyped |
148 | | * as 'void *g_malloc (unsigned long n_bytes)'. This was later fixed to the correct prototype |
149 | | * 'void *g_malloc (size_t n_bytes)'. In Wireshark we use the latter prototype |
150 | | * throughout the code. We can get away with this even with older versions of GLib by |
151 | | * adding a '(void *(*)(size_t))' cast whenever we refer to g_malloc. The only platform |
152 | | * supported by Wireshark where this isn't safe (sizeof size_t != sizeof unsigned long) is Win64. |
153 | | * However, we _always_ bundle the newest version of GLib on this platform so |
154 | | * the size_t issue doesn't exists here. Pheew.. */ |
155 | | static void *(*allocator)(size_t) = (void *(*)(size_t)) g_malloc; |
156 | | static void (*deallocator)(void *) = g_free; |
157 | | static WS_THREAD_LOCAL struct except_stacknode *stack_top; |
158 | | |
159 | 54.0M | #define get_top() (stack_top) |
160 | 54.0M | #define set_top(T) (stack_top = (T)) |
161 | 0 | #define get_catcher() (uh_catcher_ptr) |
162 | 0 | #define set_catcher(C) (uh_catcher_ptr = (C)) |
163 | 282 | #define get_alloc() (allocator) |
164 | 0 | #define set_alloc(A) (allocator = (A)) |
165 | 1.62M | #define get_dealloc() (deallocator) |
166 | 0 | #define set_dealloc(D) (deallocator = (D)) |
167 | | |
168 | | int except_init(void) |
169 | 14 | { |
170 | 14 | assert (init_counter < INT_MAX); |
171 | 14 | init_counter++; |
172 | 14 | return 1; |
173 | 14 | } |
174 | | |
175 | | void except_deinit(void) |
176 | 0 | { |
177 | 0 | assert (init_counter > 0); |
178 | 0 | init_counter--; |
179 | 0 | } |
180 | | |
181 | | #endif |
182 | | |
183 | | |
184 | | static int match(const volatile except_id_t *thrown, const except_id_t *caught) |
185 | 300k | { |
186 | 300k | int group_match = (caught->except_group == XCEPT_GROUP_ANY || |
187 | 300k | caught->except_group == thrown->except_group); |
188 | 300k | int code_match = (caught->except_code == XCEPT_CODE_ANY || |
189 | 0 | caught->except_code == thrown->except_code); |
190 | | |
191 | 300k | return group_match && code_match; |
192 | 300k | } |
193 | | |
194 | | WS_NORETURN static void do_throw(except_t *except) |
195 | 300k | { |
196 | 300k | struct except_stacknode *top; |
197 | | |
198 | 300k | assert (except->except_id.except_group != 0 && |
199 | 300k | except->except_id.except_code != 0); |
200 | | |
201 | 302k | for (top = get_top(); top != 0; top = top->except_down) { |
202 | 302k | if (top->except_type == XCEPT_CLEANUP) { |
203 | 1.89k | top->except_info.except_cleanup->except_func(top->except_info.except_cleanup->except_context); |
204 | 300k | } else { |
205 | 300k | struct except_catch *catcher = top->except_info.except_catcher; |
206 | 300k | const except_id_t *pi = catcher->except_id; |
207 | 300k | size_t i; |
208 | | |
209 | 300k | assert (top->except_type == XCEPT_CATCHER); |
210 | 300k | except_free(catcher->except_obj.except_dyndata); |
211 | | |
212 | 300k | for (i = 0; i < catcher->except_size; pi++, i++) { |
213 | 300k | if (match(&except->except_id, pi)) { |
214 | 300k | catcher->except_obj = *except; |
215 | 300k | set_top(top); |
216 | 300k | longjmp(catcher->except_jmp, 1); |
217 | 300k | } |
218 | 300k | } |
219 | 300k | } |
220 | 302k | } |
221 | | |
222 | 0 | set_top(top); |
223 | 0 | get_catcher()(except); /* unhandled exception */ |
224 | 0 | abort(); |
225 | 300k | } |
226 | | |
227 | | WS_NORETURN |
228 | | static void unhandled_catcher(except_t *except) |
229 | 0 | { |
230 | 0 | if (except->except_message == NULL) { |
231 | 0 | ws_error("Unhandled exception (group=%lu, code=%lu)\n", |
232 | 0 | except->except_id.except_group, |
233 | 0 | except->except_id.except_code); |
234 | 0 | } else { |
235 | 0 | ws_error("Unhandled exception (\"%s\", group=%lu, code=%lu)\n", |
236 | 0 | except->except_message, except->except_id.except_group, |
237 | 0 | except->except_id.except_code); |
238 | 0 | } |
239 | 0 | ws_assert_not_reached(); |
240 | 0 | } |
241 | | |
242 | | static void stack_push(struct except_stacknode *node) |
243 | 26.8M | { |
244 | 26.8M | node->except_down = get_top(); |
245 | 26.8M | set_top(node); |
246 | 26.8M | } |
247 | | |
248 | | void except_setup_clean(struct except_stacknode *esn, |
249 | | struct except_cleanup *ecl, void (*cleanf)(void *), void *context) |
250 | 25.4M | { |
251 | 25.4M | esn->except_type = XCEPT_CLEANUP; |
252 | 25.4M | ecl->except_func = cleanf; |
253 | 25.4M | ecl->except_context = context; |
254 | 25.4M | esn->except_info.except_cleanup = ecl; |
255 | 25.4M | stack_push(esn); |
256 | 25.4M | } |
257 | | |
258 | | void except_setup_try(struct except_stacknode *esn, |
259 | | struct except_catch *ech, const except_id_t id[], size_t size) |
260 | 1.39M | { |
261 | 1.39M | ech->except_id = id; |
262 | 1.39M | ech->except_size = size; |
263 | 1.39M | ech->except_obj.except_dyndata = 0; |
264 | 1.39M | esn->except_type = XCEPT_CATCHER; |
265 | 1.39M | esn->except_info.except_catcher = ech; |
266 | 1.39M | stack_push(esn); |
267 | 1.39M | } |
268 | | |
269 | | struct except_stacknode *except_pop(void) |
270 | 26.7M | { |
271 | 26.7M | struct except_stacknode *top = get_top(); |
272 | 26.7M | assert (top->except_type == XCEPT_CLEANUP || top->except_type == XCEPT_CATCHER); |
273 | 26.7M | set_top(top->except_down); |
274 | 26.7M | return top; |
275 | 26.7M | } |
276 | | |
277 | | WS_NORETURN void except_rethrow(except_t *except) |
278 | 63.5k | { |
279 | 63.5k | struct except_stacknode *top = get_top(); |
280 | 63.5k | assert (top != 0); |
281 | 63.5k | assert (top->except_type == XCEPT_CATCHER); |
282 | 63.5k | assert (&top->except_info.except_catcher->except_obj == except); |
283 | 63.5k | set_top(top->except_down); |
284 | 63.5k | do_throw(except); |
285 | 63.5k | } |
286 | | |
287 | | WS_NORETURN void except_throw(long group, long code, const char *msg) |
288 | 236k | { |
289 | 236k | except_t except; |
290 | | |
291 | 236k | except.except_id.except_group = group; |
292 | 236k | except.except_id.except_code = code; |
293 | 236k | except.except_message = msg; |
294 | 236k | except.except_dyndata = 0; |
295 | | |
296 | | #ifdef _WIN32 |
297 | | if (code == DissectorError && IsDebuggerPresent()) { |
298 | | DebugBreak(); |
299 | | } |
300 | | #endif |
301 | | |
302 | 236k | do_throw(&except); |
303 | 236k | } |
304 | | |
305 | | WS_NORETURN void except_throwd(long group, long code, const char *msg, void *data) |
306 | 282 | { |
307 | 282 | except_t except; |
308 | | |
309 | 282 | except.except_id.except_group = group; |
310 | 282 | except.except_id.except_code = code; |
311 | 282 | except.except_message = msg; |
312 | 282 | except.except_dyndata = data; |
313 | | |
314 | 282 | do_throw(&except); |
315 | 282 | } |
316 | | |
317 | | /* |
318 | | * XXX - should we use ws_strdup_printf() here, so we're not limited by |
319 | | * XCEPT_BUFFER_SIZE? We could then just use this to generate formatted |
320 | | * messages. |
321 | | */ |
322 | | WS_NORETURN void except_vthrowf(long group, long code, const char *fmt, |
323 | | va_list vl) |
324 | 282 | { |
325 | 282 | char *buf = (char *)except_alloc(XCEPT_BUFFER_SIZE); |
326 | | |
327 | 282 | vsnprintf(buf, XCEPT_BUFFER_SIZE, fmt, vl); |
328 | 282 | except_throwd(group, code, buf, buf); |
329 | 282 | } |
330 | | |
331 | | WS_NORETURN void except_throwf(long group, long code, const char *fmt, ...) |
332 | 0 | { |
333 | 0 | va_list vl; |
334 | |
|
335 | 0 | va_start (vl, fmt); |
336 | 0 | except_vthrowf(group, code, fmt, vl); |
337 | 0 | va_end (vl); |
338 | 0 | ws_assert_not_reached(); /* GCC 12 with ASAN needs this. */ |
339 | 0 | } |
340 | | |
341 | | void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *) |
342 | 0 | { |
343 | 0 | void (*old_catcher)(except_t *) = get_catcher(); |
344 | 0 | set_catcher(new_catcher); |
345 | 0 | return old_catcher; |
346 | 0 | } |
347 | | |
348 | | #undef except_code |
349 | | #undef except_group |
350 | | #undef except_message |
351 | | #undef except_data |
352 | | |
353 | | unsigned long except_code(except_t *ex) |
354 | 0 | { |
355 | 0 | return ex->except_id.except_code; |
356 | 0 | } |
357 | | |
358 | | unsigned long except_group(except_t *ex) |
359 | 0 | { |
360 | 0 | return ex->except_id.except_group; |
361 | 0 | } |
362 | | |
363 | | const char *except_message(except_t *ex) |
364 | 0 | { |
365 | 0 | return ex->except_message; |
366 | 0 | } |
367 | | |
368 | | void *except_data(except_t *ex) |
369 | 0 | { |
370 | 0 | return ex->except_dyndata; |
371 | 0 | } |
372 | | |
373 | | void *except_take_data(except_t *ex) |
374 | 0 | { |
375 | 0 | void *data = ex->except_dyndata; |
376 | 0 | ex->except_dyndata = 0; |
377 | 0 | return data; |
378 | 0 | } |
379 | | |
380 | | void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *)) |
381 | 0 | { |
382 | 0 | set_alloc(alloc); |
383 | 0 | set_dealloc(dealloc); |
384 | 0 | } |
385 | | |
386 | | void *except_alloc(size_t size) |
387 | 282 | { |
388 | 282 | void *ptr = get_alloc()(size); |
389 | | |
390 | 282 | if (ptr == 0) |
391 | 0 | except_throw(XCEPT_BAD_ALLOC, 0, "out of memory"); |
392 | 282 | return ptr; |
393 | 282 | } |
394 | | |
395 | | void except_free(void *ptr) |
396 | 1.62M | { |
397 | 1.62M | get_dealloc()(ptr); |
398 | 1.62M | } |
399 | | |
400 | | #ifdef KAZLIB_TEST_MAIN |
401 | | |
402 | | |
403 | | static void cleanup(void *arg) |
404 | | { |
405 | | printf("cleanup(\"%s\") called\n", (char *) arg); |
406 | | } |
407 | | |
408 | | static void bottom_level(void) |
409 | | { |
410 | | char buf[256]; |
411 | | printf("throw exception? "); fflush(stdout); |
412 | | fgets(buf, sizeof buf, stdin); |
413 | | |
414 | | if (buf[0] >= 0 && (buf[0] == 'Y' || buf[0] == 'y')) |
415 | | except_throw(1, 1, "nasty exception"); |
416 | | } |
417 | | |
418 | | static void top_level(void) |
419 | | { |
420 | | except_cleanup_push(cleanup, "argument"); |
421 | | bottom_level(); |
422 | | except_cleanup_pop(0); |
423 | | } |
424 | | |
425 | | int main(int argc, char **argv) |
426 | | { |
427 | | static const except_id_t catch[] = { { 1, 1 }, { 1, 2 } }; |
428 | | except_t *ex; |
429 | | char *msg; |
430 | | |
431 | | /* |
432 | | * Nested exception ``try blocks'' |
433 | | */ |
434 | | |
435 | | /* outer */ |
436 | | except_try_push(catch, 2, &ex); |
437 | | if (!ex) { |
438 | | /* inner */ |
439 | | except_try_push(catch, 2, &ex); |
440 | | if (!ex) { |
441 | | top_level(); |
442 | | } else { |
443 | | /* inner catch */ |
444 | | msg = except_message(ex); |
445 | | if (msg == NULL) { |
446 | | printf("caught exception (inner): s=%lu, c=%lu\n", |
447 | | except_group(ex), except_code(ex)); |
448 | | } else { |
449 | | printf("caught exception (inner): \"%s\", s=%lu, c=%lu\n", |
450 | | msg, except_group(ex), except_code(ex)); |
451 | | } |
452 | | except_rethrow(ex); |
453 | | } |
454 | | except_try_pop(); |
455 | | } else { |
456 | | /* outer catch */ |
457 | | msg = except_message(ex); |
458 | | if (msg == NULL) { |
459 | | printf("caught exception (outer): s=%lu, c=%lu\n", |
460 | | except_group(ex), except_code(ex)); |
461 | | } else { |
462 | | printf("caught exception (outer): \"%s\", s=%lu, c=%lu\n", |
463 | | except_message(ex), except_group(ex), except_code(ex)); |
464 | | } |
465 | | } |
466 | | except_try_pop(); |
467 | | except_throw(99, 99, "exception in main"); |
468 | | return 0; |
469 | | } |
470 | | |
471 | | |
472 | | #endif /* KAZLIB_TEST_MAIN */ |
473 | | |
474 | | /* |
475 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
476 | | * |
477 | | * Local variables: |
478 | | * c-basic-offset: 4 |
479 | | * tab-width: 8 |
480 | | * indent-tabs-mode: nil |
481 | | * End: |
482 | | * |
483 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
484 | | * :indentSize=4:tabSize=8:noTabs=true: |
485 | | */ |