Coverage Report

Created: 2026-01-02 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */