OSDN Git Service

2009-09-01 Loren J. Rittle <ljrittle@acm.org>
[pf3gnuchains/gcc-fork.git] / libmudflap / mf-hooks1.c
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