updated leak detective whitelist
[strongswan.git] / src / libstrongswan / utils / leak_detective.c
1 /**
2 * @file leak_detective.c
3 *
4 * @brief Allocation hooks to find memory leaks.
5 */
6
7 /*
8 * Copyright (C) 2006 Martin Willi
9 * Hochschule fuer Technik Rapperswil
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * for more details.
20 */
21
22 #include <stddef.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <malloc.h>
26 #include <execinfo.h>
27 #include <signal.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <dlfcn.h>
32 #include <unistd.h>
33 #include <syslog.h>
34 #include <pthread.h>
35
36 #include "leak_detective.h"
37
38 #include <types.h>
39 #include <utils/logger_manager.h>
40
41 #ifdef LEAK_DETECTIVE
42
43 /**
44 * Magic value which helps to detect memory corruption
45 */
46 #define MEMORY_HEADER_MAGIC 0xF1367ADF
47
48 static void install_hooks(void);
49 static void uninstall_hooks(void);
50 static void *malloc_hook(size_t, const void *);
51 static void *realloc_hook(void *, size_t, const void *);
52 static void free_hook(void*, const void *);
53 static void load_excluded_functions();
54
55 typedef struct memory_header_t memory_header_t;
56
57 /**
58 * Header which is prepended to each allocated memory block
59 */
60 struct memory_header_t {
61 /**
62 * Magci byte which must(!) hold MEMORY_HEADER_MAGIC
63 */
64 u_int32_t magic;
65
66 /**
67 * Number of bytes following after the header
68 */
69 size_t bytes;
70
71 /**
72 * Stack frames at the time of allocation
73 */
74 void *stack_frames[STACK_FRAMES_COUNT];
75
76 /**
77 * Number of stacks frames obtained in stack_frames
78 */
79 int stack_frame_count;
80
81 /**
82 * Pointer to previous entry in linked list
83 */
84 memory_header_t *previous;
85
86 /**
87 * Pointer to next entry in linked list
88 */
89 memory_header_t *next;
90 };
91
92 /**
93 * first mem header is just a dummy to chain
94 * the others on it...
95 */
96 static memory_header_t first_header = {
97 magic: MEMORY_HEADER_MAGIC,
98 bytes: 0,
99 stack_frame_count: 0,
100 previous: NULL,
101 next: NULL
102 };
103
104 /**
105 * logger for the leak detective
106 */
107 static logger_t *logger;
108
109 /**
110 * standard hooks, used to temparily remove hooking
111 */
112 static void *old_malloc_hook, *old_realloc_hook, *old_free_hook;
113
114 /**
115 * are the hooks currently installed?
116 */
117 static bool installed = FALSE;
118
119 /**
120 * Mutex to exclusivly uninstall hooks, access heap list
121 */
122 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
123
124
125 /**
126 * log stack frames queried by backtrace()
127 * TODO: Dump symbols of static functions!!!
128 */
129 static void log_stack_frames(void **stack_frames, int stack_frame_count)
130 {
131 char **strings;
132 size_t i;
133
134 strings = backtrace_symbols (stack_frames, stack_frame_count);
135
136 logger->log(logger, ERROR, " dumping %d stack frame addresses", stack_frame_count);
137
138 for (i = 0; i < stack_frame_count; i++)
139 {
140 logger->log(logger, ERROR, " %s", strings[i]);
141 }
142 free (strings);
143 }
144
145 /**
146 * Whitelist, which contains address ranges in stack frames ignored when leaking.
147 *
148 * This is necessary, as some function use allocation hacks (static buffers)
149 * and so on, which we want to suppress on leak reports.
150 */
151 typedef struct whitelist_t whitelist_t;
152
153 struct whitelist_t {
154 void* range_start;
155 size_t range_size;
156 };
157
158 whitelist_t whitelist[] = {
159 {pthread_create, 0x500},
160 {pthread_setspecific, 0xFF},
161 {mktime, 0xFF},
162 {inet_ntoa, 0xFF},
163 {strerror, 0xFF},
164 };
165
166 /**
167 * Check if this stack frame is whitelisted.
168 */
169 static bool is_whitelisted(void **stack_frames, int stack_frame_count)
170 {
171 int i, j;
172
173 for (i=0; i< stack_frame_count; i++)
174 {
175 for (j=0; j<sizeof(whitelist)/sizeof(whitelist_t); j++)
176 {
177 if (stack_frames[i] >= whitelist[j].range_start &&
178 stack_frames[i] <= (whitelist[j].range_start + whitelist[j].range_size))
179 {
180 return TRUE;
181 }
182 }
183 }
184 return FALSE;
185 }
186
187 /**
188 * Report leaks at library destruction
189 */
190 void report_leaks()
191 {
192 memory_header_t *hdr;
193 int leaks = 0;
194
195 /* reaquire a logger is necessary, this will force ((destructor))
196 * order to work correctly */
197 logger = logger_manager->get_logger(logger_manager, LEAK_DETECT);
198 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
199 {
200 if (!is_whitelisted(hdr->stack_frames, hdr->stack_frame_count))
201 {
202 logger->log(logger, ERROR, "Leak (%d bytes at %p):", hdr->bytes, hdr + 1);
203 log_stack_frames(hdr->stack_frames, hdr->stack_frame_count);
204 leaks++;
205 }
206 }
207
208 switch (leaks)
209 {
210 case 0:
211 logger->log(logger, CONTROL, "No leaks detected");
212 break;
213 case 1:
214 logger->log(logger, ERROR, "One leak detected");
215 break;
216 default:
217 logger->log(logger, ERROR, "%d leaks detected", leaks);
218 break;
219 }
220 }
221
222 /**
223 * Installs the malloc hooks, enables leak detection
224 */
225 static void install_hooks()
226 {
227 if (!installed)
228 {
229 old_malloc_hook = __malloc_hook;
230 old_realloc_hook = __realloc_hook;
231 old_free_hook = __free_hook;
232 __malloc_hook = malloc_hook;
233 __realloc_hook = realloc_hook;
234 __free_hook = free_hook;
235 installed = TRUE;
236 }
237 }
238
239 /**
240 * Uninstalls the malloc hooks, disables leak detection
241 */
242 static void uninstall_hooks()
243 {
244 if (installed)
245 {
246 __malloc_hook = old_malloc_hook;
247 __free_hook = old_free_hook;
248 __realloc_hook = old_realloc_hook;
249 installed = FALSE;
250 }
251 }
252
253 /**
254 * Hook function for malloc()
255 */
256 void *malloc_hook(size_t bytes, const void *caller)
257 {
258 memory_header_t *hdr;
259
260 pthread_mutex_lock(&mutex);
261 uninstall_hooks();
262 hdr = malloc(bytes + sizeof(memory_header_t));
263 /* set to something which causes crashes */
264 memset(hdr, 0xEE, bytes + sizeof(memory_header_t));
265
266 hdr->magic = MEMORY_HEADER_MAGIC;
267 hdr->bytes = bytes;
268 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
269
270 /* insert at the beginning of the list */
271 hdr->next = first_header.next;
272 if (hdr->next)
273 {
274 hdr->next->previous = hdr;
275 }
276 hdr->previous = &first_header;
277 first_header.next = hdr;
278 install_hooks();
279 pthread_mutex_unlock(&mutex);
280 return hdr + 1;
281 }
282
283 /**
284 * Hook function for free()
285 */
286 void free_hook(void *ptr, const void *caller)
287 {
288 void *stack_frames[STACK_FRAMES_COUNT];
289 int stack_frame_count;
290 memory_header_t *hdr = ptr - sizeof(memory_header_t);
291
292 /* allow freeing of NULL */
293 if (ptr == NULL)
294 {
295 return;
296 }
297
298 pthread_mutex_lock(&mutex);
299 if (hdr->magic != MEMORY_HEADER_MAGIC)
300 {
301 pthread_mutex_unlock(&mutex);
302 logger->log(logger, ERROR, "freeing of invalid memory (%p):", ptr);
303 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
304 log_stack_frames(stack_frames, stack_frame_count);
305 return;
306 }
307 /* remove magic from hdr */
308 hdr->magic = 0;
309
310 /* remove item from list */
311 if (hdr->next)
312 {
313 hdr->next->previous = hdr->previous;
314 }
315 hdr->previous->next = hdr->next;
316
317 uninstall_hooks();
318 free(hdr);
319 install_hooks();
320 pthread_mutex_unlock(&mutex);
321 }
322
323 /**
324 * Hook function for realloc()
325 */
326 void *realloc_hook(void *old, size_t bytes, const void *caller)
327 {
328 memory_header_t *hdr;
329 void *stack_frames[STACK_FRAMES_COUNT];
330 int stack_frame_count;
331
332 /* allow reallocation of NULL */
333 if (old == NULL)
334 {
335 return malloc_hook(bytes, caller);
336 }
337
338 hdr = old - sizeof(memory_header_t);
339 pthread_mutex_lock(&mutex);
340 uninstall_hooks();
341 if (hdr->magic != MEMORY_HEADER_MAGIC)
342 {
343 logger->log(logger, ERROR, "reallocation of invalid memory (%p):", old);
344 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
345 log_stack_frames(stack_frames, stack_frame_count);
346 raise(SIGKILL);
347 return NULL;
348 }
349
350 hdr = realloc(hdr, bytes + sizeof(memory_header_t));
351
352 /* update statistics */
353 hdr->bytes = bytes;
354 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
355
356 /* update header of linked list neighbours */
357 if (hdr->next)
358 {
359 hdr->next->previous = hdr;
360 }
361 hdr->previous->next = hdr;
362 install_hooks();
363 pthread_mutex_unlock(&mutex);
364 return hdr + 1;
365 }
366
367 /**
368 * Setup leak detective
369 */
370 void leak_detective_init()
371 {
372 logger = logger_manager->get_logger(logger_manager, LEAK_DETECT);
373 install_hooks();
374 }
375
376 /**
377 * Clean up leak detective
378 */
379 void leak_detective_cleanup()
380 {
381 uninstall_hooks();
382 report_leaks();
383 }
384
385 #endif /* LEAK_DETECTION */