Statistics
| Branch: | Tag: | Revision:

root / src / libstrongswan / utils / leak_detective.c @ 86813bef129d8c2bc479239b497dd72f3288463d

History | View | Annotate | Download (11 KB)

1
/*
2
 * Copyright (C) 2006-2008 Martin Willi
3
 * Hochschule fuer Technik Rapperswil
4
 *
5
 * This program is free software; you can redistribute it and/or modify it
6
 * under the terms of the GNU General Public License as published by the
7
 * Free Software Foundation; either version 2 of the License, or (at your
8
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
9
 *
10
 * This program is distributed in the hope that it will be useful, but
11
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13
 * for more details.
14
 */
15
16
#define _GNU_SOURCE
17
#include <sched.h>
18
#include <stddef.h>
19
#include <string.h>
20
#include <stdio.h>
21
#include <malloc.h>
22
#include <signal.h>
23
#include <sys/socket.h>
24
#include <netinet/in.h>
25
#include <arpa/inet.h>
26
#include <unistd.h>
27
#include <syslog.h>
28
#include <pthread.h>
29
#include <netdb.h>
30
#include <locale.h>
31
32
#include "leak_detective.h"
33
34
#include <library.h>
35
#include <debug.h>
36
#include <utils/backtrace.h>
37
38
typedef struct private_leak_detective_t private_leak_detective_t;
39
40
/**
41
 * private data of leak_detective
42
 */
43
struct private_leak_detective_t {
44
45
    /**
46
     * public functions
47
     */
48
    leak_detective_t public;
49
};
50
51
/**
52
 * Magic value which helps to detect memory corruption. Yummy!
53
 */
54
#define MEMORY_HEADER_MAGIC 0x7ac0be11
55
56
/**
57
 * Magic written to tail of allocation
58
 */
59
#define MEMORY_TAIL_MAGIC 0xcafebabe
60
61
/**
62
 * Pattern which is filled in memory before freeing it
63
 */
64
#define MEMORY_FREE_PATTERN 0xFF
65
66
/**
67
 * Pattern which is filled in newly allocated memory
68
 */
69
#define MEMORY_ALLOC_PATTERN 0xEE
70
71
72
static void install_hooks(void);
73
static void uninstall_hooks(void);
74
static void *malloc_hook(size_t, const void *);
75
static void *realloc_hook(void *, size_t, const void *);
76
static void free_hook(void*, const void *);
77
78
void *(*old_malloc_hook)(size_t, const void *);
79
void *(*old_realloc_hook)(void *, size_t, const void *);
80
void (*old_free_hook)(void*, const void *);
81
82
static u_int count_malloc = 0;
83
static u_int count_free = 0;
84
static u_int count_realloc = 0;
85
86
typedef struct memory_header_t memory_header_t;
87
typedef struct memory_tail_t memory_tail_t;
88
89
/**
90
 * Header which is prepended to each allocated memory block
91
 */
92
struct memory_header_t {
93
94
    /**
95
     * Number of bytes following after the header
96
     */
97
    u_int bytes;
98
99
    /**
100
     * Pointer to previous entry in linked list
101
     */
102
    memory_header_t *previous;
103
104
    /**
105
     * Pointer to next entry in linked list
106
     */
107
    memory_header_t *next;
108
109
    /**
110
     * backtrace taken during (re-)allocation
111
     */
112
    backtrace_t *backtrace;
113
114
    /**
115
     * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
116
     */
117
    u_int32_t magic;
118
119
}__attribute__((__packed__));
120
121
/**
122
 * tail appended to each allocated memory block
123
 */
124
struct memory_tail_t {
125
126
    /**
127
     * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
128
     */
129
    u_int32_t magic;
130
131
}__attribute__((__packed__));
132
133
/**
134
 * first mem header is just a dummy to chain
135
 * the others on it...
136
 */
137
static memory_header_t first_header = {
138
    magic: MEMORY_HEADER_MAGIC,
139
    bytes: 0,
140
    backtrace: NULL,
141
    previous: NULL,
142
    next: NULL
143
};
144
145
/**
146
 * are the hooks currently installed?
147
 */
148
static bool installed = FALSE;
149
150
/**
151
 * Leak report white list
152
 *
153
 * List of functions using static allocation buffers or should be suppressed
154
 * otherwise on leak report.
155
 */
156
char *whitelist[] = {
157
    /* backtraces, including own */
158
    "backtrace_create",
159
    /* pthread stuff */
160
    "pthread_create",
161
    "pthread_setspecific",
162
    "__pthread_setspecific",
163
    /* glibc functions */
164
    "mktime",
165
    "__gmtime_r",
166
    "localtime_r",
167
    "tzset",
168
    "inet_ntoa",
169
    "strerror",
170
    "getprotobynumber",
171
    "getservbyport",
172
    "getservbyname",
173
    "gethostbyname2",
174
    "gethostbyname_r",
175
    "gethostbyname2_r",
176
    "getnetbyname",
177
    "getpwnam_r",
178
    "getgrnam_r",
179
    "register_printf_function",
180
    "register_printf_specifier",
181
    "syslog",
182
    "vsyslog",
183
    "getaddrinfo",
184
    "setlocale",
185
    /* ignore dlopen, as we do not dlclose to get proper leak reports */
186
    "dlopen",
187
    "dlerror",
188
    "dlclose",
189
    /* mysql functions */
190
    "mysql_init_character_set",
191
    "init_client_errs",
192
    "my_thread_init",
193
    /* fastcgi library */
194
    "FCGX_Init",
195
    /* libxml */
196
    "xmlInitCharEncodingHandlers",
197
    "xmlInitParser",
198
    "xmlInitParserCtxt",
199
    /* libcurl */
200
    "Curl_client_write",
201
    /* ClearSilver */
202
    "nerr_init",
203
    /* OpenSSL */
204
    "RSA_new_method",
205
    "DH_new_method",
206
    "ENGINE_load_builtin_engines",
207
    "OPENSSL_config",
208
    "ecdsa_check",
209
    /* libgcrypt */
210
    "gcry_control",
211
    "gcry_check_version",
212
    "gcry_randomize",
213
    "gcry_create_nonce",
214
};
215
216
/**
217
 * check if a stack frame contains functions listed above
218
 */
219
static bool is_whitelisted(backtrace_t *backtrace)
220
{
221
    int i;
222
    for (i = 0; i < sizeof(whitelist)/sizeof(char*); i++)
223
    {
224
        if (backtrace->contains_function(backtrace, whitelist[i]))
225
        {
226
            return TRUE;
227
        }
228
    }
229
    return FALSE;
230
}
231
232
/**
233
 * Report leaks at library destruction
234
 */
235
void report_leaks()
236
{
237
    memory_header_t *hdr;
238
    int leaks = 0, whitelisted = 0;
239
240
    for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
241
    {
242
        if (is_whitelisted(hdr->backtrace))
243
        {
244
            whitelisted++;
245
        }
246
        else
247
        {
248
            fprintf(stderr, "Leak (%d bytes at %p):\n", hdr->bytes, hdr + 1);
249
            /* skip the first frame, contains leak detective logic */
250
            hdr->backtrace->log(hdr->backtrace, stderr);
251
            leaks++;
252
        }
253
    }
254
255
    switch (leaks)
256
    {
257
        case 0:
258
            fprintf(stderr, "No leaks detected");
259
            break;
260
        case 1:
261
            fprintf(stderr, "One leak detected");
262
            break;
263
        default:
264
            fprintf(stderr, "%d leaks detected", leaks);
265
            break;
266
    }
267
    fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
268
}
269
270
/**
271
 * Installs the malloc hooks, enables leak detection
272
 */
273
static void install_hooks()
274
{
275
    if (!installed)
276
    {
277
        old_malloc_hook = __malloc_hook;
278
        old_realloc_hook = __realloc_hook;
279
        old_free_hook = __free_hook;
280
        __malloc_hook = malloc_hook;
281
        __realloc_hook = realloc_hook;
282
        __free_hook = free_hook;
283
        installed = TRUE;
284
    }
285
}
286
287
/**
288
 * Uninstalls the malloc hooks, disables leak detection
289
 */
290
static void uninstall_hooks()
291
{
292
    if (installed)
293
    {
294
        __malloc_hook = old_malloc_hook;
295
        __free_hook = old_free_hook;
296
        __realloc_hook = old_realloc_hook;
297
        installed = FALSE;
298
    }
299
}
300
301
/**
302
 * Hook function for malloc()
303
 */
304
void *malloc_hook(size_t bytes, const void *caller)
305
{
306
    memory_header_t *hdr;
307
    memory_tail_t *tail;
308
    pthread_t thread_id = pthread_self();
309
    int oldpolicy;
310
    struct sched_param oldparams, params;
311
312
    pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
313
314
    params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
315
    pthread_setschedparam(thread_id, SCHED_FIFO, &params);
316
317
    count_malloc++;
318
    uninstall_hooks();
319
    hdr = malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
320
    tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
321
    /* set to something which causes crashes */
322
    memset(hdr, MEMORY_ALLOC_PATTERN,
323
           sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
324
325
    hdr->magic = MEMORY_HEADER_MAGIC;
326
    hdr->bytes = bytes;
327
    hdr->backtrace = backtrace_create(3);
328
    tail->magic = MEMORY_TAIL_MAGIC;
329
    install_hooks();
330
331
    /* insert at the beginning of the list */
332
    hdr->next = first_header.next;
333
    if (hdr->next)
334
    {
335
        hdr->next->previous = hdr;
336
    }
337
    hdr->previous = &first_header;
338
    first_header.next = hdr;
339
340
    pthread_setschedparam(thread_id, oldpolicy, &oldparams);
341
342
    return hdr + 1;
343
}
344
345
/**
346
 * Hook function for free()
347
 */
348
void free_hook(void *ptr, const void *caller)
349
{
350
    memory_header_t *hdr, *current;
351
    memory_tail_t *tail;
352
    backtrace_t *backtrace;
353
    pthread_t thread_id = pthread_self();
354
    int oldpolicy;
355
    struct sched_param oldparams, params;
356
    bool found = FALSE;
357
358
    /* allow freeing of NULL */
359
    if (ptr == NULL)
360
    {
361
        return;
362
    }
363
    hdr = ptr - sizeof(memory_header_t);
364
    tail = ptr + hdr->bytes;
365
366
    pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
367
368
    params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
369
    pthread_setschedparam(thread_id, SCHED_FIFO, &params);
370
371
    count_free++;
372
    uninstall_hooks();
373
    if (hdr->magic != MEMORY_HEADER_MAGIC ||
374
        tail->magic != MEMORY_TAIL_MAGIC)
375
    {
376
        for (current = &first_header; current != NULL; current = current->next)
377
        {
378
            if (current == hdr)
379
            {
380
                found = TRUE;
381
                break;
382
            }
383
        }
384
        if (found)
385
        {
386
            /* memory was allocated by our hooks but is corrupted */
387
            fprintf(stderr, "freeing corrupted memory (%p): "
388
                    "header magic 0x%x, tail magic 0x%x:\n",
389
                    ptr, hdr->magic, tail->magic);
390
        }
391
        else
392
        {
393
            /* memory was not allocated by our hooks */
394
            fprintf(stderr, "freeing invalid memory (%p)", ptr);
395
        }
396
        backtrace = backtrace_create(3);
397
        backtrace->log(backtrace, stderr);
398
        backtrace->destroy(backtrace);
399
    }
400
    else
401
    {
402
        /* remove item from list */
403
        if (hdr->next)
404
        {
405
            hdr->next->previous = hdr->previous;
406
        }
407
        hdr->previous->next = hdr->next;
408
        hdr->backtrace->destroy(hdr->backtrace);
409
410
        /* clear MAGIC, set mem to something remarkable */
411
        memset(hdr, MEMORY_FREE_PATTERN,
412
               sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
413
414
        free(hdr);
415
    }
416
417
    install_hooks();
418
    pthread_setschedparam(thread_id, oldpolicy, &oldparams);
419
}
420
421
/**
422
 * Hook function for realloc()
423
 */
424
void *realloc_hook(void *old, size_t bytes, const void *caller)
425
{
426
    memory_header_t *hdr;
427
    memory_tail_t *tail;
428
    backtrace_t *backtrace;
429
    pthread_t thread_id = pthread_self();
430
    int oldpolicy;
431
    struct sched_param oldparams, params;
432
433
    /* allow reallocation of NULL */
434
    if (old == NULL)
435
    {
436
        return malloc_hook(bytes, caller);
437
    }
438
439
    hdr = old - sizeof(memory_header_t);
440
    tail = old + hdr->bytes;
441
442
    pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
443
444
    params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
445
    pthread_setschedparam(thread_id, SCHED_FIFO, &params);
446
447
    count_realloc++;
448
    uninstall_hooks();
449
    if (hdr->magic != MEMORY_HEADER_MAGIC ||
450
        tail->magic != MEMORY_TAIL_MAGIC)
451
    {
452
        fprintf(stderr, "reallocating invalid memory (%p): "
453
                "header magic 0x%x, tail magic 0x%x:\n",
454
                old, hdr->magic, tail->magic);
455
        backtrace = backtrace_create(3);
456
        backtrace->log(backtrace, stderr);
457
        backtrace->destroy(backtrace);
458
    }
459
    /* clear tail magic, allocate, set tail magic */
460
    memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
461
    hdr = realloc(hdr, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
462
    tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
463
    tail->magic = MEMORY_TAIL_MAGIC;
464
465
    /* update statistics */
466
    hdr->bytes = bytes;
467
    hdr->backtrace->destroy(hdr->backtrace);
468
    hdr->backtrace = backtrace_create(3);
469
470
    /* update header of linked list neighbours */
471
    if (hdr->next)
472
    {
473
        hdr->next->previous = hdr;
474
    }
475
    hdr->previous->next = hdr;
476
    install_hooks();
477
    pthread_setschedparam(thread_id, oldpolicy, &oldparams);
478
    return hdr + 1;
479
}
480
481
/**
482
 * Implementation of leak_detective_t.destroy
483
 */
484
static void destroy(private_leak_detective_t *this)
485
{
486
    if (installed)
487
    {
488
        uninstall_hooks();
489
        report_leaks();
490
    }
491
    free(this);
492
}
493
494
/*
495
 * see header file
496
 */
497
leak_detective_t *leak_detective_create()
498
{
499
    private_leak_detective_t *this = malloc_thing(private_leak_detective_t);
500
501
    this->public.destroy = (void(*)(leak_detective_t*))destroy;
502
503
    if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
504
    {
505
        cpu_set_t mask;
506
507
        CPU_ZERO(&mask);
508
        CPU_SET(0, &mask);
509
510
        if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) != 0)
511
        {
512
            fprintf(stderr, "setting CPU affinity failed: %m");
513
        }
514
515
        lib->leak_detective = TRUE;
516
        install_hooks();
517
    }
518
    return &this->public;
519
}
520