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  * "g_strdup_sprintf()", 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 
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 #include <windows.h>
41 #include "exceptions.h"
42 #endif
43 
44 #define XCEPT_BUFFER_SIZE 1024
45 
46 #ifdef KAZLIB_POSIX_THREADS
47 
48 #include <pthread.h>
49 
50 static pthread_mutex_t init_mtx = PTHREAD_MUTEX_INITIALIZER;
51 static int init_counter;
52 static pthread_key_t top_key;
53 static pthread_key_t uh_key;
54 static pthread_key_t alloc_key;
55 static pthread_key_t dealloc_key;
56 static void unhandled_catcher(except_t *);
57 
58 #define get_top() ((struct except_stacknode *) pthread_getspecific(top_key))
59 #define set_top(T) (pthread_setspecific(top_key, (T)), (void)((T) == (struct except_stacknode *) 0))
60 #define set_catcher(C) (pthread_setspecific(uh_key, (void *) (C)), (void)((C) == (void (*)(except_t *)) 0))
61 #define set_alloc(A) (pthread_setspecific(alloc_key, (void *) (A)), (void)((A) == (void *(*)(size_t)) 0))
62 #define set_dealloc(D) (pthread_setspecific(dealloc_key, (void *) (D)), (void)((D) == (void (*)(void *)) 0))
63 
get_catcher(void)64 static void (*get_catcher(void))(except_t *)
65 {
66     void (*catcher)(except_t *) = (void (*)(except_t *)) pthread_getspecific(uh_key);
67     return (catcher == 0) ? unhandled_catcher : catcher;
68 }
69 
get_alloc(void)70 static void *(*get_alloc(void))(size_t)
71 {
72     void *(*alloc)(size_t) = (void *(*)(size_t)) pthread_getspecific(alloc_key);
73     return (alloc == 0) ? malloc : alloc;
74 }
75 
get_dealloc(void)76 static void (*get_dealloc(void))(void *)
77 {
78     void (*dealloc)(void *) = (void (*)(void *)) pthread_getspecific(dealloc_key);
79     return (dealloc == 0) ? free : dealloc;
80 }
81 
except_init(void)82 int except_init(void)
83 {
84     int retval = 1;
85 
86     pthread_mutex_lock(&init_mtx);
87 
88     assert (init_counter < INT_MAX);
89 
90     if (init_counter++ == 0) {
91         int top_ok = (pthread_key_create(&top_key, 0) == 0);
92         int uh_ok = (pthread_key_create(&uh_key, 0) == 0);
93         int alloc_ok = (pthread_key_create(&alloc_key, 0) == 0);
94         int dealloc_ok = (pthread_key_create(&dealloc_key, 0) == 0);
95 
96         if (!top_ok || !uh_ok || !alloc_ok || !dealloc_ok) {
97             retval = 0;
98             init_counter = 0;
99             if (top_ok)
100                 pthread_key_delete(top_key);
101             if (uh_ok)
102                 pthread_key_delete(uh_key);
103             if (alloc_ok)
104                 pthread_key_delete(alloc_key);
105             if (dealloc_ok)
106                 pthread_key_delete(dealloc_key);
107         }
108     }
109 
110     pthread_mutex_unlock(&init_mtx);
111 
112     return retval;
113 }
114 
except_deinit(void)115 void except_deinit(void)
116 {
117     pthread_mutex_lock(&init_mtx);
118 
119     assert (init_counter > 0);
120 
121     if (--init_counter == 0) {
122         pthread_key_delete(top_key);
123         pthread_key_delete(uh_key);
124         pthread_key_delete(alloc_key);
125         pthread_key_delete(dealloc_key);
126     }
127 
128     pthread_mutex_unlock(&init_mtx);
129 }
130 
131 #else /* no thread support */
132 
133 static int init_counter;
134 static void unhandled_catcher(except_t *);
135 static void (*uh_catcher_ptr)(except_t *) = unhandled_catcher;
136 /* We need this 'size_t' cast due to a glitch in GLib where g_malloc was prototyped
137  * as 'gpointer g_malloc (gulong n_bytes)'. This was later fixed to the correct prototype
138  * 'gpointer g_malloc (gsize n_bytes)'. In Wireshark we use the latter prototype
139  * throughout the code. We can get away with this even with older versions of GLib by
140  * adding a '(void *(*)(size_t))' cast whenever we refer to g_malloc. The only platform
141  * supported by Wireshark where this isn't safe (sizeof size_t != sizeof gulong) is Win64.
142  * However, we _always_ bundle the newest version of GLib on this platform so
143  * the size_t issue doesn't exists here. Pheew.. */
144 static void *(*allocator)(size_t) = (void *(*)(size_t)) g_malloc;
145 static void (*deallocator)(void *) = g_free;
146 static struct except_stacknode *stack_top;
147 
148 #define get_top() (stack_top)
149 #define set_top(T) (stack_top = (T))
150 #define get_catcher() (uh_catcher_ptr)
151 #define set_catcher(C) (uh_catcher_ptr = (C))
152 #define get_alloc() (allocator)
153 #define set_alloc(A) (allocator = (A))
154 #define get_dealloc() (deallocator)
155 #define set_dealloc(D) (deallocator = (D))
156 
except_init(void)157 int except_init(void)
158 {
159     assert (init_counter < INT_MAX);
160     init_counter++;
161     return 1;
162 }
163 
except_deinit(void)164 void except_deinit(void)
165 {
166     assert (init_counter > 0);
167     init_counter--;
168 }
169 
170 #endif
171 
172 
match(const volatile except_id_t * thrown,const except_id_t * caught)173 static int match(const volatile except_id_t *thrown, const except_id_t *caught)
174 {
175     int group_match = (caught->except_group == XCEPT_GROUP_ANY ||
176         caught->except_group == thrown->except_group);
177     int code_match = (caught->except_code == XCEPT_CODE_ANY ||
178         caught->except_code == thrown->except_code);
179 
180     return group_match && code_match;
181 }
182 
do_throw(except_t * except)183 WS_NORETURN static void do_throw(except_t *except)
184 {
185     struct except_stacknode *top;
186 
187     assert (except->except_id.except_group != 0 &&
188         except->except_id.except_code != 0);
189 
190     for (top = get_top(); top != 0; top = top->except_down) {
191         if (top->except_type == XCEPT_CLEANUP) {
192             top->except_info.except_cleanup->except_func(top->except_info.except_cleanup->except_context);
193         } else {
194             struct except_catch *catcher = top->except_info.except_catcher;
195             const except_id_t *pi = catcher->except_id;
196             size_t i;
197 
198             assert (top->except_type == XCEPT_CATCHER);
199             except_free(catcher->except_obj.except_dyndata);
200 
201             for (i = 0; i < catcher->except_size; pi++, i++) {
202                 if (match(&except->except_id, pi)) {
203                     catcher->except_obj = *except;
204                     set_top(top);
205                     longjmp(catcher->except_jmp, 1);
206                 }
207             }
208         }
209     }
210 
211     set_top(top);
212     get_catcher()(except); /* unhandled exception */
213     abort();
214 }
215 
unhandled_catcher(except_t * except)216 static void unhandled_catcher(except_t *except)
217 {
218     if (except->except_message == NULL) {
219         fprintf(stderr, "Unhandled exception (group=%lu, code=%lu)\n",
220                 except->except_id.except_group,
221                 except->except_id.except_code);
222     } else {
223         fprintf(stderr, "Unhandled exception (\"%s\", group=%lu, code=%lu)\n",
224                 except->except_message, except->except_id.except_group,
225                 except->except_id.except_code);
226     }
227     abort();
228 }
229 
stack_push(struct except_stacknode * node)230 static void stack_push(struct except_stacknode *node)
231 {
232     node->except_down = get_top();
233     set_top(node);
234 }
235 
except_setup_clean(struct except_stacknode * esn,struct except_cleanup * ecl,void (* cleanf)(void *),void * context)236 void except_setup_clean(struct except_stacknode *esn,
237         struct except_cleanup *ecl, void (*cleanf)(void *), void *context)
238 {
239     esn->except_type = XCEPT_CLEANUP;
240     ecl->except_func = cleanf;
241     ecl->except_context = context;
242     esn->except_info.except_cleanup = ecl;
243     stack_push(esn);
244 }
245 
except_setup_try(struct except_stacknode * esn,struct except_catch * ech,const except_id_t id[],size_t size)246 void except_setup_try(struct except_stacknode *esn,
247         struct except_catch *ech, const except_id_t id[], size_t size)
248 {
249    ech->except_id = id;
250    ech->except_size = size;
251    ech->except_obj.except_dyndata = 0;
252    esn->except_type = XCEPT_CATCHER;
253    esn->except_info.except_catcher = ech;
254    stack_push(esn);
255 }
256 
except_pop(void)257 struct except_stacknode *except_pop(void)
258 {
259     struct except_stacknode *top = get_top();
260     assert (top->except_type == XCEPT_CLEANUP || top->except_type == XCEPT_CATCHER);
261     set_top(top->except_down);
262     return top;
263 }
264 
except_rethrow(except_t * except)265 WS_NORETURN void except_rethrow(except_t *except)
266 {
267     struct except_stacknode *top = get_top();
268     assert (top != 0);
269     assert (top->except_type == XCEPT_CATCHER);
270     assert (&top->except_info.except_catcher->except_obj == except);
271     set_top(top->except_down);
272     do_throw(except);
273 }
274 
except_throw(long group,long code,const char * msg)275 WS_NORETURN void except_throw(long group, long code, const char *msg)
276 {
277     except_t except;
278 
279     except.except_id.except_group = group;
280     except.except_id.except_code = code;
281     except.except_message = msg;
282     except.except_dyndata = 0;
283 
284 #ifdef _WIN32
285     if (code == DissectorError && IsDebuggerPresent()) {
286         DebugBreak();
287     }
288 #endif
289 
290     do_throw(&except);
291 }
292 
except_throwd(long group,long code,const char * msg,void * data)293 WS_NORETURN void except_throwd(long group, long code, const char *msg, void *data)
294 {
295     except_t except;
296 
297     except.except_id.except_group = group;
298     except.except_id.except_code = code;
299     except.except_message = msg;
300     except.except_dyndata = data;
301 
302     do_throw(&except);
303 }
304 
305 /*
306  * XXX - should we use g_strdup_sprintf() here, so we're not limited by
307  * XCEPT_BUFFER_SIZE?  We could then just use this to generate formatted
308  * messages.
309  */
except_vthrowf(long group,long code,const char * fmt,va_list vl)310 WS_NORETURN void except_vthrowf(long group, long code, const char *fmt,
311                                 va_list vl)
312 {
313     char *buf = (char *)except_alloc(XCEPT_BUFFER_SIZE);
314 
315     g_vsnprintf(buf, XCEPT_BUFFER_SIZE, fmt, vl);
316     except_throwd(group, code, buf, buf);
317 }
318 
except_throwf(long group,long code,const char * fmt,...)319 WS_NORETURN void except_throwf(long group, long code, const char *fmt, ...)
320 {
321     va_list vl;
322 
323     va_start (vl, fmt);
324     except_vthrowf(group, code, fmt, vl);
325     va_end (vl);
326 }
327 
except_unhandled_catcher(void (* new_catcher)(except_t *))328 void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *)
329 {
330     void (*old_catcher)(except_t *) = get_catcher();
331     set_catcher(new_catcher);
332     return old_catcher;
333 }
334 
335 #undef except_code
336 #undef except_group
337 #undef except_message
338 #undef except_data
339 
except_code(except_t * ex)340 unsigned long except_code(except_t *ex)
341 {
342     return ex->except_id.except_code;
343 }
344 
except_group(except_t * ex)345 unsigned long except_group(except_t *ex)
346 {
347     return ex->except_id.except_group;
348 }
349 
except_message(except_t * ex)350 const char *except_message(except_t *ex)
351 {
352     return ex->except_message;
353 }
354 
except_data(except_t * ex)355 void *except_data(except_t *ex)
356 {
357     return ex->except_dyndata;
358 }
359 
except_take_data(except_t * ex)360 void *except_take_data(except_t *ex)
361 {
362     void *data = ex->except_dyndata;
363     ex->except_dyndata = 0;
364     return data;
365 }
366 
except_set_allocator(void * (* alloc)(size_t),void (* dealloc)(void *))367 void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *))
368 {
369     set_alloc(alloc);
370     set_dealloc(dealloc);
371 }
372 
except_alloc(size_t size)373 void *except_alloc(size_t size)
374 {
375     void *ptr = get_alloc()(size);
376 
377     if (ptr == 0)
378         except_throw(XCEPT_BAD_ALLOC, 0, "out of memory");
379     return ptr;
380 }
381 
except_free(void * ptr)382 void except_free(void *ptr)
383 {
384     get_dealloc()(ptr);
385 }
386 
387 #ifdef KAZLIB_TEST_MAIN
388 
389 
cleanup(void * arg)390 static void cleanup(void *arg)
391 {
392     printf("cleanup(\"%s\") called\n", (char *) arg);
393 }
394 
bottom_level(void)395 static void bottom_level(void)
396 {
397     char buf[256];
398     printf("throw exception? "); fflush(stdout);
399     fgets(buf, sizeof buf, stdin);
400 
401     if (buf[0] >= 0 && (buf[0] == 'Y' || buf[0] == 'y'))
402         except_throw(1, 1, "nasty exception");
403 }
404 
top_level(void)405 static void top_level(void)
406 {
407     except_cleanup_push(cleanup, "argument");
408     bottom_level();
409     except_cleanup_pop(0);
410 }
411 
main(int argc,char ** argv)412 int main(int argc, char **argv)
413 {
414     static const except_id_t catch[] = { { 1, 1 }, { 1, 2 } };
415     except_t *ex;
416     char *msg;
417 
418     /*
419      * Nested exception ``try blocks''
420      */
421 
422     /* outer */
423     except_try_push(catch, 2, &ex);
424     if (!ex) {
425         /* inner */
426         except_try_push(catch, 2, &ex);
427         if (!ex) {
428             top_level();
429         } else {
430             /* inner catch */
431             msg = except_message(ex);
432             if (msg == NULL) {
433                 printf("caught exception (inner): s=%lu, c=%lu\n",
434                        except_group(ex), except_code(ex));
435             } else {
436                 printf("caught exception (inner): \"%s\", s=%lu, c=%lu\n",
437                        msg, except_group(ex), except_code(ex));
438             }
439             except_rethrow(ex);
440         }
441         except_try_pop();
442     } else {
443         /* outer catch */
444         msg = except_message(ex);
445         if (msg == NULL) {
446             printf("caught exception (outer): s=%lu, c=%lu\n",
447                    except_group(ex), except_code(ex));
448         } else {
449             printf("caught exception (outer): \"%s\", s=%lu, c=%lu\n",
450                    except_message(ex), except_group(ex), except_code(ex));
451         }
452     }
453     except_try_pop();
454     except_throw(99, 99, "exception in main");
455     return 0;
456 }
457 
458 
459 #endif /* KAZLIB_TEST_MAIN */
460 
461 /*
462  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
463  *
464  * Local variables:
465  * c-basic-offset: 4
466  * tab-width: 8
467  * indent-tabs-mode: nil
468  * End:
469  *
470  * vi: set shiftwidth=4 tabstop=8 expandtab:
471  * :indentSize=4:tabSize=8:noTabs=true:
472  */
473