Don't pass the result of tvb_get_ptr() into a %s format string: the string may
[obnox/wireshark/wip.git] / epan / except.c
1 /*
2  * Portable Exception Handling for ANSI C.
3  * Copyright (C) 1999 Kaz Kylheku <kaz@ashi.footprints.net>
4  *
5  * $Id$
6  *
7  * Free Software License:
8  *
9  * All rights are reserved by the author, with the following exceptions:
10  * Permission is granted to freely reproduce and distribute this software,
11  * possibly in exchange for a fee, provided that this copyright notice appears
12  * intact. Permission is also granted to adapt this software to produce
13  * derivative works, as long as the modified versions carry this copyright
14  * notice and additional notices stating that the work has been modified.
15  * This source code may be translated into executable form and incorporated
16  * into proprietary software; there is no requirement for such software to
17  * contain a copyright notice related to this source.
18  */
19
20 /*
21  * Modified to support throwing an exception with a null message pointer,
22  * and to have the message not be const (as we generate messages with
23  * "g_strdup_sprintf()", which means they need to be freed; using
24  * a null message means that we don't have to use a special string
25  * for exceptions with no message, and don't have to worry about
26  * not freeing that).
27  */
28
29 #include <assert.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <limits.h>
34
35 #include <glib.h>
36
37 #include "except.h"
38
39 #ifdef _WIN32
40 /* IsDebuggerPresent() needs this #define! */
41 #define _WIN32_WINNT 0x0400
42 #include <windows.h>
43 #include "exceptions.h"
44 #endif
45
46 #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   /* no thread support */
134
135 static int init_counter;
136 static void unhandled_catcher(except_t *);
137 static void (*uh_catcher_ptr)(except_t *) = unhandled_catcher;
138 /* We need this 'size_t' cast due to a glitch in GLib where g_malloc was prototyped
139  * as 'gpointer g_malloc (gulong n_bytes)'. This was later fixed to the correct prototype
140  * 'gpointer g_malloc (gsize n_bytes)'. In Wireshark we use the latter prototype
141  * throughout the code. We can get away with this even with older versions of GLib by
142  * adding a '(void *(*)(size_t))' cast whenever we refer to g_malloc. The only platform
143  * supported by Wireshark where this isn't safe (sizeof size_t != sizeof gulong) is Win64.
144  * However, we _always_ bundle the newest version of GLib on this platform so
145  * the size_t issue doesn't exists here. Pheew.. */
146 static void *(*allocator)(size_t) = (void *(*)(size_t)) g_malloc;
147 static void (*deallocator)(void *) = g_free;
148 static struct except_stacknode *stack_top;
149
150 #define get_top() (stack_top)
151 #define set_top(T) (stack_top = (T))
152 #define get_catcher() (uh_catcher_ptr)
153 #define set_catcher(C) (uh_catcher_ptr = (C))
154 #define get_alloc() (allocator)
155 #define set_alloc(A) (allocator = (A))
156 #define get_dealloc() (deallocator)
157 #define set_dealloc(D) (deallocator = (D))
158
159 int except_init(void)
160 {
161     assert (init_counter < INT_MAX);
162     init_counter++;
163     return 1;
164 }
165
166 void except_deinit(void)
167 {
168     assert (init_counter > 0);
169     init_counter--;
170 }
171
172 #endif
173
174
175 static int match(const volatile except_id_t *thrown, const except_id_t *caught)
176 {
177     int group_match = (caught->except_group == XCEPT_GROUP_ANY ||
178         caught->except_group == thrown->except_group);
179     int code_match = (caught->except_code == XCEPT_CODE_ANY ||
180         caught->except_code == thrown->except_code);
181
182     return group_match && code_match;
183 }
184
185 G_GNUC_NORETURN static void do_throw(except_t *except)
186 {
187     struct except_stacknode *top;
188
189     assert (except->except_id.except_group != 0 &&
190         except->except_id.except_code != 0);
191
192     for (top = get_top(); top != 0; top = top->except_down) {
193         if (top->except_type == XCEPT_CLEANUP) {
194             top->except_info.except_cleanup->except_func(top->except_info.except_cleanup->except_context);
195         } else {
196             struct except_catch *catcher = top->except_info.except_catcher;
197             const except_id_t *pi = catcher->except_id;
198             size_t i;
199
200             assert (top->except_type == XCEPT_CATCHER);
201             except_free(catcher->except_obj.except_dyndata);
202
203             for (i = 0; i < catcher->except_size; pi++, i++) {
204                 if (match(&except->except_id, pi)) {
205                     catcher->except_obj = *except;
206                     set_top(top);
207                     longjmp(catcher->except_jmp, 1);
208                 }
209             }
210         }
211     }
212
213     set_top(top);
214     get_catcher()(except);      /* unhandled exception */
215     abort();
216 }
217
218 static void unhandled_catcher(except_t *except)
219 {
220     if (except->except_message == NULL) {
221         fprintf(stderr, "Unhandled exception (group=%ld, code=%ld)\n",
222                 except->except_id.except_group,
223                 except->except_id.except_code);
224     } else {
225         fprintf(stderr, "Unhandled exception (\"%s\", group=%ld, code=%ld)\n",
226                 except->except_message, except->except_id.except_group,
227                 except->except_id.except_code);
228     }
229     abort();
230 }
231
232 static void stack_push(struct except_stacknode *node)
233 {
234     node->except_down = get_top();
235     set_top(node);
236 }
237
238 void except_setup_clean(struct except_stacknode *esn,
239         struct except_cleanup *ecl, void (*cleanf)(void *), void *context)
240 {
241     esn->except_type = XCEPT_CLEANUP;
242     ecl->except_func = cleanf;
243     ecl->except_context = context;
244     esn->except_info.except_cleanup = ecl;
245     stack_push(esn);
246 }
247
248 void except_setup_try(struct except_stacknode *esn,
249         struct except_catch *ech, const except_id_t id[], size_t size)
250 {
251    ech->except_id = id;
252    ech->except_size = size;
253    ech->except_obj.except_dyndata = 0;
254    esn->except_type = XCEPT_CATCHER;
255    esn->except_info.except_catcher = ech;
256    stack_push(esn);
257 }
258
259 struct except_stacknode *except_pop(void)
260 {
261     struct except_stacknode *top = get_top();
262     set_top(top->except_down);
263     return top;
264 }
265
266 G_GNUC_NORETURN void except_rethrow(except_t *except)
267 {
268     struct except_stacknode *top = get_top();
269     assert (top != 0);
270     assert (top->except_type == XCEPT_CATCHER);
271     assert (&top->except_info.except_catcher->except_obj == except);
272     set_top(top->except_down);
273     do_throw(except);
274 }
275
276 G_GNUC_NORETURN void except_throw(long group, long code, const char *msg)
277 {
278     except_t except;
279
280     except.except_id.except_group = group;
281     except.except_id.except_code = code;
282     except.except_message = msg;
283     except.except_dyndata = 0;
284
285 #ifdef _WIN32
286     if (code == DissectorError && IsDebuggerPresent()) {
287         DebugBreak();
288     }
289 #endif
290
291     do_throw(&except);
292 }
293
294 G_GNUC_NORETURN void except_throwd(long group, long code, const char *msg, void *data)
295 {
296     except_t except;
297
298     except.except_id.except_group = group;
299     except.except_id.except_code = code;
300     except.except_message = msg;
301     except.except_dyndata = data;
302
303     do_throw(&except);
304 }
305
306 /*
307  * XXX - should we use g_strdup_sprintf() here, so we're not limited by
308  * XCEPT_BUFFER_SIZE?  We could then just use this to generate formatted
309  * messages.
310  */
311 G_GNUC_NORETURN void except_throwf(long group, long code, const char *fmt, ...)
312 {
313     char *buf = except_alloc(XCEPT_BUFFER_SIZE);
314     va_list vl;
315
316     va_start (vl, fmt);
317     g_vsnprintf(buf, XCEPT_BUFFER_SIZE, fmt, vl);
318     va_end (vl);
319     except_throwd(group, code, buf, buf);
320 }
321
322 void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *)
323 {
324     void (*old_catcher)(except_t *) = get_catcher();
325     set_catcher(new_catcher);
326     return old_catcher;
327 }
328
329 #undef except_code
330 #undef except_group
331 #undef except_message
332 #undef except_data
333
334 unsigned long except_code(except_t *ex)
335 {
336     return ex->except_id.except_code;
337 }
338
339 unsigned long except_group(except_t *ex)
340 {
341     return ex->except_id.except_group;
342 }
343
344 const char *except_message(except_t *ex)
345 {
346     return ex->except_message;
347 }
348
349 void *except_data(except_t *ex)
350 {
351     return ex->except_dyndata;
352 }
353
354 void *except_take_data(except_t *ex)
355 {
356     void *data = ex->except_dyndata;
357     ex->except_dyndata = 0;
358     return data;
359 }
360
361 void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *))
362 {
363     set_alloc(alloc);
364     set_dealloc(dealloc);
365 }
366
367 void *except_alloc(size_t size)
368 {
369     void *ptr = get_alloc()(size);
370
371     if (ptr == 0)
372         except_throw(XCEPT_BAD_ALLOC, 0, "out of memory");
373     return ptr;
374 }
375
376 void except_free(void *ptr)
377 {
378     get_dealloc()(ptr);
379 }
380
381 #ifdef KAZLIB_TEST_MAIN
382
383 #include <stdio.h>
384 #include <ctype.h>
385
386 static void cleanup(void *arg)
387 {
388     printf("cleanup(\"%s\") called\n", (char *) arg);
389 }
390
391 static void bottom_level(void)
392 {
393     char buf[256];
394     printf("throw exception? "); fflush(stdout);
395     fgets(buf, sizeof buf, stdin);
396
397     if (buf[0] >= 0 && toupper(buf[0]) == 'Y')
398         except_throw(1, 1, "nasty exception");
399 }
400
401 static void top_level(void)
402 {
403     except_cleanup_push(cleanup, "argument");
404     bottom_level();
405     except_cleanup_pop(0);
406 }
407
408 int main(int argc, char **argv)
409 {
410     static const except_id_t catch[] = { { 1, 1 }, { 1, 2 } };
411     except_t *ex;
412     char *msg;
413
414     /*
415      * Nested exception ``try blocks''
416      */
417
418     /* outer */
419     except_try_push(catch, 2, &ex);
420     if (!ex) {
421         /* inner */
422         except_try_push(catch, 2, &ex);
423         if (!ex) {
424             top_level();
425         } else {
426             /* inner catch */
427             msg = except_message(ex);
428             if (msg == NULL) {
429                 printf("caught exception (inner): s=%ld, c=%ld\n",
430                        except_group(ex), except_code(ex));
431             } else {
432                 printf("caught exception (inner): \"%s\", s=%ld, c=%ld\n",
433                        msg, except_group(ex), except_code(ex));
434             }
435             except_rethrow(ex);
436         }
437         except_try_pop();
438     } else {
439         /* outer catch */
440         msg = except_message(ex);
441         if (msg == NULL) {
442             printf("caught exception (outer): s=%ld, c=%ld\n",
443                    except_group(ex), except_code(ex));
444         } else {
445             printf("caught exception (outer): \"%s\", s=%ld, c=%ld\n",
446                    except_message(ex), except_group(ex), except_code(ex));
447         }
448     }
449     except_try_pop();
450     except_throw(99, 99, "exception in main");
451     return 0;
452 }
453
454
455 #endif