Better handling of UCNs in strings
[tinycc.git] / lib / bcheck.c
blobb512ef62f085575be39959bba1527152abc527ca
1 /*
2 * Tiny C Memory and bounds checker
3 *
4 * Copyright (c) 2002 Fabrice Bellard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <setjmp.h>
26 #if !defined(__FreeBSD__) \
27 && !defined(__FreeBSD_kernel__) \
28 && !defined(__DragonFly__) \
29 && !defined(__OpenBSD__) \
30 && !defined(__APPLE__) \
31 && !defined(__NetBSD__)
32 #include <malloc.h>
33 #endif
35 #if !defined(_WIN32)
36 #include <unistd.h>
37 #include <sys/syscall.h>
38 #endif
40 #define BOUND_DEBUG (1)
41 #define BOUND_STATISTIC (1)
43 #if BOUND_DEBUG
44 #define dprintf(a...) if (print_calls) fprintf(a)
45 #else
46 #define dprintf(a...)
47 #endif
49 #ifdef __attribute__
50 /* an __attribute__ macro is defined in the system headers */
51 #undef __attribute__
52 #endif
53 #define FASTCALL __attribute__((regparm(3)))
55 #ifdef _WIN32
56 # define DLL_EXPORT __declspec(dllexport)
57 #else
58 # define DLL_EXPORT
59 #endif
61 #if defined(__FreeBSD__) \
62 || defined(__FreeBSD_kernel__) \
63 || defined(__DragonFly__) \
64 || defined(__OpenBSD__) \
65 || defined(__NetBSD__) \
66 || defined(__dietlibc__)
68 #include <sys/mman.h>
69 #define INIT_SEM()
70 #define EXIT_SEM()
71 #define WAIT_SEM()
72 #define POST_SEM()
73 #define TRY_SEM()
74 #define HAVE_MEMALIGN (0)
75 #define MALLOC_REDIR (0)
76 #define HAVE_PTHREAD_CREATE (0)
77 #define HAVE_CTYPE (0)
78 #define HAVE_ERRNO (0)
79 #define HAVE_SIGNAL (0)
80 #define HAVE_SIGACTION (0)
81 #define HAVE_FORK (0)
82 #define HAVE_TLS_FUNC (0)
83 #define HAVE_TLS_VAR (0)
85 #elif defined(_WIN32)
87 #include <windows.h>
88 #include <signal.h>
89 static CRITICAL_SECTION bounds_sem;
90 #define INIT_SEM() InitializeCriticalSection(&bounds_sem)
91 #define EXIT_SEM() DeleteCriticalSection(&bounds_sem)
92 #define WAIT_SEM() EnterCriticalSection(&bounds_sem)
93 #define POST_SEM() LeaveCriticalSection(&bounds_sem)
94 #define TRY_SEM() TryEnterCriticalSection(&bounds_sem)
95 #define HAVE_MEMALIGN (0)
96 #define MALLOC_REDIR (0)
97 #define HAVE_PTHREAD_CREATE (0)
98 #define HAVE_CTYPE (0)
99 #define HAVE_ERRNO (0)
100 #define HAVE_SIGNAL (1)
101 #define HAVE_SIGACTION (0)
102 #define HAVE_FORK (0)
103 #define HAVE_TLS_FUNC (1)
104 #define HAVE_TLS_VAR (0)
106 #else
108 #define __USE_GNU /* get RTLD_NEXT */
109 #include <sys/mman.h>
110 #include <ctype.h>
111 #include <pthread.h>
112 #include <dlfcn.h>
113 #include <errno.h>
114 #include <signal.h>
115 #ifdef __APPLE__
116 #include <dispatch/dispatch.h>
117 static dispatch_semaphore_t bounds_sem;
118 #define INIT_SEM() bounds_sem = dispatch_semaphore_create(1)
119 #define EXIT_SEM() dispatch_release(*(dispatch_object_t*)&bounds_sem)
120 #define WAIT_SEM() if (use_sem) dispatch_semaphore_wait(bounds_sem, DISPATCH_TIME_FOREVER)
121 #define POST_SEM() if (use_sem) dispatch_semaphore_signal(bounds_sem)
122 #define TRY_SEM() if (use_sem) dispatch_semaphore_wait(bounds_sem, DISPATCH_TIME_NOW)
123 #elif 0
124 #include <semaphore.h>
125 static sem_t bounds_sem;
126 #define INIT_SEM() sem_init (&bounds_sem, 0, 1)
127 #define EXIT_SEM() sem_destroy (&bounds_sem)
128 #define WAIT_SEM() if (use_sem) while (sem_wait (&bounds_sem) < 0 \
129 && errno == EINTR)
130 #define POST_SEM() if (use_sem) sem_post (&bounds_sem)
131 #define TRY_SEM() if (use_sem) while (sem_trywait (&bounds_sem) < 0 \
132 && errno == EINTR)
133 #elif 0
134 static pthread_mutex_t bounds_mtx;
135 #define INIT_SEM() pthread_mutex_init (&bounds_mtx, NULL)
136 #define EXIT_SEM() pthread_mutex_destroy (&bounds_mtx)
137 #define WAIT_SEM() if (use_sem) pthread_mutex_lock (&bounds_mtx)
138 #define POST_SEM() if (use_sem) pthread_mutex_unlock (&bounds_mtx)
139 #define TRY_SEM() if (use_sem) pthread_mutex_trylock (&bounds_mtx)
140 #else
141 static pthread_spinlock_t bounds_spin;
142 /* about 25% faster then semaphore. */
143 #define INIT_SEM() pthread_spin_init (&bounds_spin, 0)
144 #define EXIT_SEM() pthread_spin_destroy (&bounds_spin)
145 #define WAIT_SEM() if (use_sem) pthread_spin_lock (&bounds_spin)
146 #define POST_SEM() if (use_sem) pthread_spin_unlock (&bounds_spin)
147 #define TRY_SEM() if (use_sem) pthread_spin_trylock (&bounds_spin)
148 #endif
149 #define HAVE_MEMALIGN (1)
150 #define MALLOC_REDIR (1)
151 #define HAVE_PTHREAD_CREATE (1)
152 #define HAVE_CTYPE (1)
153 #define HAVE_ERRNO (1)
154 #define HAVE_SIGNAL (1)
155 #define HAVE_SIGACTION (1)
156 #define HAVE_FORK (1)
157 #if !defined(__APPLE__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
158 #define HAVE_TLS_FUNC (0)
159 #define HAVE_TLS_VAR (1)
160 #else
161 #define HAVE_TLS_FUNC (1)
162 #define HAVE_TLS_VAR (0)
163 #endif
164 #ifdef TCC_MUSL
165 # undef HAVE_CTYPE
166 #endif
167 #endif
169 #if MALLOC_REDIR
170 static void *(*malloc_redir) (size_t);
171 static void *(*calloc_redir) (size_t, size_t);
172 static void (*free_redir) (void *);
173 static void *(*realloc_redir) (void *, size_t);
174 static unsigned int pool_index;
175 static unsigned char __attribute__((aligned(16))) initial_pool[256];
176 #endif
177 #if HAVE_MEMALIGN
178 static void *(*memalign_redir) (size_t, size_t);
179 #endif
180 #if HAVE_PTHREAD_CREATE
181 static int (*pthread_create_redir) (pthread_t *thread,
182 const pthread_attr_t *attr,
183 void *(*start_routine)(void *), void *arg);
184 #endif
185 #if HAVE_SIGNAL
186 typedef void (*bound_sig)(int);
187 static bound_sig (*signal_redir) (int signum, bound_sig handler);
188 #endif
189 #if HAVE_SIGACTION
190 static int (*sigaction_redir) (int signum, const struct sigaction *act,
191 struct sigaction *oldact);
192 #endif
193 #if HAVE_FORK
194 static int (*fork_redir) (void);
195 #endif
197 #define TCC_TYPE_NONE (0)
198 #define TCC_TYPE_MALLOC (1)
199 #define TCC_TYPE_CALLOC (2)
200 #define TCC_TYPE_REALLOC (3)
201 #define TCC_TYPE_MEMALIGN (4)
202 #define TCC_TYPE_STRDUP (5)
204 /* this pointer is generated when bound check is incorrect */
205 #define INVALID_POINTER ((void *)(-2))
207 typedef struct tree_node Tree;
208 struct tree_node {
209 Tree * left, * right;
210 size_t start;
211 size_t size;
212 unsigned char type;
213 unsigned char is_invalid; /* true if pointers outside region are invalid */
216 typedef struct alloca_list_struct {
217 size_t fp;
218 void *p;
219 size_t size;
220 struct alloca_list_struct *next;
221 } alloca_list_type;
223 #if defined(_WIN32)
224 #define BOUND_TID_TYPE DWORD
225 #define BOUND_GET_TID GetCurrentThreadId()
226 #elif defined(__OpenBSD__)
227 #define BOUND_TID_TYPE pid_t
228 #define BOUND_GET_TID syscall (SYS_getthrid)
229 #elif defined(__FreeBSD__) || defined(__NetBSD__)
230 #define BOUND_TID_TYPE pid_t
231 #define BOUND_GET_TID 0
232 #elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || defined(__riscv)
233 #define BOUND_TID_TYPE pid_t
234 #define BOUND_GET_TID syscall (SYS_gettid)
235 #else
236 #define BOUND_TID_TYPE int
237 #define BOUND_GET_TID 0
238 #endif
240 typedef struct jmp_list_struct {
241 void *penv;
242 size_t fp;
243 size_t end_fp;
244 BOUND_TID_TYPE tid;
245 struct jmp_list_struct *next;
246 } jmp_list_type;
248 #define BOUND_STATISTIC_SPLAY (0)
249 static Tree * splay (size_t addr, Tree *t);
250 static Tree * splay_end (size_t addr, Tree *t);
251 static Tree * splay_insert(size_t addr, size_t size, Tree * t);
252 static Tree * splay_delete(size_t addr, Tree *t);
253 void splay_printtree(Tree * t, int d);
255 /* external interface */
256 void __bounds_checking (int no_check);
257 void __bound_never_fatal (int no_check);
258 DLL_EXPORT void * __bound_ptr_add(void *p, size_t offset);
259 DLL_EXPORT void * __bound_ptr_indir1(void *p, size_t offset);
260 DLL_EXPORT void * __bound_ptr_indir2(void *p, size_t offset);
261 DLL_EXPORT void * __bound_ptr_indir4(void *p, size_t offset);
262 DLL_EXPORT void * __bound_ptr_indir8(void *p, size_t offset);
263 DLL_EXPORT void * __bound_ptr_indir12(void *p, size_t offset);
264 DLL_EXPORT void * __bound_ptr_indir16(void *p, size_t offset);
265 DLL_EXPORT void FASTCALL __bound_local_new(void *p1);
266 DLL_EXPORT void FASTCALL __bound_local_delete(void *p1);
267 void __bound_init(size_t *, int);
268 void __bound_main_arg(int argc, char **argv, char **envp);
269 void __bound_exit(void);
270 #if !defined(_WIN32)
271 void *__bound_mmap (void *start, size_t size, int prot, int flags, int fd,
272 off_t offset);
273 int __bound_munmap (void *start, size_t size);
274 DLL_EXPORT void __bound_siglongjmp(jmp_buf env, int val);
275 #endif
276 DLL_EXPORT void __bound_new_region(void *p, size_t size);
277 DLL_EXPORT void __bound_setjmp(jmp_buf env);
278 DLL_EXPORT void __bound_longjmp(jmp_buf env, int val);
279 DLL_EXPORT void *__bound_memcpy(void *dst, const void *src, size_t size);
280 DLL_EXPORT int __bound_memcmp(const void *s1, const void *s2, size_t size);
281 DLL_EXPORT void *__bound_memmove(void *dst, const void *src, size_t size);
282 DLL_EXPORT void *__bound_memset(void *dst, int c, size_t size);
283 DLL_EXPORT int __bound_strlen(const char *s);
284 DLL_EXPORT char *__bound_strcpy(char *dst, const char *src);
285 DLL_EXPORT char *__bound_strncpy(char *dst, const char *src, size_t n);
286 DLL_EXPORT int __bound_strcmp(const char *s1, const char *s2);
287 DLL_EXPORT int __bound_strncmp(const char *s1, const char *s2, size_t n);
288 DLL_EXPORT char *__bound_strcat(char *dest, const char *src);
289 DLL_EXPORT char *__bound_strchr(const char *string, int ch);
290 DLL_EXPORT char *__bound_strdup(const char *s);
292 #if defined(__arm__) && defined(__ARM_EABI__)
293 DLL_EXPORT void *__bound___aeabi_memcpy(void *dst, const void *src, size_t size);
294 DLL_EXPORT void *__bound___aeabi_memmove(void *dst, const void *src, size_t size);
295 DLL_EXPORT void *__bound___aeabi_memmove4(void *dst, const void *src, size_t size);
296 DLL_EXPORT void *__bound___aeabi_memmove8(void *dst, const void *src, size_t size);
297 DLL_EXPORT void *__bound___aeabi_memset(void *dst, int c, size_t size);
298 DLL_EXPORT void *__aeabi_memcpy(void *dst, const void *src, size_t size);
299 DLL_EXPORT void *__aeabi_memmove(void *dst, const void *src, size_t size);
300 DLL_EXPORT void *__aeabi_memmove4(void *dst, const void *src, size_t size);
301 DLL_EXPORT void *__aeabi_memmove8(void *dst, const void *src, size_t size);
302 DLL_EXPORT void *__aeabi_memset(void *dst, int c, size_t size);
303 #endif
305 #if MALLOC_REDIR
306 #define BOUND_MALLOC(a) malloc_redir(a)
307 #define BOUND_MEMALIGN(a,b) memalign_redir(a,b)
308 #define BOUND_FREE(a) free_redir(a)
309 #define BOUND_REALLOC(a,b) realloc_redir(a,b)
310 #define BOUND_CALLOC(a,b) calloc_redir(a,b)
311 #else
312 #define BOUND_MALLOC(a) malloc(a)
313 #define BOUND_MEMALIGN(a,b) memalign(a,b)
314 #define BOUND_FREE(a) free(a)
315 #define BOUND_REALLOC(a,b) realloc(a,b)
316 #define BOUND_CALLOC(a,b) calloc(a,b)
317 DLL_EXPORT void *__bound_malloc(size_t size, const void *caller);
318 DLL_EXPORT void *__bound_memalign(size_t size, size_t align, const void *caller);
319 DLL_EXPORT void __bound_free(void *ptr, const void *caller);
320 DLL_EXPORT void *__bound_realloc(void *ptr, size_t size, const void *caller);
321 DLL_EXPORT void *__bound_calloc(size_t nmemb, size_t size);
322 #endif
324 #define FREE_REUSE_SIZE (100)
325 static unsigned int free_reuse_index;
326 static void *free_reuse_list[FREE_REUSE_SIZE];
328 static Tree *tree = NULL;
329 #define TREE_REUSE (1)
330 #if TREE_REUSE
331 static Tree *tree_free_list;
332 #endif
333 static alloca_list_type *alloca_list;
334 static jmp_list_type *jmp_list;
336 static unsigned char inited;
337 static unsigned char print_warn_ptr_add;
338 static unsigned char print_calls;
339 static unsigned char print_heap;
340 static unsigned char print_statistic;
341 static unsigned char no_strdup;
342 static unsigned char use_sem;
343 static int never_fatal;
344 #if HAVE_TLS_FUNC
345 #if defined(_WIN32)
346 static int no_checking = 0;
347 static DWORD no_checking_key;
348 #define NO_CHECKING_CHECK() if (!p) { \
349 p = (int *) LocalAlloc(LPTR, sizeof(int)); \
350 if (!p) bound_alloc_error("tls malloc"); \
351 *p = 0; \
352 TlsSetValue(no_checking_key, p); \
354 #define NO_CHECKING_GET() ({ int *p = TlsGetValue(no_checking_key); \
355 NO_CHECKING_CHECK(); \
356 *p; \
358 #define NO_CHECKING_SET(v) { int *p = TlsGetValue(no_checking_key); \
359 NO_CHECKING_CHECK(); \
360 *p = v; \
362 #else
363 static int no_checking = 0;
364 static pthread_key_t no_checking_key;
365 #define NO_CHECKING_CHECK() if (!p) { \
366 p = (int *) BOUND_MALLOC(sizeof(int)); \
367 if (!p) bound_alloc_error("tls malloc"); \
368 *p = 0; \
369 pthread_setspecific(no_checking_key, p); \
371 #define NO_CHECKING_GET() ({ int *p = pthread_getspecific(no_checking_key); \
372 NO_CHECKING_CHECK(); \
373 *p; \
375 #define NO_CHECKING_SET(v) { int *p = pthread_getspecific(no_checking_key); \
376 NO_CHECKING_CHECK(); \
377 *p = v; \
379 #endif
380 #elif HAVE_TLS_VAR
381 static __thread int no_checking = 0;
382 #define NO_CHECKING_GET() no_checking
383 #define NO_CHECKING_SET(v) no_checking = v
384 #else
385 static int no_checking = 0;
386 #define NO_CHECKING_GET() no_checking
387 #define NO_CHECKING_SET(v) no_checking = v
388 #endif
389 static char exec[100];
391 #if BOUND_STATISTIC
392 static unsigned long long bound_ptr_add_count;
393 static unsigned long long bound_ptr_indir1_count;
394 static unsigned long long bound_ptr_indir2_count;
395 static unsigned long long bound_ptr_indir4_count;
396 static unsigned long long bound_ptr_indir8_count;
397 static unsigned long long bound_ptr_indir12_count;
398 static unsigned long long bound_ptr_indir16_count;
399 static unsigned long long bound_local_new_count;
400 static unsigned long long bound_local_delete_count;
401 static unsigned long long bound_malloc_count;
402 static unsigned long long bound_calloc_count;
403 static unsigned long long bound_realloc_count;
404 static unsigned long long bound_free_count;
405 static unsigned long long bound_memalign_count;
406 static unsigned long long bound_mmap_count;
407 static unsigned long long bound_munmap_count;
408 static unsigned long long bound_alloca_count;
409 static unsigned long long bound_setjmp_count;
410 static unsigned long long bound_longjmp_count;
411 static unsigned long long bound_mempcy_count;
412 static unsigned long long bound_memcmp_count;
413 static unsigned long long bound_memmove_count;
414 static unsigned long long bound_memset_count;
415 static unsigned long long bound_strlen_count;
416 static unsigned long long bound_strcpy_count;
417 static unsigned long long bound_strncpy_count;
418 static unsigned long long bound_strcmp_count;
419 static unsigned long long bound_strncmp_count;
420 static unsigned long long bound_strcat_count;
421 static unsigned long long bound_strchr_count;
422 static unsigned long long bound_strdup_count;
423 static unsigned long long bound_not_found;
424 #define INCR_COUNT(x) ++x
425 #else
426 #define INCR_COUNT(x)
427 #endif
428 #if BOUND_STATISTIC_SPLAY
429 static unsigned long long bound_splay;
430 static unsigned long long bound_splay_end;
431 static unsigned long long bound_splay_insert;
432 static unsigned long long bound_splay_delete;
433 #define INCR_COUNT_SPLAY(x) ++x
434 #else
435 #define INCR_COUNT_SPLAY(x)
436 #endif
438 int tcc_backtrace(const char *fmt, ...);
440 /* print a bound error message */
441 #define bound_warning(...) \
442 tcc_backtrace("^bcheck.c^BCHECK: " __VA_ARGS__)
444 #define bound_error(...) \
445 do { \
446 bound_warning(__VA_ARGS__); \
447 if (never_fatal == 0) \
448 exit(255); \
449 } while (0)
451 static void bound_alloc_error(const char *s)
453 fprintf(stderr,"FATAL: %s\n",s);
454 exit (1);
457 static void bound_not_found_warning(const char *file, const char *function,
458 void *ptr)
460 dprintf(stderr, "%s%s, %s(): Not found %p\n", exec, file, function, ptr);
463 static void fetch_and_add(int* variable, int value)
465 #if defined __i386__ || defined __x86_64__
466 __asm__ volatile("lock; addl %0, %1"
467 : "+r" (value), "+m" (*variable) // input+output
468 : // No input-only
469 : "memory"
471 #elif defined __arm__
472 extern void fetch_and_add_arm(int* variable, int value);
473 fetch_and_add_arm(variable, value);
474 #elif defined __aarch64__
475 extern void fetch_and_add_arm64(int* variable, int value);
476 fetch_and_add_arm64(variable, value);
477 #elif defined __riscv
478 extern void fetch_and_add_riscv64(int* variable, int value);
479 fetch_and_add_riscv64(variable, value);
480 #else
481 *variable += value;
482 #endif
485 /* enable/disable checking. This can be used in signal handlers. */
486 void __bounds_checking (int no_check)
488 #if HAVE_TLS_FUNC || HAVE_TLS_VAR
489 NO_CHECKING_SET(NO_CHECKING_GET() + no_check);
490 #else
491 fetch_and_add (&no_checking, no_check);
492 #endif
495 /* enable/disable checking. This can be used in signal handlers. */
496 void __bound_never_fatal (int neverfatal)
498 fetch_and_add (&never_fatal, neverfatal);
501 /* return '(p + offset)' for pointer arithmetic (a pointer can reach
502 the end of a region in this case */
503 void * __bound_ptr_add(void *p, size_t offset)
505 size_t addr = (size_t)p;
507 if (NO_CHECKING_GET())
508 return p + offset;
510 dprintf(stderr, "%s, %s(): %p 0x%lx\n",
511 __FILE__, __FUNCTION__, p, (unsigned long)offset);
513 WAIT_SEM ();
514 INCR_COUNT(bound_ptr_add_count);
515 if (tree) {
516 addr -= tree->start;
517 if (addr >= tree->size) {
518 addr = (size_t)p;
519 tree = splay (addr, tree);
520 addr -= tree->start;
522 if (addr >= tree->size) {
523 addr = (size_t)p;
524 tree = splay_end (addr, tree);
525 addr -= tree->start;
527 if (addr <= tree->size) {
528 if (tree->is_invalid || addr + offset > tree->size) {
529 POST_SEM ();
530 if (print_warn_ptr_add)
531 bound_warning("%p is outside of the region", p + offset);
532 if (never_fatal <= 0)
533 return INVALID_POINTER; /* return an invalid pointer */
534 return p + offset;
537 else if (p) { /* Allow NULL + offset. offsetoff is using it. */
538 INCR_COUNT(bound_not_found);
539 POST_SEM ();
540 bound_not_found_warning (__FILE__, __FUNCTION__, p);
541 return p + offset;
544 POST_SEM ();
545 return p + offset;
548 /* return '(p + offset)' for pointer indirection (the resulting must
549 be strictly inside the region */
550 #define BOUND_PTR_INDIR(dsize) \
551 void * __bound_ptr_indir ## dsize (void *p, size_t offset) \
553 size_t addr = (size_t)p; \
555 if (NO_CHECKING_GET()) \
556 return p + offset; \
558 dprintf(stderr, "%s, %s(): %p 0x%lx\n", \
559 __FILE__, __FUNCTION__, p, (unsigned long)offset); \
560 WAIT_SEM (); \
561 INCR_COUNT(bound_ptr_indir ## dsize ## _count); \
562 if (tree) { \
563 addr -= tree->start; \
564 if (addr >= tree->size) { \
565 addr = (size_t)p; \
566 tree = splay (addr, tree); \
567 addr -= tree->start; \
569 if (addr >= tree->size) { \
570 addr = (size_t)p; \
571 tree = splay_end (addr, tree); \
572 addr -= tree->start; \
574 if (addr <= tree->size) { \
575 if (tree->is_invalid || addr + offset + dsize > tree->size) { \
576 POST_SEM (); \
577 bound_warning("%p is outside of the region", p + offset); \
578 if (never_fatal <= 0) \
579 return INVALID_POINTER; /* return an invalid pointer */ \
580 return p + offset; \
583 else { \
584 INCR_COUNT(bound_not_found); \
585 POST_SEM (); \
586 bound_not_found_warning (__FILE__, __FUNCTION__, p); \
587 return p + offset; \
590 POST_SEM (); \
591 return p + offset; \
594 BOUND_PTR_INDIR(1)
595 BOUND_PTR_INDIR(2)
596 BOUND_PTR_INDIR(4)
597 BOUND_PTR_INDIR(8)
598 BOUND_PTR_INDIR(12)
599 BOUND_PTR_INDIR(16)
601 #if defined(__GNUC__) && (__GNUC__ >= 6)
603 * At least gcc 6.2 complains when __builtin_frame_address is used with
604 * nonzero argument.
606 #pragma GCC diagnostic push
607 #pragma GCC diagnostic ignored "-Wframe-address"
608 #endif
610 /* return the frame pointer of the caller */
611 #define GET_CALLER_FP(fp)\
613 fp = (size_t)__builtin_frame_address(1);\
616 /* called when entering a function to add all the local regions */
617 void FASTCALL __bound_local_new(void *p1)
619 size_t addr, fp, *p = p1;
621 if (NO_CHECKING_GET())
622 return;
623 GET_CALLER_FP(fp);
624 dprintf(stderr, "%s, %s(): p1=%p fp=%p\n",
625 __FILE__, __FUNCTION__, p, (void *)fp);
626 WAIT_SEM ();
627 while ((addr = p[0])) {
628 INCR_COUNT(bound_local_new_count);
629 tree = splay_insert(addr + fp, p[1], tree);
630 p += 2;
632 POST_SEM ();
633 #if BOUND_DEBUG
634 if (print_calls) {
635 p = p1;
636 while ((addr = p[0])) {
637 dprintf(stderr, "%s, %s(): %p 0x%lx\n",
638 __FILE__, __FUNCTION__,
639 (void *) (addr + fp), (unsigned long) p[1]);
640 p += 2;
643 #endif
646 /* called when leaving a function to delete all the local regions */
647 void FASTCALL __bound_local_delete(void *p1)
649 size_t addr, fp, *p = p1;
651 if (NO_CHECKING_GET())
652 return;
653 GET_CALLER_FP(fp);
654 dprintf(stderr, "%s, %s(): p1=%p fp=%p\n",
655 __FILE__, __FUNCTION__, p, (void *)fp);
656 WAIT_SEM ();
657 while ((addr = p[0])) {
658 INCR_COUNT(bound_local_delete_count);
659 tree = splay_delete(addr + fp, tree);
660 p += 2;
662 if (alloca_list) {
663 alloca_list_type *last = NULL;
664 alloca_list_type *cur = alloca_list;
666 do {
667 if (cur->fp == fp) {
668 if (last)
669 last->next = cur->next;
670 else
671 alloca_list = cur->next;
672 tree = splay_delete ((size_t) cur->p, tree);
673 dprintf(stderr, "%s, %s(): remove alloca/vla %p\n",
674 __FILE__, __FUNCTION__, cur->p);
675 BOUND_FREE (cur);
676 cur = last ? last->next : alloca_list;
678 else {
679 last = cur;
680 cur = cur->next;
682 } while (cur);
684 if (jmp_list) {
685 jmp_list_type *last = NULL;
686 jmp_list_type *cur = jmp_list;
688 do {
689 if (cur->fp == fp) {
690 if (last)
691 last->next = cur->next;
692 else
693 jmp_list = cur->next;
694 dprintf(stderr, "%s, %s(): remove setjmp %p\n",
695 __FILE__, __FUNCTION__, cur->penv);
696 BOUND_FREE (cur);
697 cur = last ? last->next : jmp_list;
699 else {
700 last = cur;
701 cur = cur->next;
703 } while (cur);
706 POST_SEM ();
707 #if BOUND_DEBUG
708 if (print_calls) {
709 p = p1;
710 while ((addr = p[0])) {
711 if (addr != 1) {
712 dprintf(stderr, "%s, %s(): %p 0x%lx\n",
713 __FILE__, __FUNCTION__,
714 (void *) (addr + fp), (unsigned long) p[1]);
716 p+= 2;
719 #endif
722 /* used by alloca */
723 void __bound_new_region(void *p, size_t size)
725 size_t fp;
726 alloca_list_type *last;
727 alloca_list_type *cur;
728 alloca_list_type *new;
730 if (NO_CHECKING_GET())
731 return;
733 dprintf(stderr, "%s, %s(): %p, 0x%lx\n",
734 __FILE__, __FUNCTION__, p, (unsigned long)size);
735 GET_CALLER_FP (fp);
736 new = BOUND_MALLOC (sizeof (alloca_list_type));
737 WAIT_SEM ();
738 INCR_COUNT(bound_alloca_count);
739 last = NULL;
740 cur = alloca_list;
741 while (cur) {
742 #if defined(__i386__) || (defined(__arm__) && !defined(__ARM_EABI__))
743 int align = 4;
744 #elif defined(__arm__)
745 int align = 8;
746 #else
747 int align = 16;
748 #endif
749 void *cure = (void *)((char *)cur->p + ((cur->size + align) & -align));
750 void *pe = (void *)((char *)p + ((size + align) & -align));
751 if (cur->fp == fp && ((cur->p <= p && cure > p) ||
752 (p <= cur->p && pe > cur->p))) {
753 if (last)
754 last->next = cur->next;
755 else
756 alloca_list = cur->next;
757 tree = splay_delete((size_t)cur->p, tree);
758 break;
760 last = cur;
761 cur = cur->next;
763 tree = splay_insert((size_t)p, size, tree);
764 if (new) {
765 new->fp = fp;
766 new->p = p;
767 new->size = size;
768 new->next = alloca_list;
769 alloca_list = new;
771 POST_SEM ();
772 if (cur) {
773 dprintf(stderr, "%s, %s(): remove alloca/vla %p\n",
774 __FILE__, __FUNCTION__, cur->p);
775 BOUND_FREE (cur);
779 void __bound_setjmp(jmp_buf env)
781 jmp_list_type *jl;
782 void *e = (void *) env;
784 if (NO_CHECKING_GET() == 0) {
785 dprintf(stderr, "%s, %s(): %p\n", __FILE__, __FUNCTION__, e);
786 WAIT_SEM ();
787 INCR_COUNT(bound_setjmp_count);
788 jl = jmp_list;
789 while (jl) {
790 if (jl->penv == e)
791 break;
792 jl = jl->next;
794 if (jl == NULL) {
795 jl = BOUND_MALLOC (sizeof (jmp_list_type));
796 if (jl) {
797 jl->penv = e;
798 jl->next = jmp_list;
799 jmp_list = jl;
802 if (jl) {
803 size_t fp;
805 GET_CALLER_FP (fp);
806 jl->fp = fp;
807 jl->end_fp = (size_t)__builtin_frame_address(0);
808 jl->tid = BOUND_GET_TID;
810 POST_SEM ();
814 static void __bound_long_jump(jmp_buf env, int val, int sig, const char *func)
816 jmp_list_type *jl;
817 void *e;
818 BOUND_TID_TYPE tid;
820 if (NO_CHECKING_GET() == 0) {
821 e = (void *)env;
822 tid = BOUND_GET_TID;
823 dprintf(stderr, "%s, %s(): %p\n", __FILE__, func, e);
824 WAIT_SEM();
825 INCR_COUNT(bound_longjmp_count);
826 jl = jmp_list;
827 while (jl) {
828 if (jl->penv == e && jl->tid == tid) {
829 size_t start_fp = (size_t)__builtin_frame_address(0);
830 size_t end_fp = jl->end_fp;
831 jmp_list_type *cur = jmp_list;
832 jmp_list_type *last = NULL;
834 while (cur->penv != e || cur->tid != tid) {
835 if (cur->tid == tid) {
836 dprintf(stderr, "%s, %s(): remove setjmp %p\n",
837 __FILE__, func, cur->penv);
838 if (last)
839 last->next = cur->next;
840 else
841 jmp_list = cur->next;
842 BOUND_FREE (cur);
843 cur = last ? last->next : jmp_list;
845 else {
846 last = cur;
847 cur = cur->next;
850 for (;;) {
851 Tree *t = tree;
852 alloca_list_type *last;
853 alloca_list_type *cur;
855 while (t && (t->start < start_fp || t->start > end_fp))
856 if (t->start < start_fp)
857 t = t->right;
858 else
859 t = t->left;
860 if (t == NULL)
861 break;
862 last = NULL;
863 cur = alloca_list;
864 while (cur) {
865 if ((size_t) cur->p == t->start) {
866 dprintf(stderr, "%s, %s(): remove alloca/vla %p\n",
867 __FILE__, func, cur->p);
868 if (last)
869 last->next = cur->next;
870 else
871 alloca_list = cur->next;
872 BOUND_FREE (cur);
873 break;
875 last = cur;
876 cur = cur->next;
878 dprintf(stderr, "%s, %s(): delete %p\n",
879 __FILE__, func, (void *) t->start);
880 tree = splay_delete(t->start, tree);
882 break;
884 jl = jl->next;
886 POST_SEM();
888 #if !defined(_WIN32)
889 sig ? siglongjmp(env, val) :
890 #endif
891 longjmp (env, val);
894 void __bound_longjmp(jmp_buf env, int val)
896 __bound_long_jump(env,val, 0, __FUNCTION__);
899 #if !defined(_WIN32)
900 void __bound_siglongjmp(jmp_buf env, int val)
902 __bound_long_jump(env,val, 1, __FUNCTION__);
904 #endif
906 #if defined(__GNUC__) && (__GNUC__ >= 6)
907 #pragma GCC diagnostic pop
908 #endif
910 void __bound_init(size_t *p, int mode)
912 dprintf(stderr, "%s, %s(): start %s\n", __FILE__, __FUNCTION__,
913 mode < 0 ? "lazy" : mode == 0 ? "normal use" : "for -run");
915 if (inited) {
916 WAIT_SEM();
917 goto add_bounds;
919 inited = 1;
921 #if HAVE_TLS_FUNC
922 #if defined(_WIN32)
923 no_checking_key = TlsAlloc();
924 TlsSetValue(no_checking_key, &no_checking);
925 #else
926 pthread_key_create(&no_checking_key, NULL);
927 pthread_setspecific(no_checking_key, &no_checking);
928 #endif
929 #endif
930 NO_CHECKING_SET(1);
932 print_warn_ptr_add = getenv ("TCC_BOUNDS_WARN_POINTER_ADD") != NULL;
933 print_calls = getenv ("TCC_BOUNDS_PRINT_CALLS") != NULL;
934 print_heap = getenv ("TCC_BOUNDS_PRINT_HEAP") != NULL;
935 print_statistic = getenv ("TCC_BOUNDS_PRINT_STATISTIC") != NULL;
936 never_fatal = getenv ("TCC_BOUNDS_NEVER_FATAL") != NULL;
938 INIT_SEM ();
940 #if MALLOC_REDIR
942 void *addr = mode > 0 ? RTLD_DEFAULT : RTLD_NEXT;
944 /* tcc -run required RTLD_DEFAULT. Normal usage requires RTLD_NEXT,
945 but using RTLD_NEXT with -run segfaults on MacOS in dyld as the
946 generated code segment isn't registered with dyld and hence the
947 caller image of dlsym isn't known to it */
948 *(void **) (&malloc_redir) = dlsym (addr, "malloc");
949 if (malloc_redir == NULL) {
950 dprintf(stderr, "%s, %s(): use RTLD_DEFAULT\n",
951 __FILE__, __FUNCTION__);
952 addr = RTLD_DEFAULT;
953 *(void **) (&malloc_redir) = dlsym (addr, "malloc");
955 *(void **) (&calloc_redir) = dlsym (addr, "calloc");
956 *(void **) (&free_redir) = dlsym (addr, "free");
957 *(void **) (&realloc_redir) = dlsym (addr, "realloc");
958 *(void **) (&memalign_redir) = dlsym (addr, "memalign");
959 dprintf(stderr, "%s, %s(): malloc_redir %p\n",
960 __FILE__, __FUNCTION__, malloc_redir);
961 dprintf(stderr, "%s, %s(): free_redir %p\n",
962 __FILE__, __FUNCTION__, free_redir);
963 dprintf(stderr, "%s, %s(): realloc_redir %p\n",
964 __FILE__, __FUNCTION__, realloc_redir);
965 dprintf(stderr, "%s, %s(): memalign_redir %p\n",
966 __FILE__, __FUNCTION__, memalign_redir);
967 if (malloc_redir == NULL || free_redir == NULL)
968 bound_alloc_error ("Cannot redirect malloc/free");
969 #if HAVE_PTHREAD_CREATE
970 *(void **) (&pthread_create_redir) = dlsym (addr, "pthread_create");
971 dprintf(stderr, "%s, %s(): pthread_create_redir %p\n",
972 __FILE__, __FUNCTION__, pthread_create_redir);
973 if (pthread_create_redir == NULL)
974 bound_alloc_error ("Cannot redirect pthread_create");
975 #endif
976 #if HAVE_SIGNAL
977 *(void **) (&signal_redir) = dlsym (addr, "signal");
978 dprintf(stderr, "%s, %s(): signal_redir %p\n",
979 __FILE__, __FUNCTION__, signal_redir);
980 if (signal_redir == NULL)
981 bound_alloc_error ("Cannot redirect signal");
982 #endif
983 #if HAVE_SIGACTION
984 *(void **) (&sigaction_redir) = dlsym (addr, "sigaction");
985 dprintf(stderr, "%s, %s(): sigaction_redir %p\n",
986 __FILE__, __FUNCTION__, sigaction_redir);
987 if (sigaction_redir == NULL)
988 bound_alloc_error ("Cannot redirect sigaction");
989 #endif
990 #if HAVE_FORK
991 *(void **) (&fork_redir) = dlsym (addr, "fork");
992 dprintf(stderr, "%s, %s(): fork_redir %p\n",
993 __FILE__, __FUNCTION__, fork_redir);
994 if (fork_redir == NULL)
995 bound_alloc_error ("Cannot redirect fork");
996 #endif
998 #endif
1000 #ifdef __linux__
1002 FILE *fp;
1003 unsigned char found;
1004 unsigned long start;
1005 unsigned long end;
1006 unsigned long ad =
1007 (unsigned long) __builtin_return_address(0);
1008 char line[1000];
1010 /* Display exec name. Usefull when a lot of code is compiled with tcc */
1011 fp = fopen ("/proc/self/comm", "r");
1012 if (fp) {
1013 memset (exec, 0, sizeof(exec));
1014 fread (exec, 1, sizeof(exec) - 2, fp);
1015 if (strchr(exec,'\n'))
1016 *strchr(exec,'\n') = '\0';
1017 strcat (exec, ":");
1018 fclose (fp);
1020 /* check if dlopen is used (is threre a better way?) */
1021 found = 0;
1022 fp = fopen ("/proc/self/maps", "r");
1023 if (fp) {
1024 while (fgets (line, sizeof(line), fp)) {
1025 if (sscanf (line, "%lx-%lx", &start, &end) == 2 &&
1026 ad >= start && ad < end) {
1027 found = 1;
1028 break;
1030 if (strstr (line,"[heap]"))
1031 break;
1033 fclose (fp);
1035 if (found == 0) {
1036 use_sem = 1;
1037 no_strdup = 1;
1040 #endif
1042 WAIT_SEM ();
1044 #if HAVE_CTYPE
1045 #ifdef __APPLE__
1046 tree = splay_insert((size_t) &_DefaultRuneLocale,
1047 sizeof (_DefaultRuneLocale), tree);
1048 #else
1049 /* XXX: Does not work if locale is changed */
1050 tree = splay_insert((size_t) __ctype_b_loc(),
1051 sizeof (unsigned short *), tree);
1052 tree = splay_insert((size_t) (*__ctype_b_loc() - 128),
1053 384 * sizeof (unsigned short), tree);
1054 tree = splay_insert((size_t) __ctype_tolower_loc(),
1055 sizeof (__int32_t *), tree);
1056 tree = splay_insert((size_t) (*__ctype_tolower_loc() - 128),
1057 384 * sizeof (__int32_t), tree);
1058 tree = splay_insert((size_t) __ctype_toupper_loc(),
1059 sizeof (__int32_t *), tree);
1060 tree = splay_insert((size_t) (*__ctype_toupper_loc() - 128),
1061 384 * sizeof (__int32_t), tree);
1062 #endif
1063 #endif
1064 #if HAVE_ERRNO
1065 tree = splay_insert((size_t) (&errno), sizeof (int), tree);
1066 #endif
1068 add_bounds:
1069 if (!p)
1070 goto no_bounds;
1072 /* add all static bound check values */
1073 while (p[0] != 0) {
1074 tree = splay_insert(p[0], p[1], tree);
1075 #if BOUND_DEBUG
1076 if (print_calls) {
1077 dprintf(stderr, "%s, %s(): static var %p 0x%lx\n",
1078 __FILE__, __FUNCTION__,
1079 (void *) p[0], (unsigned long) p[1]);
1081 #endif
1082 p += 2;
1084 no_bounds:
1086 POST_SEM ();
1087 NO_CHECKING_SET(0);
1088 dprintf(stderr, "%s, %s(): end\n\n", __FILE__, __FUNCTION__);
1091 void
1092 #if (defined(__GLIBC__) && (__GLIBC_MINOR__ >= 4)) || defined(_WIN32)
1093 __attribute__((constructor))
1094 #endif
1095 __bound_main_arg(int argc, char **argv, char **envp)
1097 __bound_init (0, -1);
1098 if (argc && argv) {
1099 int i;
1101 WAIT_SEM ();
1102 for (i = 0; i < argc; i++)
1103 tree = splay_insert((size_t) argv[i], strlen (argv[i]) + 1, tree);
1104 tree = splay_insert((size_t) argv, (argc + 1) * sizeof(char *), tree);
1105 POST_SEM ();
1106 #if BOUND_DEBUG
1107 if (print_calls) {
1108 for (i = 0; i < argc; i++)
1109 dprintf(stderr, "%s, %s(): arg %p 0x%lx\n",
1110 __FILE__, __FUNCTION__,
1111 argv[i], (unsigned long)(strlen (argv[i]) + 1));
1112 dprintf(stderr, "%s, %s(): argv %p %d\n",
1113 __FILE__, __FUNCTION__, argv,
1114 (int)((argc + 1) * sizeof(char *)));
1116 #endif
1119 if (envp && *envp) {
1120 char **p = envp;
1122 WAIT_SEM ();
1123 while (*p) {
1124 tree = splay_insert((size_t) *p, strlen (*p) + 1, tree);
1125 ++p;
1127 tree = splay_insert((size_t) envp, (++p - envp) * sizeof(char *), tree);
1128 POST_SEM ();
1129 #if BOUND_DEBUG
1130 if (print_calls) {
1131 p = envp;
1132 while (*p) {
1133 dprintf(stderr, "%s, %s(): env %p 0x%lx\n",
1134 __FILE__, __FUNCTION__,
1135 *p, (unsigned long)(strlen (*p) + 1));
1136 ++p;
1138 dprintf(stderr, "%s, %s(): environ %p %d\n",
1139 __FILE__, __FUNCTION__, envp,
1140 (int)((++p - envp) * sizeof(char *)));
1142 #endif
1146 void __attribute__((destructor)) __bound_exit(void)
1148 int i;
1149 static const char * const alloc_type[] = {
1150 "", "malloc", "calloc", "realloc", "memalign", "strdup"
1153 dprintf(stderr, "%s, %s():\n", __FILE__, __FUNCTION__);
1155 if (inited) {
1156 #if !defined(_WIN32) && !defined(__APPLE__) && !defined TCC_MUSL && \
1157 !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
1158 if (print_heap) {
1159 extern void __libc_freeres (void);
1160 __libc_freeres ();
1162 #endif
1164 NO_CHECKING_SET(1);
1166 TRY_SEM ();
1167 while (alloca_list) {
1168 alloca_list_type *next = alloca_list->next;
1170 tree = splay_delete ((size_t) alloca_list->p, tree);
1171 BOUND_FREE (alloca_list);
1172 alloca_list = next;
1174 while (jmp_list) {
1175 jmp_list_type *next = jmp_list->next;
1177 BOUND_FREE (jmp_list);
1178 jmp_list = next;
1180 for (i = 0; i < FREE_REUSE_SIZE; i++) {
1181 if (free_reuse_list[i]) {
1182 tree = splay_delete ((size_t) free_reuse_list[i], tree);
1183 BOUND_FREE (free_reuse_list[i]);
1186 while (tree) {
1187 if (print_heap && tree->type != 0)
1188 fprintf (stderr, "%s, %s(): %s found size %lu\n",
1189 __FILE__, __FUNCTION__, alloc_type[tree->type],
1190 (unsigned long) tree->size);
1191 tree = splay_delete (tree->start, tree);
1193 #if TREE_REUSE
1194 while (tree_free_list) {
1195 Tree *next = tree_free_list->left;
1196 BOUND_FREE (tree_free_list);
1197 tree_free_list = next;
1199 #endif
1200 POST_SEM ();
1201 EXIT_SEM ();
1202 #if HAVE_TLS_FUNC
1203 #if defined(_WIN32)
1204 TlsFree(no_checking_key);
1205 #else
1206 pthread_key_delete(no_checking_key);
1207 #endif
1208 #endif
1209 inited = 0;
1210 if (print_statistic) {
1211 #if BOUND_STATISTIC
1212 fprintf (stderr, "bound_ptr_add_count %llu\n", bound_ptr_add_count);
1213 fprintf (stderr, "bound_ptr_indir1_count %llu\n", bound_ptr_indir1_count);
1214 fprintf (stderr, "bound_ptr_indir2_count %llu\n", bound_ptr_indir2_count);
1215 fprintf (stderr, "bound_ptr_indir4_count %llu\n", bound_ptr_indir4_count);
1216 fprintf (stderr, "bound_ptr_indir8_count %llu\n", bound_ptr_indir8_count);
1217 fprintf (stderr, "bound_ptr_indir12_count %llu\n", bound_ptr_indir12_count);
1218 fprintf (stderr, "bound_ptr_indir16_count %llu\n", bound_ptr_indir16_count);
1219 fprintf (stderr, "bound_local_new_count %llu\n", bound_local_new_count);
1220 fprintf (stderr, "bound_local_delete_count %llu\n", bound_local_delete_count);
1221 fprintf (stderr, "bound_malloc_count %llu\n", bound_malloc_count);
1222 fprintf (stderr, "bound_calloc_count %llu\n", bound_calloc_count);
1223 fprintf (stderr, "bound_realloc_count %llu\n", bound_realloc_count);
1224 fprintf (stderr, "bound_free_count %llu\n", bound_free_count);
1225 fprintf (stderr, "bound_memalign_count %llu\n", bound_memalign_count);
1226 fprintf (stderr, "bound_mmap_count %llu\n", bound_mmap_count);
1227 fprintf (stderr, "bound_munmap_count %llu\n", bound_munmap_count);
1228 fprintf (stderr, "bound_alloca_count %llu\n", bound_alloca_count);
1229 fprintf (stderr, "bound_setjmp_count %llu\n", bound_setjmp_count);
1230 fprintf (stderr, "bound_longjmp_count %llu\n", bound_longjmp_count);
1231 fprintf (stderr, "bound_mempcy_count %llu\n", bound_mempcy_count);
1232 fprintf (stderr, "bound_memcmp_count %llu\n", bound_memcmp_count);
1233 fprintf (stderr, "bound_memmove_count %llu\n", bound_memmove_count);
1234 fprintf (stderr, "bound_memset_count %llu\n", bound_memset_count);
1235 fprintf (stderr, "bound_strlen_count %llu\n", bound_strlen_count);
1236 fprintf (stderr, "bound_strcpy_count %llu\n", bound_strcpy_count);
1237 fprintf (stderr, "bound_strncpy_count %llu\n", bound_strncpy_count);
1238 fprintf (stderr, "bound_strcmp_count %llu\n", bound_strcmp_count);
1239 fprintf (stderr, "bound_strncmp_count %llu\n", bound_strncmp_count);
1240 fprintf (stderr, "bound_strcat_count %llu\n", bound_strcat_count);
1241 fprintf (stderr, "bound_strchr_count %llu\n", bound_strchr_count);
1242 fprintf (stderr, "bound_strdup_count %llu\n", bound_strdup_count);
1243 fprintf (stderr, "bound_not_found %llu\n", bound_not_found);
1244 #endif
1245 #if BOUND_STATISTIC_SPLAY
1246 fprintf (stderr, "bound_splay %llu\n", bound_splay);
1247 fprintf (stderr, "bound_splay_end %llu\n", bound_splay_end);
1248 fprintf (stderr, "bound_splay_insert %llu\n", bound_splay_insert);
1249 fprintf (stderr, "bound_splay_delete %llu\n", bound_splay_delete);
1250 #endif
1255 #if HAVE_PTHREAD_CREATE
1256 typedef struct {
1257 void *(*start_routine) (void *);
1258 void *arg;
1259 sigset_t old_mask;
1260 } bound_thread_create_type;
1262 static void *bound_thread_create(void *bdata)
1264 bound_thread_create_type *data = (bound_thread_create_type *) bdata;
1265 void *retval;
1266 #if HAVE_TLS_FUNC
1267 int *p = (int *) BOUND_MALLOC(sizeof(int));
1269 if (!p) bound_alloc_error("bound_thread_create malloc");
1270 *p = 0;
1271 pthread_setspecific(no_checking_key, p);
1272 #endif
1273 pthread_sigmask(SIG_SETMASK, &data->old_mask, NULL);
1274 retval = data->start_routine(data->arg);
1275 #if HAVE_TLS_FUNC
1276 pthread_setspecific(no_checking_key, NULL);
1277 BOUND_FREE (p);
1278 #endif
1279 BOUND_FREE (data);
1280 return retval;
1283 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
1284 void *(*start_routine) (void *), void *arg)
1286 int retval;
1287 bound_thread_create_type *data;
1288 sigset_t mask;
1289 sigset_t old_mask;
1291 use_sem = 1;
1292 dprintf (stderr, "%s, %s()\n", __FILE__, __FUNCTION__);
1293 sigfillset(&mask);
1294 pthread_sigmask(SIG_SETMASK, &mask, &old_mask);
1295 data = (bound_thread_create_type *) BOUND_MALLOC(sizeof(bound_thread_create_type));
1296 if (!data) bound_alloc_error("bound_thread_create malloc");
1297 data->start_routine = start_routine;
1298 data->arg = arg;
1299 data->old_mask = old_mask;
1300 retval = pthread_create_redir(thread, attr, bound_thread_create, data);
1301 pthread_sigmask(SIG_SETMASK, &old_mask, NULL);
1302 return retval;
1304 #endif
1306 #if HAVE_SIGNAL || HAVE_SIGACTION
1307 typedef union {
1308 #if HAVE_SIGNAL
1309 bound_sig signal_handler;
1310 #endif
1311 #if HAVE_SIGACTION
1312 void (*sig_handler)(int);
1313 void (*sig_sigaction)(int, siginfo_t *, void *);
1314 #endif
1315 } bound_sig_type;
1317 static unsigned char bound_sig_used[NSIG];
1318 static bound_sig_type bound_sig_data[NSIG];
1319 #endif
1321 #if HAVE_SIGNAL
1322 static void signal_handler(int sig)
1324 __bounds_checking(1);
1325 bound_sig_data[sig].signal_handler(sig);
1326 __bounds_checking(-1);
1329 bound_sig signal(int signum, bound_sig handler)
1331 bound_sig retval;
1333 dprintf (stderr, "%s, %s() %d %p\n", __FILE__, __FUNCTION__,
1334 signum, handler);
1335 retval = signal_redir(signum, handler ? signal_handler : handler);
1336 if (retval != SIG_ERR) {
1337 if (bound_sig_used[signum])
1338 retval = bound_sig_data[signum].signal_handler;
1339 if (handler) {
1340 bound_sig_used[signum] = 1;
1341 bound_sig_data[signum].signal_handler = handler;
1344 return retval;
1346 #endif
1348 #if HAVE_SIGACTION
1349 static void sig_handler(int sig)
1351 __bounds_checking(1);
1352 bound_sig_data[sig].sig_handler(sig);
1353 __bounds_checking(-1);
1356 static void sig_sigaction(int sig, siginfo_t *info, void *ucontext)
1358 __bounds_checking(1);
1359 bound_sig_data[sig].sig_sigaction(sig, info, ucontext);
1360 __bounds_checking(-1);
1363 int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
1365 int retval;
1366 struct sigaction nact, oact;
1368 dprintf (stderr, "%s, %s() %d %p %p\n", __FILE__, __FUNCTION__,
1369 signum, act, oldact);
1370 if (act) {
1371 nact = *act;
1372 if (nact.sa_flags & SA_SIGINFO)
1373 nact.sa_sigaction = sig_sigaction;
1374 else
1375 nact.sa_handler = sig_handler;
1376 retval = sigaction_redir(signum, &nact, &oact);
1378 else
1379 retval = sigaction_redir(signum, act, &oact);
1380 if (retval >= 0) {
1381 if (bound_sig_used[signum]) {
1382 if (oact.sa_flags & SA_SIGINFO)
1383 oact.sa_sigaction = bound_sig_data[signum].sig_sigaction;
1384 else
1385 oact.sa_handler = bound_sig_data[signum].sig_handler;
1387 if (oldact) {
1388 *oldact = oact;
1390 if (act) {
1391 bound_sig_used[signum] = 1;
1392 if (act->sa_flags & SA_SIGINFO)
1393 bound_sig_data[signum].sig_sigaction = act->sa_sigaction;
1394 else
1395 bound_sig_data[signum].sig_handler = act->sa_handler;
1398 return retval;
1400 #endif
1402 #if HAVE_FORK
1403 pid_t fork(void)
1405 pid_t retval;
1407 WAIT_SEM();
1408 retval = (*fork_redir)();
1409 if (retval == 0)
1410 INIT_SEM();
1411 else
1412 POST_SEM();
1413 return retval;
1415 #endif
1417 #if MALLOC_REDIR
1418 void *malloc(size_t size)
1419 #else
1420 void *__bound_malloc(size_t size, const void *caller)
1421 #endif
1423 void *ptr;
1425 #if MALLOC_REDIR
1426 /* This will catch the first dlsym call from __bound_init */
1427 if (malloc_redir == NULL) {
1428 __bound_init (0, -1);
1429 if (malloc_redir == NULL) {
1430 ptr = &initial_pool[pool_index];
1431 pool_index = (pool_index + size + 15) & ~15;
1432 if (pool_index >= sizeof (initial_pool))
1433 bound_alloc_error ("initial memory pool too small");
1434 dprintf (stderr, "%s, %s(): initial %p, 0x%lx\n",
1435 __FILE__, __FUNCTION__, ptr, (unsigned long)size);
1436 return ptr;
1439 #endif
1440 /* we allocate one more byte to ensure the regions will be
1441 separated by at least one byte. With the glibc malloc, it may
1442 be in fact not necessary */
1443 ptr = BOUND_MALLOC (size + 1);
1444 dprintf(stderr, "%s, %s(): %p, 0x%lx\n",
1445 __FILE__, __FUNCTION__, ptr, (unsigned long)size);
1447 if (NO_CHECKING_GET() == 0) {
1448 WAIT_SEM ();
1449 INCR_COUNT(bound_malloc_count);
1451 if (ptr) {
1452 tree = splay_insert ((size_t) ptr, size ? size : size + 1, tree);
1453 if (tree && tree->start == (size_t) ptr)
1454 tree->type = TCC_TYPE_MALLOC;
1456 POST_SEM ();
1458 return ptr;
1461 #if MALLOC_REDIR
1462 void *memalign(size_t size, size_t align)
1463 #else
1464 void *__bound_memalign(size_t size, size_t align, const void *caller)
1465 #endif
1467 void *ptr;
1469 #if HAVE_MEMALIGN
1470 /* we allocate one more byte to ensure the regions will be
1471 separated by at least one byte. With the glibc malloc, it may
1472 be in fact not necessary */
1473 ptr = BOUND_MEMALIGN(size + 1, align);
1474 #else
1475 if (align > 4) {
1476 /* XXX: handle it ? */
1477 ptr = NULL;
1478 } else {
1479 /* we suppose that malloc aligns to at least four bytes */
1480 ptr = BOUND_MALLOC(size + 1);
1482 #endif
1483 dprintf(stderr, "%s, %s(): %p, 0x%lx\n",
1484 __FILE__, __FUNCTION__, ptr, (unsigned long)size);
1486 if (NO_CHECKING_GET() == 0) {
1487 WAIT_SEM ();
1488 INCR_COUNT(bound_memalign_count);
1490 if (ptr) {
1491 tree = splay_insert((size_t) ptr, size ? size : size + 1, tree);
1492 if (tree && tree->start == (size_t) ptr)
1493 tree->type = TCC_TYPE_MEMALIGN;
1495 POST_SEM ();
1497 return ptr;
1500 #if MALLOC_REDIR
1501 void free(void *ptr)
1502 #else
1503 void __bound_free(void *ptr, const void *caller)
1504 #endif
1506 size_t addr = (size_t) ptr;
1507 void *p;
1509 if (ptr == NULL || tree == NULL
1510 #if MALLOC_REDIR
1511 || ((unsigned char *) ptr >= &initial_pool[0] &&
1512 (unsigned char *) ptr < &initial_pool[sizeof(initial_pool)])
1513 #endif
1515 return;
1517 dprintf(stderr, "%s, %s(): %p\n", __FILE__, __FUNCTION__, ptr);
1519 if (NO_CHECKING_GET() == 0) {
1520 WAIT_SEM ();
1521 INCR_COUNT(bound_free_count);
1522 tree = splay (addr, tree);
1523 if (tree->start == addr) {
1524 if (tree->is_invalid) {
1525 POST_SEM ();
1526 bound_error("freeing invalid region");
1527 return;
1529 tree->is_invalid = 1;
1530 memset (ptr, 0x5a, tree->size);
1531 p = free_reuse_list[free_reuse_index];
1532 free_reuse_list[free_reuse_index] = ptr;
1533 free_reuse_index = (free_reuse_index + 1) % FREE_REUSE_SIZE;
1534 if (p)
1535 tree = splay_delete((size_t)p, tree);
1536 ptr = p;
1538 POST_SEM ();
1540 BOUND_FREE (ptr);
1543 #if MALLOC_REDIR
1544 void *realloc(void *ptr, size_t size)
1545 #else
1546 void *__bound_realloc(void *ptr, size_t size, const void *caller)
1547 #endif
1549 void *new_ptr;
1551 if (size == 0) {
1552 #if MALLOC_REDIR
1553 free(ptr);
1554 #else
1555 __bound_free(ptr, caller);
1556 #endif
1557 return NULL;
1560 new_ptr = BOUND_REALLOC (ptr, size + 1);
1561 dprintf(stderr, "%s, %s(): %p, 0x%lx\n",
1562 __FILE__, __FUNCTION__, new_ptr, (unsigned long)size);
1564 if (NO_CHECKING_GET() == 0) {
1565 WAIT_SEM ();
1566 INCR_COUNT(bound_realloc_count);
1568 if (ptr)
1569 tree = splay_delete ((size_t) ptr, tree);
1570 if (new_ptr) {
1571 tree = splay_insert ((size_t) new_ptr, size ? size : size + 1, tree);
1572 if (tree && tree->start == (size_t) new_ptr)
1573 tree->type = TCC_TYPE_REALLOC;
1575 POST_SEM ();
1577 return new_ptr;
1580 #if MALLOC_REDIR
1581 void *calloc(size_t nmemb, size_t size)
1582 #else
1583 void *__bound_calloc(size_t nmemb, size_t size)
1584 #endif
1586 void *ptr;
1588 size *= nmemb;
1589 #if MALLOC_REDIR
1590 /* This will catch the first dlsym call from __bound_init */
1591 if (malloc_redir == NULL) {
1592 __bound_init (0, -1);
1593 if (malloc_redir == NULL) {
1594 ptr = &initial_pool[pool_index];
1595 pool_index = (pool_index + size + 15) & ~15;
1596 if (pool_index >= sizeof (initial_pool))
1597 bound_alloc_error ("initial memory pool too small");
1598 dprintf (stderr, "%s, %s(): initial %p, 0x%lx\n",
1599 __FILE__, __FUNCTION__, ptr, (unsigned long)size);
1600 memset (ptr, 0, size);
1601 return ptr;
1604 #endif
1605 ptr = BOUND_MALLOC(size + 1);
1606 dprintf (stderr, "%s, %s(): %p, 0x%lx\n",
1607 __FILE__, __FUNCTION__, ptr, (unsigned long)size);
1609 if (ptr) {
1610 memset (ptr, 0, size);
1611 if (NO_CHECKING_GET() == 0) {
1612 WAIT_SEM ();
1613 INCR_COUNT(bound_calloc_count);
1614 tree = splay_insert ((size_t) ptr, size ? size : size + 1, tree);
1615 if (tree && tree->start == (size_t) ptr)
1616 tree->type = TCC_TYPE_CALLOC;
1617 POST_SEM ();
1620 return ptr;
1623 #if !defined(_WIN32)
1624 void *__bound_mmap (void *start, size_t size, int prot,
1625 int flags, int fd, off_t offset)
1627 void *result;
1629 dprintf(stderr, "%s, %s(): %p, 0x%lx\n",
1630 __FILE__, __FUNCTION__, start, (unsigned long)size);
1631 result = mmap (start, size, prot, flags, fd, offset);
1632 if (result && NO_CHECKING_GET() == 0) {
1633 WAIT_SEM ();
1634 INCR_COUNT(bound_mmap_count);
1635 tree = splay_insert((size_t)result, size, tree);
1636 POST_SEM ();
1638 return result;
1641 int __bound_munmap (void *start, size_t size)
1643 int result;
1645 dprintf(stderr, "%s, %s(): %p, 0x%lx\n",
1646 __FILE__, __FUNCTION__, start, (unsigned long)size);
1647 if (start && NO_CHECKING_GET() == 0) {
1648 WAIT_SEM ();
1649 INCR_COUNT(bound_munmap_count);
1650 tree = splay_delete ((size_t) start, tree);
1651 POST_SEM ();
1653 result = munmap (start, size);
1654 return result;
1656 #endif
1658 /* some useful checked functions */
1660 /* check that (p ... p + size - 1) lies inside 'p' region, if any */
1661 static void __bound_check(const void *p, size_t size, const char *function)
1663 if (size != 0 && __bound_ptr_add((void *)p, size) == INVALID_POINTER) {
1664 bound_error("invalid pointer %p, size 0x%lx in %s",
1665 p, (unsigned long)size, function);
1669 static int check_overlap (const void *p1, size_t n1,
1670 const void *p2, size_t n2,
1671 const char *function)
1673 const void *p1e = (const void *) ((const char *) p1 + n1);
1674 const void *p2e = (const void *) ((const char *) p2 + n2);
1676 if (NO_CHECKING_GET() == 0 && n1 != 0 && n2 !=0 &&
1677 ((p1 <= p2 && p1e > p2) || /* p1----p2====p1e----p2e */
1678 (p2 <= p1 && p2e > p1))) { /* p2----p1====p2e----p1e */
1679 bound_error("overlapping regions %p(0x%lx), %p(0x%lx) in %s",
1680 p1, (unsigned long)n1, p2, (unsigned long)n2, function);
1681 return never_fatal < 0;
1683 return 0;
1686 void *__bound_memcpy(void *dest, const void *src, size_t n)
1688 dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n",
1689 __FILE__, __FUNCTION__, dest, src, (unsigned long)n);
1690 INCR_COUNT(bound_mempcy_count);
1691 __bound_check(dest, n, "memcpy dest");
1692 __bound_check(src, n, "memcpy src");
1693 if (check_overlap(dest, n, src, n, "memcpy"))
1694 return dest;
1695 return memcpy(dest, src, n);
1698 int __bound_memcmp(const void *s1, const void *s2, size_t n)
1700 const unsigned char *u1 = (const unsigned char *) s1;
1701 const unsigned char *u2 = (const unsigned char *) s2;
1702 int retval = 0;
1704 dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n",
1705 __FILE__, __FUNCTION__, s1, s2, (unsigned long)n);
1706 INCR_COUNT(bound_memcmp_count);
1707 for (;;) {
1708 if ((ssize_t) --n == -1)
1709 break;
1710 else if (*u1 != *u2) {
1711 retval = *u1++ - *u2++;
1712 break;
1714 ++u1;
1715 ++u2;
1717 __bound_check(s1, (const void *)u1 - s1, "memcmp s1");
1718 __bound_check(s2, (const void *)u2 - s2, "memcmp s2");
1719 return retval;
1722 void *__bound_memmove(void *dest, const void *src, size_t n)
1724 dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n",
1725 __FILE__, __FUNCTION__, dest, src, (unsigned long)n);
1726 INCR_COUNT(bound_memmove_count);
1727 __bound_check(dest, n, "memmove dest");
1728 __bound_check(src, n, "memmove src");
1729 return memmove(dest, src, n);
1732 void *__bound_memset(void *s, int c, size_t n)
1734 dprintf(stderr, "%s, %s(): %p, %d, 0x%lx\n",
1735 __FILE__, __FUNCTION__, s, c, (unsigned long)n);
1736 INCR_COUNT(bound_memset_count);
1737 __bound_check(s, n, "memset");
1738 return memset(s, c, n);
1741 #if defined(__arm__) && defined(__ARM_EABI__)
1742 void *__bound___aeabi_memcpy(void *dest, const void *src, size_t n)
1744 dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n",
1745 __FILE__, __FUNCTION__, dest, src, (unsigned long)n);
1746 INCR_COUNT(bound_mempcy_count);
1747 __bound_check(dest, n, "memcpy dest");
1748 __bound_check(src, n, "memcpy src");
1749 if (check_overlap(dest, n, src, n, "memcpy"))
1750 return dest;
1751 return __aeabi_memcpy(dest, src, n);
1754 void *__bound___aeabi_memmove(void *dest, const void *src, size_t n)
1756 dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n",
1757 __FILE__, __FUNCTION__, dest, src, (unsigned long)n);
1758 INCR_COUNT(bound_memmove_count);
1759 __bound_check(dest, n, "memmove dest");
1760 __bound_check(src, n, "memmove src");
1761 return __aeabi_memmove(dest, src, n);
1764 void *__bound___aeabi_memmove4(void *dest, const void *src, size_t n)
1766 dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n",
1767 __FILE__, __FUNCTION__, dest, src, (unsigned long)n);
1768 INCR_COUNT(bound_memmove_count);
1769 __bound_check(dest, n, "memmove dest");
1770 __bound_check(src, n, "memmove src");
1771 return __aeabi_memmove4(dest, src, n);
1774 void *__bound___aeabi_memmove8(void *dest, const void *src, size_t n)
1776 dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n",
1777 __FILE__, __FUNCTION__, dest, src, (unsigned long)n);
1778 INCR_COUNT(bound_memmove_count);
1779 __bound_check(dest, n, "memmove dest");
1780 __bound_check(src, n, "memmove src");
1781 return __aeabi_memmove8(dest, src, n);
1784 void *__bound___aeabi_memset(void *s, int c, size_t n)
1786 dprintf(stderr, "%s, %s(): %p, %d, 0x%lx\n",
1787 __FILE__, __FUNCTION__, s, c, (unsigned long)n);
1788 INCR_COUNT(bound_memset_count);
1789 __bound_check(s, n, "memset");
1790 return __aeabi_memset(s, c, n);
1792 #endif
1794 int __bound_strlen(const char *s)
1796 const char *p = s;
1798 dprintf(stderr, "%s, %s(): %p\n",
1799 __FILE__, __FUNCTION__, s);
1800 INCR_COUNT(bound_strlen_count);
1801 while (*p++);
1802 __bound_check(s, p - s, "strlen");
1803 return (p - s) - 1;
1806 char *__bound_strcpy(char *dest, const char *src)
1808 size_t len;
1809 const char *p = src;
1811 dprintf(stderr, "%s, %s(): %p, %p\n",
1812 __FILE__, __FUNCTION__, dest, src);
1813 INCR_COUNT(bound_strcpy_count);
1814 while (*p++);
1815 len = p - src;
1816 __bound_check(dest, len, "strcpy dest");
1817 __bound_check(src, len, "strcpy src");
1818 if (check_overlap(dest, len, src, len, "strcpy"))
1819 return dest;
1820 return strcpy (dest, src);
1823 char *__bound_strncpy(char *dest, const char *src, size_t n)
1825 size_t len = n;
1826 const char *p = src;
1828 dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n",
1829 __FILE__, __FUNCTION__, dest, src, (unsigned long)n);
1830 INCR_COUNT(bound_strncpy_count);
1831 while (len-- && *p++);
1832 len = p - src;
1833 __bound_check(dest, len, "strncpy dest");
1834 __bound_check(src, len, "strncpy src");
1835 if (check_overlap(dest, len, src, len, "strncpy"))
1836 return dest;
1837 return strncpy(dest, src, n);
1840 int __bound_strcmp(const char *s1, const char *s2)
1842 const unsigned char *u1 = (const unsigned char *) s1;
1843 const unsigned char *u2 = (const unsigned char *) s2;
1845 dprintf(stderr, "%s, %s(): %p, %p\n",
1846 __FILE__, __FUNCTION__, s1, s2);
1847 INCR_COUNT(bound_strcmp_count);
1848 while (*u1 && *u1 == *u2) {
1849 ++u1;
1850 ++u2;
1852 __bound_check(s1, ((const char *)u1 - s1) + 1, "strcmp s1");
1853 __bound_check(s2, ((const char *)u2 - s2) + 1, "strcmp s2");
1854 return *u1 - *u2;
1857 int __bound_strncmp(const char *s1, const char *s2, size_t n)
1859 const unsigned char *u1 = (const unsigned char *) s1;
1860 const unsigned char *u2 = (const unsigned char *) s2;
1861 int retval = 0;
1863 dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n",
1864 __FILE__, __FUNCTION__, s1, s2, (unsigned long)n);
1865 INCR_COUNT(bound_strncmp_count);
1866 do {
1867 if ((ssize_t) --n == -1)
1868 break;
1869 else if (*u1 != *u2) {
1870 retval = *u1++ - *u2++;
1871 break;
1873 ++u2;
1874 } while (*u1++);
1875 __bound_check(s1, (const char *)u1 - s1, "strncmp s1");
1876 __bound_check(s2, (const char *)u2 - s2, "strncmp s2");
1877 return retval;
1880 char *__bound_strcat(char *dest, const char *src)
1882 char *r = dest;
1883 const char *s = src;
1885 dprintf(stderr, "%s, %s(): %p, %p\n",
1886 __FILE__, __FUNCTION__, dest, src);
1887 INCR_COUNT(bound_strcat_count);
1888 while (*dest++);
1889 while (*src++);
1890 __bound_check(r, (dest - r) + (src - s) - 1, "strcat dest");
1891 __bound_check(s, src - s, "strcat src");
1892 if (check_overlap(r, (dest - r) + (src - s) - 1, s, src - s, "strcat"))
1893 return dest;
1894 return strcat(r, s);
1897 char *__bound_strchr(const char *s, int c)
1899 const unsigned char *str = (const unsigned char *) s;
1900 unsigned char ch = c;
1902 dprintf(stderr, "%s, %s(): %p, %d\n",
1903 __FILE__, __FUNCTION__, s, ch);
1904 INCR_COUNT(bound_strchr_count);
1905 while (*str) {
1906 if (*str == ch)
1907 break;
1908 ++str;
1910 __bound_check(s, ((const char *)str - s) + 1, "strchr");
1911 return *str == ch ? (char *) str : NULL;
1914 char *__bound_strdup(const char *s)
1916 const char *p = s;
1917 char *new;
1919 INCR_COUNT(bound_strdup_count);
1920 while (*p++);
1921 __bound_check(s, p - s, "strdup");
1922 new = BOUND_MALLOC ((p - s) + 1);
1923 dprintf(stderr, "%s, %s(): %p, 0x%lx\n",
1924 __FILE__, __FUNCTION__, new, (unsigned long)(p -s));
1925 if (new) {
1926 if (NO_CHECKING_GET() == 0 && no_strdup == 0) {
1927 WAIT_SEM ();
1928 tree = splay_insert((size_t)new, p - s, tree);
1929 if (tree && tree->start == (size_t) new)
1930 tree->type = TCC_TYPE_STRDUP;
1931 POST_SEM ();
1933 memcpy (new, s, p - s);
1935 return new;
1939 An implementation of top-down splaying with sizes
1940 D. Sleator <sleator@cs.cmu.edu>, January 1994.
1942 This extends top-down-splay.c to maintain a size field in each node.
1943 This is the number of nodes in the subtree rooted there. This makes
1944 it possible to efficiently compute the rank of a key. (The rank is
1945 the number of nodes to the left of the given key.) It it also
1946 possible to quickly find the node of a given rank. Both of these
1947 operations are illustrated in the code below. The remainder of this
1948 introduction is taken from top-down-splay.c.
1950 "Splay trees", or "self-adjusting search trees" are a simple and
1951 efficient data structure for storing an ordered set. The data
1952 structure consists of a binary tree, with no additional fields. It
1953 allows searching, insertion, deletion, deletemin, deletemax,
1954 splitting, joining, and many other operations, all with amortized
1955 logarithmic performance. Since the trees adapt to the sequence of
1956 requests, their performance on real access patterns is typically even
1957 better. Splay trees are described in a number of texts and papers
1958 [1,2,3,4].
1960 The code here is adapted from simple top-down splay, at the bottom of
1961 page 669 of [2]. It can be obtained via anonymous ftp from
1962 spade.pc.cs.cmu.edu in directory /usr/sleator/public.
1964 The chief modification here is that the splay operation works even if the
1965 item being splayed is not in the tree, and even if the tree root of the
1966 tree is NULL. So the line:
1968 t = splay(i, t);
1970 causes it to search for item with key i in the tree rooted at t. If it's
1971 there, it is splayed to the root. If it isn't there, then the node put
1972 at the root is the last one before NULL that would have been reached in a
1973 normal binary search for i. (It's a neighbor of i in the tree.) This
1974 allows many other operations to be easily implemented, as shown below.
1976 [1] "Data Structures and Their Algorithms", Lewis and Denenberg,
1977 Harper Collins, 1991, pp 243-251.
1978 [2] "Self-adjusting Binary Search Trees" Sleator and Tarjan,
1979 JACM Volume 32, No 3, July 1985, pp 652-686.
1980 [3] "Data Structure and Algorithm Analysis", Mark Weiss,
1981 Benjamin Cummins, 1992, pp 119-130.
1982 [4] "Data Structures, Algorithms, and Performance", Derick Wood,
1983 Addison-Wesley, 1993, pp 367-375
1986 /* Code adapted for tcc */
1988 #define compare(start,tstart,tsize) (start < tstart ? -1 : \
1989 start >= tstart+tsize ? 1 : 0)
1991 static Tree * splay (size_t addr, Tree *t)
1992 /* Splay using the key start (which may or may not be in the tree.) */
1993 /* The starting root is t, and the tree used is defined by rat */
1995 Tree N, *l, *r, *y;
1996 int comp;
1998 INCR_COUNT_SPLAY(bound_splay);
1999 if (t == NULL) return t;
2000 N.left = N.right = NULL;
2001 l = r = &N;
2003 for (;;) {
2004 comp = compare(addr, t->start, t->size);
2005 if (comp < 0) {
2006 y = t->left;
2007 if (y == NULL) break;
2008 if (compare(addr, y->start, y->size) < 0) {
2009 t->left = y->right; /* rotate right */
2010 y->right = t;
2011 t = y;
2012 if (t->left == NULL) break;
2014 r->left = t; /* link right */
2015 r = t;
2016 t = t->left;
2017 } else if (comp > 0) {
2018 y = t->right;
2019 if (y == NULL) break;
2020 if (compare(addr, y->start, y->size) > 0) {
2021 t->right = y->left; /* rotate left */
2022 y->left = t;
2023 t = y;
2024 if (t->right == NULL) break;
2026 l->right = t; /* link left */
2027 l = t;
2028 t = t->right;
2029 } else {
2030 break;
2033 l->right = t->left; /* assemble */
2034 r->left = t->right;
2035 t->left = N.right;
2036 t->right = N.left;
2038 return t;
2041 #define compare_end(start,tend) (start < tend ? -1 : \
2042 start > tend ? 1 : 0)
2044 static Tree * splay_end (size_t addr, Tree *t)
2045 /* Splay using the key start (which may or may not be in the tree.) */
2046 /* The starting root is t, and the tree used is defined by rat */
2048 Tree N, *l, *r, *y;
2049 int comp;
2051 INCR_COUNT_SPLAY(bound_splay_end);
2052 if (t == NULL) return t;
2053 N.left = N.right = NULL;
2054 l = r = &N;
2056 for (;;) {
2057 comp = compare_end(addr, t->start + t->size);
2058 if (comp < 0) {
2059 y = t->left;
2060 if (y == NULL) break;
2061 if (compare_end(addr, y->start + y->size) < 0) {
2062 t->left = y->right; /* rotate right */
2063 y->right = t;
2064 t = y;
2065 if (t->left == NULL) break;
2067 r->left = t; /* link right */
2068 r = t;
2069 t = t->left;
2070 } else if (comp > 0) {
2071 y = t->right;
2072 if (y == NULL) break;
2073 if (compare_end(addr, y->start + y->size) > 0) {
2074 t->right = y->left; /* rotate left */
2075 y->left = t;
2076 t = y;
2077 if (t->right == NULL) break;
2079 l->right = t; /* link left */
2080 l = t;
2081 t = t->right;
2082 } else {
2083 break;
2086 l->right = t->left; /* assemble */
2087 r->left = t->right;
2088 t->left = N.right;
2089 t->right = N.left;
2091 return t;
2094 static Tree * splay_insert(size_t addr, size_t size, Tree * t)
2095 /* Insert key start into the tree t, if it is not already there. */
2096 /* Return a pointer to the resulting tree. */
2098 Tree * new;
2100 INCR_COUNT_SPLAY(bound_splay_insert);
2101 if (t != NULL) {
2102 t = splay(addr,t);
2103 if (compare(addr, t->start, t->size)==0) {
2104 return t; /* it's already there */
2107 #if TREE_REUSE
2108 if (tree_free_list) {
2109 new = tree_free_list;
2110 tree_free_list = new->left;
2112 else
2113 #endif
2115 new = (Tree *) BOUND_MALLOC (sizeof (Tree));
2117 if (new == NULL) {
2118 bound_alloc_error("not enough memory for bound checking code");
2120 else {
2121 if (t == NULL) {
2122 new->left = new->right = NULL;
2123 } else if (compare(addr, t->start, t->size) < 0) {
2124 new->left = t->left;
2125 new->right = t;
2126 t->left = NULL;
2127 } else {
2128 new->right = t->right;
2129 new->left = t;
2130 t->right = NULL;
2132 new->start = addr;
2133 new->size = size;
2134 new->type = TCC_TYPE_NONE;
2135 new->is_invalid = 0;
2137 return new;
2140 #define compare_destroy(start,tstart) (start < tstart ? -1 : \
2141 start > tstart ? 1 : 0)
2143 static Tree * splay_delete(size_t addr, Tree *t)
2144 /* Deletes addr from the tree if it's there. */
2145 /* Return a pointer to the resulting tree. */
2147 Tree * x;
2149 INCR_COUNT_SPLAY(bound_splay_delete);
2150 if (t==NULL) return NULL;
2151 t = splay(addr,t);
2152 if (compare_destroy(addr, t->start) == 0) { /* found it */
2153 if (t->left == NULL) {
2154 x = t->right;
2155 } else {
2156 x = splay(addr, t->left);
2157 x->right = t->right;
2159 #if TREE_REUSE
2160 t->left = tree_free_list;
2161 tree_free_list = t;
2162 #else
2163 BOUND_FREE(t);
2164 #endif
2165 return x;
2166 } else {
2167 return t; /* It wasn't there */
2171 void splay_printtree(Tree * t, int d)
2173 int i;
2174 if (t == NULL) return;
2175 splay_printtree(t->right, d+1);
2176 for (i=0; i<d; i++) fprintf(stderr," ");
2177 fprintf(stderr,"%p(0x%lx:%u:%u)\n",
2178 (void *) t->start, (unsigned long) t->size,
2179 (unsigned)t->type, (unsigned)t->is_invalid);
2180 splay_printtree(t->left, d+1);