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