added better relocation support (needed for file output)
[tinycc.git] / bcheck.c
blob242c055bf72cc366190f67815a6e67cb719ea37c
1 /*
2 * Tiny C Memory and bounds checker
3 *
4 * Copyright (c) 2002 Fabrice Bellard
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 //#define BOUND_DEBUG
23 /* define so that bound array is static (faster, but use memory if
24 bound checking not used) */
25 //#define BOUND_STATIC
27 /* use malloc hooks. Currently the code cannot be reliable if no hooks */
28 #define CONFIG_TCC_MALLOC_HOOKS
30 #define BOUND_T1_BITS 13
31 #define BOUND_T2_BITS 11
32 #define BOUND_T3_BITS (32 - BOUND_T1_BITS - BOUND_T2_BITS)
34 #define BOUND_T1_SIZE (1 << BOUND_T1_BITS)
35 #define BOUND_T2_SIZE (1 << BOUND_T2_BITS)
36 #define BOUND_T3_SIZE (1 << BOUND_T3_BITS)
37 #define BOUND_E_BITS 4
39 #define BOUND_T23_BITS (BOUND_T2_BITS + BOUND_T3_BITS)
40 #define BOUND_T23_SIZE (1 << BOUND_T23_BITS)
43 /* this pointer is generated when bound check is incorrect */
44 #define INVALID_POINTER ((void *)(-2))
45 /* size of an empty region */
46 #define EMPTY_SIZE 0xffffffff
47 /* size of an invalid region */
48 #define INVALID_SIZE 0
50 typedef struct BoundEntry {
51 unsigned long start;
52 unsigned long size;
53 struct BoundEntry *next;
54 unsigned long is_invalid; /* true if pointers outside region are invalid */
55 } BoundEntry;
57 /* external interface */
58 void __bound_init(void);
59 void __bound_new_region(void *p, unsigned long size);
60 int __bound_delete_region(void *p);
62 /* currently, tcc cannot compile that because we use unsupported GNU C
63 extensions */
64 #if !defined(__TINYC__)
65 void *__bound_ptr_add(void *p, int offset) __attribute__((regparm(2)));
66 void *__bound_ptr_indir1(void *p, int offset) __attribute__((regparm(2)));
67 void *__bound_ptr_indir2(void *p, int offset) __attribute__((regparm(2)));
68 void *__bound_ptr_indir4(void *p, int offset) __attribute__((regparm(2)));
69 void *__bound_ptr_indir8(void *p, int offset) __attribute__((regparm(2)));
70 void *__bound_ptr_indir12(void *p, int offset) __attribute__((regparm(2)));
71 void *__bound_ptr_indir16(void *p, int offset) __attribute__((regparm(2)));
72 void __bound_local_new(void *p) __attribute__((regparm(1)));
73 void __bound_local_delete(void *p) __attribute__((regparm(1)));
74 #endif
75 static void *get_caller_pc(int n);
77 void *__bound_malloc(size_t size, const void *caller);
78 void *__bound_memalign(size_t size, size_t align, const void *caller);
79 void __bound_free(void *ptr, const void *caller);
80 void *__bound_realloc(void *ptr, size_t size, const void *caller);
81 static void *libc_malloc(size_t size);
82 static void libc_free(void *ptr);
83 static void install_malloc_hooks(void);
84 static void restore_malloc_hooks(void);
86 #ifdef CONFIG_TCC_MALLOC_HOOKS
87 static void *saved_malloc_hook;
88 static void *saved_free_hook;
89 static void *saved_realloc_hook;
90 static void *saved_memalign_hook;
91 #endif
93 extern char _end;
95 #ifdef BOUND_STATIC
96 static BoundEntry *__bound_t1[BOUND_T1_SIZE]; /* page table */
97 #else
98 static BoundEntry **__bound_t1; /* page table */
99 #endif
100 static BoundEntry *__bound_empty_t2; /* empty page, for unused pages */
101 static BoundEntry *__bound_invalid_t2; /* invalid page, for invalid pointers */
103 static BoundEntry *__bound_find_region(BoundEntry *e1, void *p)
105 unsigned long addr, tmp;
106 BoundEntry *e;
108 e = e1;
109 while (e != NULL) {
110 addr = (unsigned long)p;
111 addr -= e->start;
112 if (addr <= e->size) {
113 /* put region at the head */
114 tmp = e1->start;
115 e1->start = e->start;
116 e->start = tmp;
117 tmp = e1->size;
118 e1->size = e->size;
119 e->size = tmp;
120 return e1;
122 e = e->next;
124 /* no entry found: return empty entry or invalid entry */
125 if (e1->is_invalid)
126 return __bound_invalid_t2;
127 else
128 return __bound_empty_t2;
131 /* print a bound error message */
132 static void bound_error(const void *caller, const char *fmt, ...)
134 rt_error((unsigned long)caller, "%s", fmt);
137 static void bound_alloc_error(void)
139 bound_error(NULL, "not enough memory for bound checking code");
142 /* currently, tcc cannot compile that because we use GNUC extensions */
143 #if !defined(__TINYC__)
145 /* return '(p + offset)' for pointer arithmetic (a pointer can reach
146 the end of a region in this case */
147 void *__bound_ptr_add(void *p, int offset)
149 unsigned long addr = (unsigned long)p;
150 BoundEntry *e;
151 #if defined(BOUND_DEBUG)
152 printf("add: 0x%x %d\n", (int)p, offset);
153 #endif
155 e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)];
156 e = (BoundEntry *)((char *)e +
157 ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) &
158 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS)));
159 addr -= e->start;
160 if (addr > e->size) {
161 e = __bound_find_region(e, p);
162 addr = (unsigned long)p - e->start;
164 addr += offset;
165 if (addr > e->size)
166 return INVALID_POINTER; /* return an invalid pointer */
167 return p + offset;
170 /* return '(p + offset)' for pointer indirection (the resulting must
171 be strictly inside the region */
172 #define BOUND_PTR_INDIR(dsize) \
173 void *__bound_ptr_indir ## dsize (void *p, int offset) \
175 unsigned long addr = (unsigned long)p; \
176 BoundEntry *e; \
178 e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; \
179 e = (BoundEntry *)((char *)e + \
180 ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & \
181 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); \
182 addr -= e->start; \
183 if (addr > e->size) { \
184 e = __bound_find_region(e, p); \
185 addr = (unsigned long)p - e->start; \
187 addr += offset + dsize; \
188 if (addr > e->size) \
189 return INVALID_POINTER; /* return an invalid pointer */ \
190 return p + offset; \
193 #ifdef __i386__
195 /* return the PC of the N'th caller (N=1: first caller) */
196 static void *get_caller_pc(int n)
198 unsigned long fp;
199 int i;
201 __asm__ __volatile__ ("movl %%ebp,%0" :"=g" (fp));
202 for(i=0;i<n;i++)
203 fp = ((unsigned long *)fp)[0];
204 return ((void **)fp)[1];
207 /* return the frame pointer of the caller */
208 #define GET_CALLER_FP(fp)\
210 unsigned long *fp1;\
211 __asm__ __volatile__ ("movl %%ebp,%0" :"=g" (fp1));\
212 fp = fp1[0];\
214 #else
215 #error put code to extract the calling frame pointer
216 #endif
218 /* called when entering a function to add all the local regions */
219 void __bound_local_new(void *p1)
221 unsigned long addr, size, fp, *p = p1;
222 GET_CALLER_FP(fp);
223 for(;;) {
224 addr = p[0];
225 if (addr == 0)
226 break;
227 addr += fp;
228 size = p[1];
229 p += 2;
230 __bound_new_region((void *)addr, size);
234 /* called when leaving a function to delete all the local regions */
235 void __bound_local_delete(void *p1)
237 unsigned long addr, fp, *p = p1;
238 GET_CALLER_FP(fp);
239 for(;;) {
240 addr = p[0];
241 if (addr == 0)
242 break;
243 addr += fp;
244 p += 2;
245 __bound_delete_region((void *)addr);
249 #else
251 void __bound_local_new(void *p)
254 void __bound_local_delete(void *p)
258 void *__bound_ptr_add(void *p, int offset)
260 return p + offset;
263 #define BOUND_PTR_INDIR(dsize) \
264 void *__bound_ptr_indir ## dsize (void *p, int offset) \
266 return p + offset; \
269 static void *get_caller_pc(int n)
271 return 0;
273 #endif
275 BOUND_PTR_INDIR(1)
276 BOUND_PTR_INDIR(2)
277 BOUND_PTR_INDIR(4)
278 BOUND_PTR_INDIR(8)
279 BOUND_PTR_INDIR(12)
280 BOUND_PTR_INDIR(16)
282 static BoundEntry *__bound_new_page(void)
284 BoundEntry *page;
285 int i;
287 page = libc_malloc(sizeof(BoundEntry) * BOUND_T2_SIZE);
288 if (!page)
289 bound_alloc_error();
290 for(i=0;i<BOUND_T2_SIZE;i++) {
291 /* put empty entries */
292 page[i].start = 0;
293 page[i].size = EMPTY_SIZE;
294 page[i].next = NULL;
295 page[i].is_invalid = 0;
297 return page;
300 /* currently we use malloc(). Should use bound_new_page() */
301 static BoundEntry *bound_new_entry(void)
303 BoundEntry *e;
304 e = libc_malloc(sizeof(BoundEntry));
305 return e;
308 static void bound_free_entry(BoundEntry *e)
310 libc_free(e);
313 static inline BoundEntry *get_page(int index)
315 BoundEntry *page;
316 page = __bound_t1[index];
317 if (page == __bound_empty_t2 || page == __bound_invalid_t2) {
318 /* create a new page if necessary */
319 page = __bound_new_page();
320 __bound_t1[index] = page;
322 return page;
325 /* mark a region as being invalid (can only be used during init) */
326 static void mark_invalid(unsigned long addr, unsigned long size)
328 unsigned long start, end;
329 BoundEntry *page;
330 int t1_start, t1_end, i, j, t2_start, t2_end;
332 start = addr;
333 end = addr + size;
335 t2_start = (start + BOUND_T3_SIZE - 1) >> BOUND_T3_BITS;
336 if (end != 0)
337 t2_end = end >> BOUND_T3_BITS;
338 else
339 t2_end = 1 << (BOUND_T1_BITS + BOUND_T2_BITS);
341 #if 0
342 printf("mark_invalid: start = %x %x\n", t2_start, t2_end);
343 #endif
345 /* first we handle full pages */
346 t1_start = (t2_start + BOUND_T2_SIZE - 1) >> BOUND_T2_BITS;
347 t1_end = t2_end >> BOUND_T2_BITS;
349 i = t2_start & (BOUND_T2_SIZE - 1);
350 j = t2_end & (BOUND_T2_SIZE - 1);
352 if (t1_start == t1_end) {
353 page = get_page(t2_start >> BOUND_T2_BITS);
354 for(; i < j; i++) {
355 page[i].size = INVALID_SIZE;
356 page[i].is_invalid = 1;
358 } else {
359 if (i > 0) {
360 page = get_page(t2_start >> BOUND_T2_BITS);
361 for(; i < BOUND_T2_SIZE; i++) {
362 page[i].size = INVALID_SIZE;
363 page[i].is_invalid = 1;
366 for(i = t1_start; i < t1_end; i++) {
367 __bound_t1[i] = __bound_invalid_t2;
369 if (j != 0) {
370 page = get_page(t1_end);
371 for(i = 0; i < j; i++) {
372 page[i].size = INVALID_SIZE;
373 page[i].is_invalid = 1;
379 void __bound_init(void)
381 int i;
382 BoundEntry *page;
383 unsigned long start, size;
385 /* save malloc hooks and install bound check hooks */
386 install_malloc_hooks();
388 #ifndef BOUND_STATIC
389 __bound_t1 = libc_malloc(BOUND_T1_SIZE * sizeof(BoundEntry *));
390 if (!__bound_t1)
391 bound_alloc_error();
392 #endif
393 __bound_empty_t2 = __bound_new_page();
394 for(i=0;i<BOUND_T1_SIZE;i++) {
395 __bound_t1[i] = __bound_empty_t2;
398 page = __bound_new_page();
399 for(i=0;i<BOUND_T2_SIZE;i++) {
400 /* put invalid entries */
401 page[i].start = 0;
402 page[i].size = INVALID_SIZE;
403 page[i].next = NULL;
404 page[i].is_invalid = 1;
406 __bound_invalid_t2 = page;
408 /* invalid pointer zone */
409 start = (unsigned long)INVALID_POINTER & ~(BOUND_T23_SIZE - 1);
410 size = BOUND_T23_SIZE;
411 mark_invalid(start, size);
413 #if !defined(__TINYC__) && defined(CONFIG_TCC_MALLOC_HOOKS)
414 /* malloc zone is also marked invalid. can only use that with
415 hooks because all libs should use the same malloc. The solution
416 would be to build a new malloc for tcc. */
417 start = (unsigned long)&_end;
418 size = 128 * 0x100000;
419 mark_invalid(start, size);
420 #endif
423 static inline void add_region(BoundEntry *e,
424 unsigned long start, unsigned long size)
426 BoundEntry *e1;
427 if (e->start == 0) {
428 /* no region : add it */
429 e->start = start;
430 e->size = size;
431 } else {
432 /* already regions in the list: add it at the head */
433 e1 = bound_new_entry();
434 e1->start = e->start;
435 e1->size = e->size;
436 e1->next = e->next;
437 e->start = start;
438 e->size = size;
439 e->next = e1;
443 /* create a new region. It should not already exist in the region list */
444 void __bound_new_region(void *p, unsigned long size)
446 unsigned long start, end;
447 BoundEntry *page, *e, *e2;
448 int t1_start, t1_end, i, t2_start, t2_end;
450 start = (unsigned long)p;
451 end = start + size;
452 t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS);
453 t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS);
455 /* start */
456 page = get_page(t1_start);
457 t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) &
458 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
459 t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) &
460 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
461 #ifdef BOUND_DEBUG
462 printf("new %lx %lx %x %x %x %x\n",
463 start, end, t1_start, t1_end, t2_start, t2_end);
464 #endif
466 e = (BoundEntry *)((char *)page + t2_start);
467 add_region(e, start, size);
469 if (t1_end == t1_start) {
470 /* same ending page */
471 e2 = (BoundEntry *)((char *)page + t2_end);
472 if (e2 > e) {
473 e++;
474 for(;e<e2;e++) {
475 e->start = start;
476 e->size = size;
478 add_region(e, start, size);
480 } else {
481 /* mark until end of page */
482 e2 = page + BOUND_T2_SIZE;
483 e++;
484 for(;e<e2;e++) {
485 e->start = start;
486 e->size = size;
488 /* mark intermediate pages, if any */
489 for(i=t1_start+1;i<t1_end;i++) {
490 page = get_page(i);
491 e2 = page + BOUND_T2_SIZE;
492 for(e=page;e<e2;e++) {
493 e->start = start;
494 e->size = size;
497 /* last page */
498 page = get_page(t1_end);
499 e2 = (BoundEntry *)((char *)page + t2_end);
500 for(e=page;e<e2;e++) {
501 e->start = start;
502 e->size = size;
504 add_region(e, start, size);
508 /* delete a region */
509 static inline void delete_region(BoundEntry *e,
510 void *p, unsigned long empty_size)
512 unsigned long addr;
513 BoundEntry *e1;
515 addr = (unsigned long)p;
516 addr -= e->start;
517 if (addr <= e->size) {
518 /* region found is first one */
519 e1 = e->next;
520 if (e1 == NULL) {
521 /* no more region: mark it empty */
522 e->start = 0;
523 e->size = empty_size;
524 } else {
525 /* copy next region in head */
526 e->start = e1->start;
527 e->size = e1->size;
528 e->next = e1->next;
529 bound_free_entry(e1);
531 } else {
532 /* find the matching region */
533 for(;;) {
534 e1 = e;
535 e = e->next;
536 /* region not found: do nothing */
537 if (e == NULL)
538 break;
539 addr = (unsigned long)p - e->start;
540 if (addr <= e->size) {
541 /* found: remove entry */
542 e1->next = e->next;
543 bound_free_entry(e);
544 break;
550 /* WARNING: 'p' must be the starting point of the region. */
551 /* return non zero if error */
552 int __bound_delete_region(void *p)
554 unsigned long start, end, addr, size, empty_size;
555 BoundEntry *page, *e, *e2;
556 int t1_start, t1_end, t2_start, t2_end, i;
558 start = (unsigned long)p;
559 t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS);
560 t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) &
561 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
563 /* find region size */
564 page = __bound_t1[t1_start];
565 e = (BoundEntry *)((char *)page + t2_start);
566 addr = start - e->start;
567 if (addr > e->size)
568 e = __bound_find_region(e, p);
569 /* test if invalid region */
570 if (e->size == EMPTY_SIZE || (unsigned long)p != e->start)
571 return -1;
572 /* compute the size we put in invalid regions */
573 if (e->is_invalid)
574 empty_size = INVALID_SIZE;
575 else
576 empty_size = EMPTY_SIZE;
577 size = e->size;
578 end = start + size;
580 /* now we can free each entry */
581 t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS);
582 t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) &
583 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
585 delete_region(e, p, empty_size);
586 if (t1_end == t1_start) {
587 /* same ending page */
588 e2 = (BoundEntry *)((char *)page + t2_end);
589 if (e2 > e) {
590 e++;
591 for(;e<e2;e++) {
592 e->start = 0;
593 e->size = empty_size;
595 delete_region(e, p, empty_size);
597 } else {
598 /* mark until end of page */
599 e2 = page + BOUND_T2_SIZE;
600 e++;
601 for(;e<e2;e++) {
602 e->start = 0;
603 e->size = empty_size;
605 /* mark intermediate pages, if any */
606 /* XXX: should free them */
607 for(i=t1_start+1;i<t1_end;i++) {
608 page = get_page(i);
609 e2 = page + BOUND_T2_SIZE;
610 for(e=page;e<e2;e++) {
611 e->start = 0;
612 e->size = empty_size;
615 /* last page */
616 page = get_page(t2_end);
617 e2 = (BoundEntry *)((char *)page + t2_end);
618 for(e=page;e<e2;e++) {
619 e->start = 0;
620 e->size = empty_size;
622 delete_region(e, p, empty_size);
624 return 0;
627 /* return the size of the region starting at p, or EMPTY_SIZE if non
628 existant region. */
629 static unsigned long get_region_size(void *p)
631 unsigned long addr = (unsigned long)p;
632 BoundEntry *e;
634 e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)];
635 e = (BoundEntry *)((char *)e +
636 ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) &
637 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS)));
638 addr -= e->start;
639 if (addr > e->size)
640 e = __bound_find_region(e, p);
641 if (e->start != (unsigned long)p)
642 return EMPTY_SIZE;
643 return e->size;
646 /* patched memory functions */
648 static void install_malloc_hooks(void)
650 #ifdef CONFIG_TCC_MALLOC_HOOKS
651 saved_malloc_hook = __malloc_hook;
652 saved_free_hook = __free_hook;
653 saved_realloc_hook = __realloc_hook;
654 saved_memalign_hook = __memalign_hook;
655 __malloc_hook = __bound_malloc;
656 __free_hook = __bound_free;
657 __realloc_hook = __bound_realloc;
658 __memalign_hook = __bound_memalign;
659 #endif
662 static void restore_malloc_hooks(void)
664 #ifdef CONFIG_TCC_MALLOC_HOOKS
665 __malloc_hook = saved_malloc_hook;
666 __free_hook = saved_free_hook;
667 __realloc_hook = saved_realloc_hook;
668 __memalign_hook = saved_memalign_hook;
669 #endif
672 static void *libc_malloc(size_t size)
674 void *ptr;
675 restore_malloc_hooks();
676 ptr = malloc(size);
677 install_malloc_hooks();
678 return ptr;
681 static void libc_free(void *ptr)
683 restore_malloc_hooks();
684 free(ptr);
685 install_malloc_hooks();
688 /* XXX: we should use a malloc which ensure that it is unlikely that
689 two malloc'ed data have the same address if 'free' are made in
690 between. */
691 void *__bound_malloc(size_t size, const void *caller)
693 void *ptr;
695 /* we allocate one more byte to ensure the regions will be
696 separated by at least one byte. With the glibc malloc, it may
697 be in fact not necessary */
698 ptr = libc_malloc(size + 1);
700 if (!ptr)
701 return NULL;
702 __bound_new_region(ptr, size);
703 return ptr;
706 void *__bound_memalign(size_t size, size_t align, const void *caller)
708 void *ptr;
710 restore_malloc_hooks();
712 /* we allocate one more byte to ensure the regions will be
713 separated by at least one byte. With the glibc malloc, it may
714 be in fact not necessary */
715 ptr = memalign(size + 1, align);
717 install_malloc_hooks();
719 if (!ptr)
720 return NULL;
721 __bound_new_region(ptr, size);
722 return ptr;
725 void __bound_free(void *ptr, const void *caller)
727 if (ptr == NULL)
728 return;
729 if (__bound_delete_region(ptr) != 0)
730 bound_error(caller, "freeing invalid region");
732 libc_free(ptr);
735 void *__bound_realloc(void *ptr, size_t size, const void *caller)
737 void *ptr1;
738 int old_size;
740 if (size == 0) {
741 __bound_free(ptr, caller);
742 return NULL;
743 } else {
744 ptr1 = __bound_malloc(size, caller);
745 if (ptr == NULL || ptr1 == NULL)
746 return ptr1;
747 old_size = get_region_size(ptr);
748 if (old_size == EMPTY_SIZE)
749 bound_error(caller, "realloc'ing invalid pointer");
750 memcpy(ptr1, ptr, old_size);
751 __bound_free(ptr, caller);
752 return ptr1;
756 #ifndef CONFIG_TCC_MALLOC_HOOKS
757 void *__bound_calloc(size_t nmemb, size_t size)
759 void *ptr;
760 size = size * nmemb;
761 ptr = __bound_malloc(size);
762 if (!ptr)
763 return NULL;
764 memset(ptr, 0, size);
765 return ptr;
767 #endif
769 #if 0
770 static void bound_dump(void)
772 BoundEntry *page, *e;
773 int i, j;
775 printf("region dump:\n");
776 for(i=0;i<BOUND_T1_SIZE;i++) {
777 page = __bound_t1[i];
778 for(j=0;j<BOUND_T2_SIZE;j++) {
779 e = page + j;
780 /* do not print invalid or empty entries */
781 if (e->size != EMPTY_SIZE && e->start != 0) {
782 printf("%08x:",
783 (i << (BOUND_T2_BITS + BOUND_T3_BITS)) +
784 (j << BOUND_T3_BITS));
785 do {
786 printf(" %08lx:%08lx", e->start, e->start + e->size);
787 e = e->next;
788 } while (e != NULL);
789 printf("\n");
794 #endif
796 /* some useful checked functions */
798 /* check that (p ... p + size - 1) lies inside 'p' region, if any */
799 static void __bound_check(const void *p, size_t size)
801 if (size == 0)
802 return;
803 p = __bound_ptr_add((void *)p, size);
804 if (p == INVALID_POINTER)
805 bound_error(get_caller_pc(2), "invalid pointer");
808 void *__bound_memcpy(void *dst, const void *src, size_t size)
810 __bound_check(dst, size);
811 __bound_check(src, size);
812 /* check also region overlap */
813 if (src >= dst && src < dst + size)
814 bound_error(get_caller_pc(1), "overlapping regions in memcpy()");
815 return memcpy(dst, src, size);
818 void *__bound_memmove(void *dst, const void *src, size_t size)
820 __bound_check(dst, size);
821 __bound_check(src, size);
822 return memmove(dst, src, size);
825 void *__bound_memset(void *dst, int c, size_t size)
827 __bound_check(dst, size);
828 return memset(dst, c, size);
831 /* XXX: could be optimized */
832 int __bound_strlen(const char *s)
834 const char *p;
835 int len;
837 len = 0;
838 for(;;) {
839 p = __bound_ptr_indir1((char *)s, len);
840 if (p == INVALID_POINTER)
841 bound_error(get_caller_pc(1), "bad pointer in strlen()");
842 if (*p == '\0')
843 break;
844 len++;
846 return len;
849 char *__bound_strcpy(char *dst, const char *src)
851 int len;
852 len = __bound_strlen(src);
853 return __bound_memcpy(dst, src, len + 1);
856 /* resolve bound check syms */
857 typedef struct BCSyms {
858 char *str;
859 void *ptr;
860 } BCSyms;
862 static BCSyms bcheck_syms[] = {
863 #ifndef CONFIG_TCC_MALLOC_HOOKS
864 { "malloc", __bound_malloc },
865 { "free", __bound_free },
866 { "realloc", __bound_realloc },
867 { "memalign", __bound_memalign },
868 { "calloc", __bound_calloc },
869 #endif
870 { "memcpy", __bound_memcpy },
871 { "memmove", __bound_memmove },
872 { "memset", __bound_memset },
873 { "strlen", __bound_strlen },
874 { "strcpy", __bound_strcpy },
875 { NULL, NULL },
878 static void *bound_resolve_sym(const char *sym)
880 BCSyms *p;
881 p = bcheck_syms;
882 while (p->str != NULL) {
883 if (!strcmp(p->str, sym))
884 return p->ptr;
885 p++;
887 return NULL;