0
|
1 /* Mudflap: narrow-pointer bounds-checking by tree rewriting.
|
|
2 Copyright (C) 2002, 2003, 2004, 2009 Free Software Foundation, Inc.
|
|
3 Contributed by Frank Ch. Eigler <fche@redhat.com>
|
|
4 and Graydon Hoare <graydon@redhat.com>
|
|
5
|
|
6 This file is part of GCC.
|
|
7
|
|
8 GCC is free software; you can redistribute it and/or modify it under
|
|
9 the terms of the GNU General Public License as published by the Free
|
|
10 Software Foundation; either version 3, or (at your option) any later
|
|
11 version.
|
|
12
|
|
13 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
16 for more details.
|
|
17
|
|
18 Under Section 7 of GPL version 3, you are granted additional
|
|
19 permissions described in the GCC Runtime Library Exception, version
|
|
20 3.1, as published by the Free Software Foundation.
|
|
21
|
|
22 You should have received a copy of the GNU General Public License and
|
|
23 a copy of the GCC Runtime Library Exception along with this program;
|
|
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
25 <http://www.gnu.org/licenses/>. */
|
|
26
|
|
27 #include "config.h"
|
|
28
|
|
29 #ifndef HAVE_SOCKLEN_T
|
|
30 #define socklen_t int
|
|
31 #endif
|
|
32
|
|
33
|
|
34 /* These attempt to coax various unix flavours to declare all our
|
|
35 needed tidbits in the system headers. */
|
|
36 #if !defined(__FreeBSD__) && !defined(__APPLE__)
|
|
37 #define _POSIX_SOURCE
|
|
38 #endif /* Some BSDs break <sys/socket.h> if this is defined. */
|
|
39 #define _GNU_SOURCE
|
|
40 #define _XOPEN_SOURCE
|
|
41 #define _BSD_TYPES
|
|
42 #define __EXTENSIONS__
|
|
43 #define _ALL_SOURCE
|
|
44 #define _LARGE_FILE_API
|
|
45 #define _XOPEN_SOURCE_EXTENDED 1
|
|
46
|
|
47 #include <string.h>
|
|
48 #include <stdio.h>
|
|
49 #include <stdlib.h>
|
|
50 #include <sys/time.h>
|
|
51 #include <sys/types.h>
|
|
52 #include <unistd.h>
|
|
53 #include <assert.h>
|
|
54 #include <errno.h>
|
|
55 #include <limits.h>
|
|
56 #include <time.h>
|
|
57
|
|
58 #include "mf-runtime.h"
|
|
59 #include "mf-impl.h"
|
|
60
|
|
61 #ifdef _MUDFLAP
|
|
62 #error "Do not compile this file with -fmudflap!"
|
|
63 #endif
|
|
64
|
|
65
|
|
66 /* Memory allocation related hook functions. Some of these are
|
|
67 intercepted via linker wrapping or symbol interposition. Others
|
|
68 use plain macros in mf-runtime.h. */
|
|
69
|
|
70
|
|
71 #if PIC
|
|
72
|
|
73 enum { BS = 4096, NB=10 };
|
|
74 static char __mf_0fn_bufs[NB][BS];
|
|
75 static unsigned __mf_0fn_bufs_used[NB];
|
|
76
|
|
77
|
|
78 /* A special bootstrap variant. */
|
|
79 void *
|
|
80 __mf_0fn_malloc (size_t c)
|
|
81 {
|
|
82 unsigned i;
|
|
83
|
|
84 for (i=0; i<NB; i++)
|
|
85 {
|
|
86 if (! __mf_0fn_bufs_used[i] && c < BS)
|
|
87 {
|
|
88 __mf_0fn_bufs_used[i] = 1;
|
|
89 return & __mf_0fn_bufs[i][0];
|
|
90 }
|
|
91 }
|
|
92 return NULL;
|
|
93 }
|
|
94 #endif
|
|
95
|
|
96
|
|
97 #undef malloc
|
|
98 WRAPPER(void *, malloc, size_t c)
|
|
99 {
|
|
100 size_t size_with_crumple_zones;
|
|
101 DECLARE(void *, malloc, size_t c);
|
|
102 void *result;
|
|
103 BEGIN_PROTECT (malloc, c);
|
|
104
|
|
105 size_with_crumple_zones =
|
|
106 CLAMPADD(c,CLAMPADD(__mf_opts.crumple_zone,
|
|
107 __mf_opts.crumple_zone));
|
|
108 BEGIN_MALLOC_PROTECT ();
|
|
109 result = (char *) CALL_REAL (malloc, size_with_crumple_zones);
|
|
110 END_MALLOC_PROTECT ();
|
|
111
|
|
112 if (LIKELY(result))
|
|
113 {
|
|
114 result += __mf_opts.crumple_zone;
|
|
115 __mf_register (result, c, __MF_TYPE_HEAP, "malloc region");
|
|
116 /* XXX: register __MF_TYPE_NOACCESS for crumple zones. */
|
|
117 }
|
|
118
|
|
119 return result;
|
|
120 }
|
|
121
|
|
122
|
|
123 #ifdef PIC
|
|
124 /* A special bootstrap variant. */
|
|
125 void *
|
|
126 __mf_0fn_calloc (size_t c, size_t n)
|
|
127 {
|
|
128 return __mf_0fn_malloc (c * n);
|
|
129 }
|
|
130 #endif
|
|
131
|
|
132
|
|
133 #undef calloc
|
|
134 WRAPPER(void *, calloc, size_t c, size_t n)
|
|
135 {
|
|
136 size_t size_with_crumple_zones;
|
|
137 DECLARE(void *, calloc, size_t, size_t);
|
|
138 DECLARE(void *, malloc, size_t);
|
|
139 DECLARE(void *, memset, void *, int, size_t);
|
|
140 char *result;
|
|
141 BEGIN_PROTECT (calloc, c, n);
|
|
142
|
|
143 size_with_crumple_zones =
|
|
144 CLAMPADD((c * n), /* XXX: CLAMPMUL */
|
|
145 CLAMPADD(__mf_opts.crumple_zone,
|
|
146 __mf_opts.crumple_zone));
|
|
147 BEGIN_MALLOC_PROTECT ();
|
|
148 result = (char *) CALL_REAL (malloc, size_with_crumple_zones);
|
|
149 END_MALLOC_PROTECT ();
|
|
150
|
|
151 if (LIKELY(result))
|
|
152 memset (result, 0, size_with_crumple_zones);
|
|
153
|
|
154 if (LIKELY(result))
|
|
155 {
|
|
156 result += __mf_opts.crumple_zone;
|
|
157 __mf_register (result, c*n /* XXX: clamp */, __MF_TYPE_HEAP_I, "calloc region");
|
|
158 /* XXX: register __MF_TYPE_NOACCESS for crumple zones. */
|
|
159 }
|
|
160
|
|
161 return result;
|
|
162 }
|
|
163
|
|
164
|
|
165 #if PIC
|
|
166 /* A special bootstrap variant. */
|
|
167 void *
|
|
168 __mf_0fn_realloc (void *buf, size_t c)
|
|
169 {
|
|
170 return NULL;
|
|
171 }
|
|
172 #endif
|
|
173
|
|
174
|
|
175 #undef realloc
|
|
176 WRAPPER(void *, realloc, void *buf, size_t c)
|
|
177 {
|
|
178 DECLARE(void * , realloc, void *, size_t);
|
|
179 size_t size_with_crumple_zones;
|
|
180 char *base = buf;
|
|
181 unsigned saved_wipe_heap;
|
|
182 char *result;
|
|
183 BEGIN_PROTECT (realloc, buf, c);
|
|
184
|
|
185 if (LIKELY(buf))
|
|
186 base -= __mf_opts.crumple_zone;
|
|
187
|
|
188 size_with_crumple_zones =
|
|
189 CLAMPADD(c, CLAMPADD(__mf_opts.crumple_zone,
|
|
190 __mf_opts.crumple_zone));
|
|
191 BEGIN_MALLOC_PROTECT ();
|
|
192 result = (char *) CALL_REAL (realloc, base, size_with_crumple_zones);
|
|
193 END_MALLOC_PROTECT ();
|
|
194
|
|
195 /* Ensure heap wiping doesn't occur during this peculiar
|
|
196 unregister/reregister pair. */
|
|
197 LOCKTH ();
|
|
198 __mf_set_state (reentrant);
|
|
199 saved_wipe_heap = __mf_opts.wipe_heap;
|
|
200 __mf_opts.wipe_heap = 0;
|
|
201
|
|
202 if (LIKELY(buf))
|
|
203 __mfu_unregister (buf, 0, __MF_TYPE_HEAP_I);
|
|
204 /* NB: underlying region may have been __MF_TYPE_HEAP. */
|
|
205
|
|
206 if (LIKELY(result))
|
|
207 {
|
|
208 result += __mf_opts.crumple_zone;
|
|
209 __mfu_register (result, c, __MF_TYPE_HEAP_I, "realloc region");
|
|
210 /* XXX: register __MF_TYPE_NOACCESS for crumple zones. */
|
|
211 }
|
|
212
|
|
213 /* Restore previous setting. */
|
|
214 __mf_opts.wipe_heap = saved_wipe_heap;
|
|
215
|
|
216 __mf_set_state (active);
|
|
217 UNLOCKTH ();
|
|
218
|
|
219 return result;
|
|
220 }
|
|
221
|
|
222
|
|
223 #if PIC
|
|
224 /* A special bootstrap variant. */
|
|
225 void
|
|
226 __mf_0fn_free (void *buf)
|
|
227 {
|
|
228 return;
|
|
229 }
|
|
230 #endif
|
|
231
|
|
232 #undef free
|
|
233 WRAPPER(void, free, void *buf)
|
|
234 {
|
|
235 /* Use a circular queue to delay some number (__mf_opts.free_queue_length) of free()s. */
|
|
236 static void *free_queue [__MF_FREEQ_MAX];
|
|
237 static unsigned free_ptr = 0;
|
|
238 static int freeq_initialized = 0;
|
|
239 DECLARE(void, free, void *);
|
|
240
|
|
241 BEGIN_PROTECT (free, buf);
|
|
242
|
|
243 if (UNLIKELY(buf == NULL))
|
|
244 return;
|
|
245
|
|
246 #if PIC
|
|
247 /* Check whether the given buffer might have come from a
|
|
248 __mf_0fn_malloc/calloc call that for whatever reason was not
|
|
249 redirected back to __mf_0fn_free. If so, we just ignore the
|
|
250 call. */
|
|
251 if (UNLIKELY((uintptr_t) buf >= (uintptr_t) __mf_0fn_bufs &&
|
|
252 (uintptr_t) buf < ((uintptr_t) __mf_0fn_bufs + sizeof(__mf_0fn_bufs))))
|
|
253 {
|
|
254 VERBOSE_TRACE ("skipping free of boot (0fn) alloc buffer %p\n", buf);
|
|
255 return;
|
|
256 }
|
|
257 #endif
|
|
258
|
|
259 LOCKTH ();
|
|
260 if (UNLIKELY(!freeq_initialized))
|
|
261 {
|
|
262 memset (free_queue, 0,
|
|
263 __MF_FREEQ_MAX * sizeof (void *));
|
|
264 freeq_initialized = 1;
|
|
265 }
|
|
266 UNLOCKTH ();
|
|
267
|
|
268 __mf_unregister (buf, 0, __MF_TYPE_HEAP_I);
|
|
269 /* NB: underlying region may have been __MF_TYPE_HEAP. */
|
|
270
|
|
271 if (UNLIKELY(__mf_opts.free_queue_length > 0))
|
|
272 {
|
|
273 char *freeme = NULL;
|
|
274 LOCKTH ();
|
|
275 if (free_queue [free_ptr] != NULL)
|
|
276 {
|
|
277 freeme = free_queue [free_ptr];
|
|
278 freeme -= __mf_opts.crumple_zone;
|
|
279 }
|
|
280 free_queue [free_ptr] = buf;
|
|
281 free_ptr = (free_ptr == (__mf_opts.free_queue_length-1) ? 0 : free_ptr + 1);
|
|
282 UNLOCKTH ();
|
|
283 if (freeme)
|
|
284 {
|
|
285 if (__mf_opts.trace_mf_calls)
|
|
286 {
|
|
287 VERBOSE_TRACE ("freeing deferred pointer %p (crumple %u)\n",
|
|
288 (void *) freeme,
|
|
289 __mf_opts.crumple_zone);
|
|
290 }
|
|
291 BEGIN_MALLOC_PROTECT ();
|
|
292 CALL_REAL (free, freeme);
|
|
293 END_MALLOC_PROTECT ();
|
|
294 }
|
|
295 }
|
|
296 else
|
|
297 {
|
|
298 /* back pointer up a bit to the beginning of crumple zone */
|
|
299 char *base = (char *)buf;
|
|
300 base -= __mf_opts.crumple_zone;
|
|
301 if (__mf_opts.trace_mf_calls)
|
|
302 {
|
|
303 VERBOSE_TRACE ("freeing pointer %p = %p - %u\n",
|
|
304 (void *) base,
|
|
305 (void *) buf,
|
|
306 __mf_opts.crumple_zone);
|
|
307 }
|
|
308 BEGIN_MALLOC_PROTECT ();
|
|
309 CALL_REAL (free, base);
|
|
310 END_MALLOC_PROTECT ();
|
|
311 }
|
|
312 }
|
|
313
|
|
314
|
|
315 /* We can only wrap mmap if the target supports it. Likewise for munmap.
|
|
316 We assume we have both if we have mmap. */
|
|
317 #ifdef HAVE_MMAP
|
|
318
|
|
319 #if PIC
|
|
320 /* A special bootstrap variant. */
|
|
321 void *
|
|
322 __mf_0fn_mmap (void *start, size_t l, int prot, int f, int fd, off_t off)
|
|
323 {
|
|
324 return (void *) -1;
|
|
325 }
|
|
326 #endif
|
|
327
|
|
328
|
|
329 #undef mmap
|
|
330 WRAPPER(void *, mmap,
|
|
331 void *start, size_t length, int prot,
|
|
332 int flags, int fd, off_t offset)
|
|
333 {
|
|
334 DECLARE(void *, mmap, void *, size_t, int,
|
|
335 int, int, off_t);
|
|
336 void *result;
|
|
337 BEGIN_PROTECT (mmap, start, length, prot, flags, fd, offset);
|
|
338
|
|
339 result = CALL_REAL (mmap, start, length, prot,
|
|
340 flags, fd, offset);
|
|
341
|
|
342 /*
|
|
343 VERBOSE_TRACE ("mmap (%08lx, %08lx, ...) => %08lx\n",
|
|
344 (uintptr_t) start, (uintptr_t) length,
|
|
345 (uintptr_t) result);
|
|
346 */
|
|
347
|
|
348 if (result != (void *)-1)
|
|
349 {
|
|
350 /* Register each page as a heap object. Why not register it all
|
|
351 as a single segment? That's so that a later munmap() call
|
|
352 can unmap individual pages. XXX: would __MF_TYPE_GUESS make
|
|
353 this more automatic? */
|
|
354 size_t ps = getpagesize ();
|
|
355 uintptr_t base = (uintptr_t) result;
|
|
356 uintptr_t offset;
|
|
357
|
|
358 for (offset=0; offset<length; offset+=ps)
|
|
359 {
|
|
360 /* XXX: We could map PROT_NONE to __MF_TYPE_NOACCESS. */
|
|
361 /* XXX: Unaccessed HEAP pages are reported as leaks. Is this
|
|
362 appropriate for unaccessed mmap pages? */
|
|
363 __mf_register ((void *) CLAMPADD (base, offset), ps,
|
|
364 __MF_TYPE_HEAP_I, "mmap page");
|
|
365 }
|
|
366 }
|
|
367
|
|
368 return result;
|
|
369 }
|
|
370
|
|
371
|
|
372 #if PIC
|
|
373 /* A special bootstrap variant. */
|
|
374 int
|
|
375 __mf_0fn_munmap (void *start, size_t length)
|
|
376 {
|
|
377 return -1;
|
|
378 }
|
|
379 #endif
|
|
380
|
|
381
|
|
382 #undef munmap
|
|
383 WRAPPER(int , munmap, void *start, size_t length)
|
|
384 {
|
|
385 DECLARE(int, munmap, void *, size_t);
|
|
386 int result;
|
|
387 BEGIN_PROTECT (munmap, start, length);
|
|
388
|
|
389 result = CALL_REAL (munmap, start, length);
|
|
390
|
|
391 /*
|
|
392 VERBOSE_TRACE ("munmap (%08lx, %08lx, ...) => %08lx\n",
|
|
393 (uintptr_t) start, (uintptr_t) length,
|
|
394 (uintptr_t) result);
|
|
395 */
|
|
396
|
|
397 if (result == 0)
|
|
398 {
|
|
399 /* Unregister each page as a heap object. */
|
|
400 size_t ps = getpagesize ();
|
|
401 uintptr_t base = (uintptr_t) start & (~ (ps - 1)); /* page align */
|
|
402 uintptr_t offset;
|
|
403
|
|
404 for (offset=0; offset<length; offset+=ps)
|
|
405 __mf_unregister ((void *) CLAMPADD (base, offset), ps, __MF_TYPE_HEAP_I);
|
|
406 }
|
|
407 return result;
|
|
408 }
|
|
409 #endif /* HAVE_MMAP */
|
|
410
|
|
411
|
|
412 /* This wrapper is a little different, as it's called indirectly from
|
|
413 __mf_fini also to clean up pending allocations. */
|
|
414 void *
|
|
415 __mf_wrap_alloca_indirect (size_t c)
|
|
416 {
|
|
417 DECLARE (void *, malloc, size_t);
|
|
418 DECLARE (void, free, void *);
|
|
419
|
|
420 /* This struct, a linked list, tracks alloca'd objects. The newest
|
|
421 object is at the head of the list. If we detect that we've
|
|
422 popped a few levels of stack, then the listed objects are freed
|
|
423 as needed. NB: The tracking struct is allocated with
|
|
424 real_malloc; the user data with wrap_malloc.
|
|
425 */
|
|
426 struct alloca_tracking { void *ptr; void *stack; struct alloca_tracking* next; };
|
|
427 static struct alloca_tracking *alloca_history = NULL;
|
|
428
|
|
429 void *stack = __builtin_frame_address (0);
|
|
430 void *result;
|
|
431 struct alloca_tracking *track;
|
|
432
|
|
433 TRACE ("%s\n", __PRETTY_FUNCTION__);
|
|
434 VERBOSE_TRACE ("alloca stack level %p\n", (void *) stack);
|
|
435
|
|
436 /* XXX: thread locking! */
|
|
437
|
|
438 /* Free any previously alloca'd blocks that belong to deeper-nested functions,
|
|
439 which must therefore have exited by now. */
|
|
440
|
|
441 #define DEEPER_THAN < /* XXX: for x86; steal find_stack_direction() from libiberty/alloca.c */
|
|
442
|
|
443 while (alloca_history &&
|
|
444 ((uintptr_t) alloca_history->stack DEEPER_THAN (uintptr_t) stack))
|
|
445 {
|
|
446 struct alloca_tracking *next = alloca_history->next;
|
|
447 __mf_unregister (alloca_history->ptr, 0, __MF_TYPE_HEAP);
|
|
448 BEGIN_MALLOC_PROTECT ();
|
|
449 CALL_REAL (free, alloca_history->ptr);
|
|
450 CALL_REAL (free, alloca_history);
|
|
451 END_MALLOC_PROTECT ();
|
|
452 alloca_history = next;
|
|
453 }
|
|
454
|
|
455 /* Allocate new block. */
|
|
456 result = NULL;
|
|
457 if (LIKELY (c > 0)) /* alloca(0) causes no allocation. */
|
|
458 {
|
|
459 BEGIN_MALLOC_PROTECT ();
|
|
460 track = (struct alloca_tracking *) CALL_REAL (malloc,
|
|
461 sizeof (struct alloca_tracking));
|
|
462 END_MALLOC_PROTECT ();
|
|
463 if (LIKELY (track != NULL))
|
|
464 {
|
|
465 BEGIN_MALLOC_PROTECT ();
|
|
466 result = CALL_REAL (malloc, c);
|
|
467 END_MALLOC_PROTECT ();
|
|
468 if (UNLIKELY (result == NULL))
|
|
469 {
|
|
470 BEGIN_MALLOC_PROTECT ();
|
|
471 CALL_REAL (free, track);
|
|
472 END_MALLOC_PROTECT ();
|
|
473 /* Too bad. XXX: What about errno? */
|
|
474 }
|
|
475 else
|
|
476 {
|
|
477 __mf_register (result, c, __MF_TYPE_HEAP, "alloca region");
|
|
478 track->ptr = result;
|
|
479 track->stack = stack;
|
|
480 track->next = alloca_history;
|
|
481 alloca_history = track;
|
|
482 }
|
|
483 }
|
|
484 }
|
|
485
|
|
486 return result;
|
|
487 }
|
|
488
|
|
489
|
|
490 #undef alloca
|
|
491 WRAPPER(void *, alloca, size_t c)
|
|
492 {
|
|
493 return __mf_wrap_alloca_indirect (c);
|
|
494 }
|
|
495
|