]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libstrongswan/utils/leak_detective.c
renamed xml plugin to smp to avoid confusion
[thirdparty/strongswan.git] / src / libstrongswan / utils / leak_detective.c
CommitLineData
5113680f 1/*
552cc11b 2 * Copyright (C) 2006-2008 Martin Willi
5113680f
MW
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.
552cc11b
MW
14 *
15 * $Id$
5113680f
MW
16 */
17
552cc11b
MW
18#ifdef HAVE_DLADDR
19# define _GNU_SOURCE
20# include <dlfcn.h>
21#endif /* HAVE_DLADDR */
22
5113680f 23#include <stddef.h>
5113680f
MW
24#include <string.h>
25#include <stdio.h>
26#include <malloc.h>
5113680f
MW
27#include <signal.h>
28#include <sys/socket.h>
29#include <netinet/in.h>
30#include <arpa/inet.h>
31#include <dlfcn.h>
1d025fbc 32#include <unistd.h>
8bc96e08 33#include <syslog.h>
8bc96e08 34#include <pthread.h>
b1904247 35#include <netdb.h>
151168f6 36#include <printf.h>
552cc11b 37#include <locale.h>
5347a84f
MW
38#ifdef HAVE_BACKTRACE
39# include <execinfo.h>
40#endif /* HAVE_BACKTRACE */
5113680f
MW
41
42#include "leak_detective.h"
43
60356f33 44#include <library.h>
db7ef624 45#include <debug.h>
5113680f 46
552cc11b
MW
47typedef struct private_leak_detective_t private_leak_detective_t;
48
49/**
50 * private data of leak_detective
51 */
52struct private_leak_detective_t {
53
54 /**
55 * public functions
56 */
57 leak_detective_t public;
58};
5113680f
MW
59
60/**
269f7f44 61 * Magic value which helps to detect memory corruption. Yummy!
5113680f 62 */
269f7f44
MW
63#define MEMORY_HEADER_MAGIC 0x7ac0be11
64
65/**
66 * Pattern which is filled in memory before freeing it
67 */
68#define MEMORY_FREE_PATTERN 0xFF
69
70/**
71 * Pattern which is filled in newly allocated memory
72 */
73#define MEMORY_ALLOC_PATTERN 0xEE
74
5113680f 75
5113680f
MW
76static void install_hooks(void);
77static void uninstall_hooks(void);
78static void *malloc_hook(size_t, const void *);
79static void *realloc_hook(void *, size_t, const void *);
80static void free_hook(void*, const void *);
73760ca5
MW
81
82static u_int count_malloc = 0;
83static u_int count_free = 0;
84static u_int count_realloc = 0;
5113680f
MW
85
86typedef struct memory_header_t memory_header_t;
87
88/**
89 * Header which is prepended to each allocated memory block
90 */
91struct memory_header_t {
92 /**
93 * Magci byte which must(!) hold MEMORY_HEADER_MAGIC
94 */
95 u_int32_t magic;
96
97 /**
98 * Number of bytes following after the header
99 */
100 size_t bytes;
101
102 /**
103 * Stack frames at the time of allocation
104 */
105 void *stack_frames[STACK_FRAMES_COUNT];
106
107 /**
108 * Number of stacks frames obtained in stack_frames
109 */
110 int stack_frame_count;
111
112 /**
113 * Pointer to previous entry in linked list
114 */
115 memory_header_t *previous;
116
117 /**
118 * Pointer to next entry in linked list
119 */
120 memory_header_t *next;
121};
122
123/**
124 * first mem header is just a dummy to chain
125 * the others on it...
126 */
6b3292da 127static memory_header_t first_header = {
5113680f
MW
128 magic: MEMORY_HEADER_MAGIC,
129 bytes: 0,
130 stack_frame_count: 0,
131 previous: NULL,
132 next: NULL
133};
134
135/**
136 * standard hooks, used to temparily remove hooking
137 */
6b3292da 138static void *old_malloc_hook, *old_realloc_hook, *old_free_hook;
5113680f 139
1d025fbc 140/**
6b3292da 141 * are the hooks currently installed?
1d025fbc 142 */
6b3292da 143static bool installed = FALSE;
5113680f 144
8bc96e08 145/**
6b3292da 146 * Mutex to exclusivly uninstall hooks, access heap list
8bc96e08 147 */
fcfeb322 148static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
6b3292da 149
5113680f
MW
150
151/**
152 * log stack frames queried by backtrace()
151168f6
MW
153 * TODO: Dump symbols of static functions. This could be done with
154 * the addr2line utility or the GNU BFD Library...
5113680f 155 */
6b3292da 156static void log_stack_frames(void **stack_frames, int stack_frame_count)
5113680f 157{
e696757c 158#ifdef HAVE_BACKTRACE
5113680f
MW
159 char **strings;
160 size_t i;
161
552cc11b 162 strings = backtrace_symbols(stack_frames, stack_frame_count);
5113680f 163
552cc11b 164 fprintf(stderr, " dumping %d stack frame addresses\n", stack_frame_count);
5113680f
MW
165
166 for (i = 0; i < stack_frame_count; i++)
167 {
552cc11b
MW
168#ifdef HAVE_DLADDR
169 Dl_info info;
170
171 /* TODO: this is quite hackish, but it works. A more proper solution
172 * would execve addr2strongline and pipe the output to DBG1() */
173 if (dladdr(stack_frames[i], &info))
174 {
175 char cmd[1024];
176 void *ptr = stack_frames[i];
177
178 if (strstr(info.dli_fname, ".so"))
179 {
180 ptr = (void*)(stack_frames[i] - info.dli_fbase);
181 }
182 snprintf(cmd, sizeof(cmd), "addr2line -e %s %p", info.dli_fname, ptr);
183 if (info.dli_sname)
184 {
185 fprintf(stderr, " \e[33m%s\e[0m @ %p (\e[31m%s+0x%x\e[0m) [%p]\n",
186 info.dli_fname, info.dli_fbase, info.dli_sname,
187 stack_frames[i] - info.dli_saddr, stack_frames[i]);
188 }
189 else
190 {
191 fprintf(stderr, " \e[33m%s\e[0m @ %p [%p]\n", info.dli_fname,
192 info.dli_fbase, stack_frames[i]);
193 }
194 fprintf(stderr, " -> \e[32m");
195 system(cmd);
196 fprintf(stderr, "\e[0m");
197 }
198 else
199#endif /* HAVE_DLADDR */
200 {
201 fprintf(stderr, " %s\n", strings[i]);
202 }
5113680f
MW
203 }
204 free (strings);
e696757c 205#endif /* HAVE_BACKTRACE */
5113680f
MW
206}
207
986d23bd 208/**
552cc11b 209 * Leak report white list
151168f6 210 *
552cc11b
MW
211 * List of functions using static allocation buffers or should be suppressed
212 * otherwise on leak report.
986d23bd 213 */
552cc11b
MW
214char *whitelist[] = {
215 "pthread_create",
216 "pthread_setspecific",
217 "mktime",
218 "tzset",
219 "inet_ntoa",
220 "strerror",
221 "getprotobynumber",
222 "getservbyport",
223 "getservbyname",
224 "register_printf_function",
225 "syslog",
226 "vsyslog",
227 "dlopen",
228 "getaddrinfo",
229 "setlocale",
230 "mysql_init_character_set",
231 "init_client_errs",
232 "my_thread_init",
cf4caefa 233 "FCGX_Init",
986d23bd
MW
234};
235
236/**
552cc11b 237 * check if a stack frame contains functions listed above
986d23bd
MW
238 */
239static bool is_whitelisted(void **stack_frames, int stack_frame_count)
240{
241 int i, j;
242
552cc11b 243#ifdef HAVE_DLADDR
986d23bd
MW
244 for (i=0; i< stack_frame_count; i++)
245 {
552cc11b
MW
246 Dl_info info;
247
248 if (dladdr(stack_frames[i], &info) && info.dli_sname)
249 {
250 for (j = 0; j < sizeof(whitelist)/sizeof(char*); j++)
986d23bd 251 {
552cc11b
MW
252 if (streq(info.dli_sname, whitelist[j]))
253 {
254 return TRUE;
255 }
986d23bd
MW
256 }
257 }
258 }
552cc11b 259#endif /* HAVE_DLADDR */
986d23bd
MW
260 return FALSE;
261}
262
6b3292da
MW
263/**
264 * Report leaks at library destruction
265 */
266void report_leaks()
267{
268 memory_header_t *hdr;
cf4caefa 269 int leaks = 0, whitelisted = 0;
6b3292da 270
6b3292da
MW
271 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
272 {
cf4caefa
MW
273 if (is_whitelisted(hdr->stack_frames, hdr->stack_frame_count))
274 {
275 whitelisted++;
276 }
277 else
986d23bd 278 {
552cc11b
MW
279 fprintf(stderr, "Leak (%d bytes at %p):\n", hdr->bytes, hdr + 1);
280 /* skip the first frame, contains leak detective logic */
281 log_stack_frames(hdr->stack_frames + 1, hdr->stack_frame_count - 1);
986d23bd
MW
282 leaks++;
283 }
6b3292da
MW
284 }
285
286 switch (leaks)
287 {
288 case 0:
cf4caefa 289 fprintf(stderr, "No leaks detected");
6b3292da
MW
290 break;
291 case 1:
cf4caefa 292 fprintf(stderr, "One leak detected");
6b3292da
MW
293 break;
294 default:
cf4caefa 295 fprintf(stderr, "%d leaks detected", leaks);
6b3292da
MW
296 break;
297 }
cf4caefa 298 fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
6b3292da
MW
299}
300
5113680f
MW
301/**
302 * Installs the malloc hooks, enables leak detection
303 */
6b3292da 304static void install_hooks()
5113680f 305{
8bc96e08
MW
306 if (!installed)
307 {
308 old_malloc_hook = __malloc_hook;
309 old_realloc_hook = __realloc_hook;
310 old_free_hook = __free_hook;
311 __malloc_hook = malloc_hook;
312 __realloc_hook = realloc_hook;
313 __free_hook = free_hook;
314 installed = TRUE;
315 }
5113680f
MW
316}
317
318/**
319 * Uninstalls the malloc hooks, disables leak detection
320 */
6b3292da 321static void uninstall_hooks()
5113680f 322{
8bc96e08
MW
323 if (installed)
324 {
325 __malloc_hook = old_malloc_hook;
326 __free_hook = old_free_hook;
327 __realloc_hook = old_realloc_hook;
328 installed = FALSE;
329 }
5113680f
MW
330}
331
332/**
333 * Hook function for malloc()
334 */
6b3292da 335void *malloc_hook(size_t bytes, const void *caller)
5113680f
MW
336{
337 memory_header_t *hdr;
338
339 pthread_mutex_lock(&mutex);
73760ca5 340 count_malloc++;
5113680f
MW
341 uninstall_hooks();
342 hdr = malloc(bytes + sizeof(memory_header_t));
986d23bd 343 /* set to something which causes crashes */
269f7f44 344 memset(hdr, MEMORY_ALLOC_PATTERN, bytes + sizeof(memory_header_t));
5113680f
MW
345
346 hdr->magic = MEMORY_HEADER_MAGIC;
347 hdr->bytes = bytes;
348 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
269f7f44 349 install_hooks();
5113680f
MW
350
351 /* insert at the beginning of the list */
352 hdr->next = first_header.next;
353 if (hdr->next)
354 {
355 hdr->next->previous = hdr;
356 }
357 hdr->previous = &first_header;
358 first_header.next = hdr;
5113680f
MW
359 pthread_mutex_unlock(&mutex);
360 return hdr + 1;
361}
362
363/**
364 * Hook function for free()
365 */
6b3292da 366void free_hook(void *ptr, const void *caller)
5113680f
MW
367{
368 void *stack_frames[STACK_FRAMES_COUNT];
369 int stack_frame_count;
370 memory_header_t *hdr = ptr - sizeof(memory_header_t);
371
372 /* allow freeing of NULL */
373 if (ptr == NULL)
374 {
375 return;
376 }
377
378 pthread_mutex_lock(&mutex);
73760ca5 379 count_free++;
269f7f44 380 uninstall_hooks();
5113680f
MW
381 if (hdr->magic != MEMORY_HEADER_MAGIC)
382 {
552cc11b
MW
383 fprintf(stderr, "freeing of invalid memory (%p, MAGIC 0x%x != 0x%x):\n",
384 ptr, hdr->magic, MEMORY_HEADER_MAGIC);
5113680f
MW
385 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
386 log_stack_frames(stack_frames, stack_frame_count);
269f7f44
MW
387 install_hooks();
388 pthread_mutex_unlock(&mutex);
5113680f
MW
389 return;
390 }
5113680f
MW
391
392 /* remove item from list */
393 if (hdr->next)
394 {
395 hdr->next->previous = hdr->previous;
396 }
397 hdr->previous->next = hdr->next;
398
269f7f44
MW
399 /* clear MAGIC, set mem to something remarkable */
400 memset(hdr, MEMORY_FREE_PATTERN, hdr->bytes + sizeof(memory_header_t));
401
5113680f
MW
402 free(hdr);
403 install_hooks();
404 pthread_mutex_unlock(&mutex);
405}
406
407/**
408 * Hook function for realloc()
409 */
6b3292da 410void *realloc_hook(void *old, size_t bytes, const void *caller)
5113680f 411{
a401efd0 412 memory_header_t *hdr;
5113680f
MW
413 void *stack_frames[STACK_FRAMES_COUNT];
414 int stack_frame_count;
415
416 /* allow reallocation of NULL */
417 if (old == NULL)
418 {
419 return malloc_hook(bytes, caller);
420 }
a401efd0
MW
421
422 hdr = old - sizeof(memory_header_t);
269f7f44 423
a401efd0 424 pthread_mutex_lock(&mutex);
73760ca5 425 count_realloc++;
a401efd0 426 uninstall_hooks();
5113680f
MW
427 if (hdr->magic != MEMORY_HEADER_MAGIC)
428 {
552cc11b 429 fprintf(stderr, "reallocation of invalid memory (%p):\n", old);
5113680f
MW
430 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
431 log_stack_frames(stack_frames, stack_frame_count);
269f7f44
MW
432 install_hooks();
433 pthread_mutex_unlock(&mutex);
986d23bd 434 raise(SIGKILL);
5113680f
MW
435 return NULL;
436 }
437
a401efd0
MW
438 hdr = realloc(hdr, bytes + sizeof(memory_header_t));
439
440 /* update statistics */
441 hdr->bytes = bytes;
442 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
5113680f 443
a401efd0
MW
444 /* update header of linked list neighbours */
445 if (hdr->next)
446 {
447 hdr->next->previous = hdr;
448 }
449 hdr->previous->next = hdr;
450 install_hooks();
451 pthread_mutex_unlock(&mutex);
452 return hdr + 1;
5113680f
MW
453}
454
455/**
552cc11b 456 * Implementation of leak_detective_t.destroy
5113680f 457 */
552cc11b 458static void destroy(private_leak_detective_t *this)
5113680f 459{
552cc11b 460 if (installed)
9cc7a297
MW
461 {
462 uninstall_hooks();
463 report_leaks();
464 }
552cc11b 465 free(this);
6b3292da
MW
466}
467
552cc11b
MW
468/*
469 * see header file
73760ca5 470 */
552cc11b 471leak_detective_t *leak_detective_create()
73760ca5 472{
552cc11b 473 private_leak_detective_t *this = malloc_thing(private_leak_detective_t);
73760ca5 474
552cc11b 475 this->public.destroy = (void(*)(leak_detective_t*))destroy;
9cc7a297 476
552cc11b 477 if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
73760ca5 478 {
552cc11b 479 install_hooks();
73760ca5 480 }
552cc11b 481 return &this->public;
73760ca5
MW
482}
483