fixed -w option - doc update (thanx to Romain Francoise)
[tinycc.git] / bcheck.c
blob11b1f391d5642ca991889f5b9171b14d1a745e2f
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.
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #ifndef __FreeBSD__
25 #include <malloc.h>
26 #endif
28 //#define BOUND_DEBUG
30 /* define so that bound array is static (faster, but use memory if
31 bound checking not used) */
32 //#define BOUND_STATIC
34 /* use malloc hooks. Currently the code cannot be reliable if no hooks */
35 #define CONFIG_TCC_MALLOC_HOOKS
37 #define HAVE_MEMALIGN
39 #ifdef __FreeBSD__
40 #warning Bound checking not fully supported on FreeBSD
41 #undef CONFIG_TCC_MALLOC_HOOKS
42 #undef HAVE_MEMALIGN
43 #endif
45 #define BOUND_T1_BITS 13
46 #define BOUND_T2_BITS 11
47 #define BOUND_T3_BITS (32 - BOUND_T1_BITS - BOUND_T2_BITS)
49 #define BOUND_T1_SIZE (1 << BOUND_T1_BITS)
50 #define BOUND_T2_SIZE (1 << BOUND_T2_BITS)
51 #define BOUND_T3_SIZE (1 << BOUND_T3_BITS)
52 #define BOUND_E_BITS 4
54 #define BOUND_T23_BITS (BOUND_T2_BITS + BOUND_T3_BITS)
55 #define BOUND_T23_SIZE (1 << BOUND_T23_BITS)
58 /* this pointer is generated when bound check is incorrect */
59 #define INVALID_POINTER ((void *)(-2))
60 /* size of an empty region */
61 #define EMPTY_SIZE 0xffffffff
62 /* size of an invalid region */
63 #define INVALID_SIZE 0
65 typedef struct BoundEntry {
66 unsigned long start;
67 unsigned long size;
68 struct BoundEntry *next;
69 unsigned long is_invalid; /* true if pointers outside region are invalid */
70 } BoundEntry;
72 /* external interface */
73 void __bound_init(void);
74 void __bound_new_region(void *p, unsigned long size);
75 int __bound_delete_region(void *p);
77 /* currently, tcc cannot compile that because we use unsupported GNU C
78 extensions */
79 #if !defined(__TINYC__)
80 void *__bound_ptr_add(void *p, int offset) __attribute__((regparm(2)));
81 void *__bound_ptr_indir1(void *p, int offset) __attribute__((regparm(2)));
82 void *__bound_ptr_indir2(void *p, int offset) __attribute__((regparm(2)));
83 void *__bound_ptr_indir4(void *p, int offset) __attribute__((regparm(2)));
84 void *__bound_ptr_indir8(void *p, int offset) __attribute__((regparm(2)));
85 void *__bound_ptr_indir12(void *p, int offset) __attribute__((regparm(2)));
86 void *__bound_ptr_indir16(void *p, int offset) __attribute__((regparm(2)));
87 void __bound_local_new(void *p) __attribute__((regparm(1)));
88 void __bound_local_delete(void *p) __attribute__((regparm(1)));
89 #endif
91 void *__bound_malloc(size_t size, const void *caller);
92 void *__bound_memalign(size_t size, size_t align, const void *caller);
93 void __bound_free(void *ptr, const void *caller);
94 void *__bound_realloc(void *ptr, size_t size, const void *caller);
95 static void *libc_malloc(size_t size);
96 static void libc_free(void *ptr);
97 static void install_malloc_hooks(void);
98 static void restore_malloc_hooks(void);
100 #ifdef CONFIG_TCC_MALLOC_HOOKS
101 static void *saved_malloc_hook;
102 static void *saved_free_hook;
103 static void *saved_realloc_hook;
104 static void *saved_memalign_hook;
105 #endif
107 /* linker definitions */
108 extern char _end;
110 /* TCC definitions */
111 extern char __bounds_start; /* start of static bounds table */
112 /* error message, just for TCC */
113 const char *__bound_error_msg;
115 /* runtime error output */
116 extern void rt_error(unsigned long pc, const char *fmt, ...);
118 #ifdef BOUND_STATIC
119 static BoundEntry *__bound_t1[BOUND_T1_SIZE]; /* page table */
120 #else
121 static BoundEntry **__bound_t1; /* page table */
122 #endif
123 static BoundEntry *__bound_empty_t2; /* empty page, for unused pages */
124 static BoundEntry *__bound_invalid_t2; /* invalid page, for invalid pointers */
126 static BoundEntry *__bound_find_region(BoundEntry *e1, void *p)
128 unsigned long addr, tmp;
129 BoundEntry *e;
131 e = e1;
132 while (e != NULL) {
133 addr = (unsigned long)p;
134 addr -= e->start;
135 if (addr <= e->size) {
136 /* put region at the head */
137 tmp = e1->start;
138 e1->start = e->start;
139 e->start = tmp;
140 tmp = e1->size;
141 e1->size = e->size;
142 e->size = tmp;
143 return e1;
145 e = e->next;
147 /* no entry found: return empty entry or invalid entry */
148 if (e1->is_invalid)
149 return __bound_invalid_t2;
150 else
151 return __bound_empty_t2;
154 /* print a bound error message */
155 static void bound_error(const char *fmt, ...)
157 __bound_error_msg = fmt;
158 *(int *)0 = 0; /* force a runtime error */
161 static void bound_alloc_error(void)
163 bound_error("not enough memory for bound checking code");
166 /* currently, tcc cannot compile that because we use GNUC extensions */
167 #if !defined(__TINYC__)
169 /* return '(p + offset)' for pointer arithmetic (a pointer can reach
170 the end of a region in this case */
171 void *__bound_ptr_add(void *p, int offset)
173 unsigned long addr = (unsigned long)p;
174 BoundEntry *e;
175 #if defined(BOUND_DEBUG)
176 printf("add: 0x%x %d\n", (int)p, offset);
177 #endif
179 e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)];
180 e = (BoundEntry *)((char *)e +
181 ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) &
182 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS)));
183 addr -= e->start;
184 if (addr > e->size) {
185 e = __bound_find_region(e, p);
186 addr = (unsigned long)p - e->start;
188 addr += offset;
189 if (addr > e->size)
190 return INVALID_POINTER; /* return an invalid pointer */
191 return p + offset;
194 /* return '(p + offset)' for pointer indirection (the resulting must
195 be strictly inside the region */
196 #define BOUND_PTR_INDIR(dsize) \
197 void *__bound_ptr_indir ## dsize (void *p, int offset) \
199 unsigned long addr = (unsigned long)p; \
200 BoundEntry *e; \
202 e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; \
203 e = (BoundEntry *)((char *)e + \
204 ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & \
205 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); \
206 addr -= e->start; \
207 if (addr > e->size) { \
208 e = __bound_find_region(e, p); \
209 addr = (unsigned long)p - e->start; \
211 addr += offset + dsize; \
212 if (addr > e->size) \
213 return INVALID_POINTER; /* return an invalid pointer */ \
214 return p + offset; \
217 #ifdef __i386__
218 /* return the frame pointer of the caller */
219 #define GET_CALLER_FP(fp)\
221 unsigned long *fp1;\
222 __asm__ __volatile__ ("movl %%ebp,%0" :"=g" (fp1));\
223 fp = fp1[0];\
225 #else
226 #error put code to extract the calling frame pointer
227 #endif
229 /* called when entering a function to add all the local regions */
230 void __bound_local_new(void *p1)
232 unsigned long addr, size, fp, *p = p1;
233 GET_CALLER_FP(fp);
234 for(;;) {
235 addr = p[0];
236 if (addr == 0)
237 break;
238 addr += fp;
239 size = p[1];
240 p += 2;
241 __bound_new_region((void *)addr, size);
245 /* called when leaving a function to delete all the local regions */
246 void __bound_local_delete(void *p1)
248 unsigned long addr, fp, *p = p1;
249 GET_CALLER_FP(fp);
250 for(;;) {
251 addr = p[0];
252 if (addr == 0)
253 break;
254 addr += fp;
255 p += 2;
256 __bound_delete_region((void *)addr);
260 #else
262 void __bound_local_new(void *p)
265 void __bound_local_delete(void *p)
269 void *__bound_ptr_add(void *p, int offset)
271 return p + offset;
274 #define BOUND_PTR_INDIR(dsize) \
275 void *__bound_ptr_indir ## dsize (void *p, int offset) \
277 return p + offset; \
279 #endif
281 BOUND_PTR_INDIR(1)
282 BOUND_PTR_INDIR(2)
283 BOUND_PTR_INDIR(4)
284 BOUND_PTR_INDIR(8)
285 BOUND_PTR_INDIR(12)
286 BOUND_PTR_INDIR(16)
288 static BoundEntry *__bound_new_page(void)
290 BoundEntry *page;
291 int i;
293 page = libc_malloc(sizeof(BoundEntry) * BOUND_T2_SIZE);
294 if (!page)
295 bound_alloc_error();
296 for(i=0;i<BOUND_T2_SIZE;i++) {
297 /* put empty entries */
298 page[i].start = 0;
299 page[i].size = EMPTY_SIZE;
300 page[i].next = NULL;
301 page[i].is_invalid = 0;
303 return page;
306 /* currently we use malloc(). Should use bound_new_page() */
307 static BoundEntry *bound_new_entry(void)
309 BoundEntry *e;
310 e = libc_malloc(sizeof(BoundEntry));
311 return e;
314 static void bound_free_entry(BoundEntry *e)
316 libc_free(e);
319 static inline BoundEntry *get_page(int index)
321 BoundEntry *page;
322 page = __bound_t1[index];
323 if (page == __bound_empty_t2 || page == __bound_invalid_t2) {
324 /* create a new page if necessary */
325 page = __bound_new_page();
326 __bound_t1[index] = page;
328 return page;
331 /* mark a region as being invalid (can only be used during init) */
332 static void mark_invalid(unsigned long addr, unsigned long size)
334 unsigned long start, end;
335 BoundEntry *page;
336 int t1_start, t1_end, i, j, t2_start, t2_end;
338 start = addr;
339 end = addr + size;
341 t2_start = (start + BOUND_T3_SIZE - 1) >> BOUND_T3_BITS;
342 if (end != 0)
343 t2_end = end >> BOUND_T3_BITS;
344 else
345 t2_end = 1 << (BOUND_T1_BITS + BOUND_T2_BITS);
347 #if 0
348 printf("mark_invalid: start = %x %x\n", t2_start, t2_end);
349 #endif
351 /* first we handle full pages */
352 t1_start = (t2_start + BOUND_T2_SIZE - 1) >> BOUND_T2_BITS;
353 t1_end = t2_end >> BOUND_T2_BITS;
355 i = t2_start & (BOUND_T2_SIZE - 1);
356 j = t2_end & (BOUND_T2_SIZE - 1);
358 if (t1_start == t1_end) {
359 page = get_page(t2_start >> BOUND_T2_BITS);
360 for(; i < j; i++) {
361 page[i].size = INVALID_SIZE;
362 page[i].is_invalid = 1;
364 } else {
365 if (i > 0) {
366 page = get_page(t2_start >> BOUND_T2_BITS);
367 for(; i < BOUND_T2_SIZE; i++) {
368 page[i].size = INVALID_SIZE;
369 page[i].is_invalid = 1;
372 for(i = t1_start; i < t1_end; i++) {
373 __bound_t1[i] = __bound_invalid_t2;
375 if (j != 0) {
376 page = get_page(t1_end);
377 for(i = 0; i < j; i++) {
378 page[i].size = INVALID_SIZE;
379 page[i].is_invalid = 1;
385 void __bound_init(void)
387 int i;
388 BoundEntry *page;
389 unsigned long start, size;
390 int *p;
392 /* save malloc hooks and install bound check hooks */
393 install_malloc_hooks();
395 #ifndef BOUND_STATIC
396 __bound_t1 = libc_malloc(BOUND_T1_SIZE * sizeof(BoundEntry *));
397 if (!__bound_t1)
398 bound_alloc_error();
399 #endif
400 __bound_empty_t2 = __bound_new_page();
401 for(i=0;i<BOUND_T1_SIZE;i++) {
402 __bound_t1[i] = __bound_empty_t2;
405 page = __bound_new_page();
406 for(i=0;i<BOUND_T2_SIZE;i++) {
407 /* put invalid entries */
408 page[i].start = 0;
409 page[i].size = INVALID_SIZE;
410 page[i].next = NULL;
411 page[i].is_invalid = 1;
413 __bound_invalid_t2 = page;
415 /* invalid pointer zone */
416 start = (unsigned long)INVALID_POINTER & ~(BOUND_T23_SIZE - 1);
417 size = BOUND_T23_SIZE;
418 mark_invalid(start, size);
420 #if !defined(__TINYC__) && defined(CONFIG_TCC_MALLOC_HOOKS)
421 /* malloc zone is also marked invalid. can only use that with
422 hooks because all libs should use the same malloc. The solution
423 would be to build a new malloc for tcc. */
424 start = (unsigned long)&_end;
425 size = 128 * 0x100000;
426 mark_invalid(start, size);
427 #endif
429 /* add all static bound check values */
430 p = (int *)&__bounds_start;
431 while (p[0] != 0) {
432 __bound_new_region((void *)p[0], p[1]);
433 p += 2;
437 static inline void add_region(BoundEntry *e,
438 unsigned long start, unsigned long size)
440 BoundEntry *e1;
441 if (e->start == 0) {
442 /* no region : add it */
443 e->start = start;
444 e->size = size;
445 } else {
446 /* already regions in the list: add it at the head */
447 e1 = bound_new_entry();
448 e1->start = e->start;
449 e1->size = e->size;
450 e1->next = e->next;
451 e->start = start;
452 e->size = size;
453 e->next = e1;
457 /* create a new region. It should not already exist in the region list */
458 void __bound_new_region(void *p, unsigned long size)
460 unsigned long start, end;
461 BoundEntry *page, *e, *e2;
462 int t1_start, t1_end, i, t2_start, t2_end;
464 start = (unsigned long)p;
465 end = start + size;
466 t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS);
467 t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS);
469 /* start */
470 page = get_page(t1_start);
471 t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) &
472 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
473 t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) &
474 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
475 #ifdef BOUND_DEBUG
476 printf("new %lx %lx %x %x %x %x\n",
477 start, end, t1_start, t1_end, t2_start, t2_end);
478 #endif
480 e = (BoundEntry *)((char *)page + t2_start);
481 add_region(e, start, size);
483 if (t1_end == t1_start) {
484 /* same ending page */
485 e2 = (BoundEntry *)((char *)page + t2_end);
486 if (e2 > e) {
487 e++;
488 for(;e<e2;e++) {
489 e->start = start;
490 e->size = size;
492 add_region(e, start, size);
494 } else {
495 /* mark until end of page */
496 e2 = page + BOUND_T2_SIZE;
497 e++;
498 for(;e<e2;e++) {
499 e->start = start;
500 e->size = size;
502 /* mark intermediate pages, if any */
503 for(i=t1_start+1;i<t1_end;i++) {
504 page = get_page(i);
505 e2 = page + BOUND_T2_SIZE;
506 for(e=page;e<e2;e++) {
507 e->start = start;
508 e->size = size;
511 /* last page */
512 page = get_page(t1_end);
513 e2 = (BoundEntry *)((char *)page + t2_end);
514 for(e=page;e<e2;e++) {
515 e->start = start;
516 e->size = size;
518 add_region(e, start, size);
522 /* delete a region */
523 static inline void delete_region(BoundEntry *e,
524 void *p, unsigned long empty_size)
526 unsigned long addr;
527 BoundEntry *e1;
529 addr = (unsigned long)p;
530 addr -= e->start;
531 if (addr <= e->size) {
532 /* region found is first one */
533 e1 = e->next;
534 if (e1 == NULL) {
535 /* no more region: mark it empty */
536 e->start = 0;
537 e->size = empty_size;
538 } else {
539 /* copy next region in head */
540 e->start = e1->start;
541 e->size = e1->size;
542 e->next = e1->next;
543 bound_free_entry(e1);
545 } else {
546 /* find the matching region */
547 for(;;) {
548 e1 = e;
549 e = e->next;
550 /* region not found: do nothing */
551 if (e == NULL)
552 break;
553 addr = (unsigned long)p - e->start;
554 if (addr <= e->size) {
555 /* found: remove entry */
556 e1->next = e->next;
557 bound_free_entry(e);
558 break;
564 /* WARNING: 'p' must be the starting point of the region. */
565 /* return non zero if error */
566 int __bound_delete_region(void *p)
568 unsigned long start, end, addr, size, empty_size;
569 BoundEntry *page, *e, *e2;
570 int t1_start, t1_end, t2_start, t2_end, i;
572 start = (unsigned long)p;
573 t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS);
574 t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) &
575 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
577 /* find region size */
578 page = __bound_t1[t1_start];
579 e = (BoundEntry *)((char *)page + t2_start);
580 addr = start - e->start;
581 if (addr > e->size)
582 e = __bound_find_region(e, p);
583 /* test if invalid region */
584 if (e->size == EMPTY_SIZE || (unsigned long)p != e->start)
585 return -1;
586 /* compute the size we put in invalid regions */
587 if (e->is_invalid)
588 empty_size = INVALID_SIZE;
589 else
590 empty_size = EMPTY_SIZE;
591 size = e->size;
592 end = start + size;
594 /* now we can free each entry */
595 t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS);
596 t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) &
597 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
599 delete_region(e, p, empty_size);
600 if (t1_end == t1_start) {
601 /* same ending page */
602 e2 = (BoundEntry *)((char *)page + t2_end);
603 if (e2 > e) {
604 e++;
605 for(;e<e2;e++) {
606 e->start = 0;
607 e->size = empty_size;
609 delete_region(e, p, empty_size);
611 } else {
612 /* mark until end of page */
613 e2 = page + BOUND_T2_SIZE;
614 e++;
615 for(;e<e2;e++) {
616 e->start = 0;
617 e->size = empty_size;
619 /* mark intermediate pages, if any */
620 /* XXX: should free them */
621 for(i=t1_start+1;i<t1_end;i++) {
622 page = get_page(i);
623 e2 = page + BOUND_T2_SIZE;
624 for(e=page;e<e2;e++) {
625 e->start = 0;
626 e->size = empty_size;
629 /* last page */
630 page = get_page(t2_end);
631 e2 = (BoundEntry *)((char *)page + t2_end);
632 for(e=page;e<e2;e++) {
633 e->start = 0;
634 e->size = empty_size;
636 delete_region(e, p, empty_size);
638 return 0;
641 /* return the size of the region starting at p, or EMPTY_SIZE if non
642 existant region. */
643 static unsigned long get_region_size(void *p)
645 unsigned long addr = (unsigned long)p;
646 BoundEntry *e;
648 e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)];
649 e = (BoundEntry *)((char *)e +
650 ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) &
651 ((BOUND_T2_SIZE - 1) << BOUND_E_BITS)));
652 addr -= e->start;
653 if (addr > e->size)
654 e = __bound_find_region(e, p);
655 if (e->start != (unsigned long)p)
656 return EMPTY_SIZE;
657 return e->size;
660 /* patched memory functions */
662 static void install_malloc_hooks(void)
664 #ifdef CONFIG_TCC_MALLOC_HOOKS
665 saved_malloc_hook = __malloc_hook;
666 saved_free_hook = __free_hook;
667 saved_realloc_hook = __realloc_hook;
668 saved_memalign_hook = __memalign_hook;
669 __malloc_hook = __bound_malloc;
670 __free_hook = __bound_free;
671 __realloc_hook = __bound_realloc;
672 __memalign_hook = __bound_memalign;
673 #endif
676 static void restore_malloc_hooks(void)
678 #ifdef CONFIG_TCC_MALLOC_HOOKS
679 __malloc_hook = saved_malloc_hook;
680 __free_hook = saved_free_hook;
681 __realloc_hook = saved_realloc_hook;
682 __memalign_hook = saved_memalign_hook;
683 #endif
686 static void *libc_malloc(size_t size)
688 void *ptr;
689 restore_malloc_hooks();
690 ptr = malloc(size);
691 install_malloc_hooks();
692 return ptr;
695 static void libc_free(void *ptr)
697 restore_malloc_hooks();
698 free(ptr);
699 install_malloc_hooks();
702 /* XXX: we should use a malloc which ensure that it is unlikely that
703 two malloc'ed data have the same address if 'free' are made in
704 between. */
705 void *__bound_malloc(size_t size, const void *caller)
707 void *ptr;
709 /* we allocate one more byte to ensure the regions will be
710 separated by at least one byte. With the glibc malloc, it may
711 be in fact not necessary */
712 ptr = libc_malloc(size + 1);
714 if (!ptr)
715 return NULL;
716 __bound_new_region(ptr, size);
717 return ptr;
720 void *__bound_memalign(size_t size, size_t align, const void *caller)
722 void *ptr;
724 restore_malloc_hooks();
726 #ifndef HAVE_MEMALIGN
727 if (align > 4) {
728 /* XXX: handle it ? */
729 ptr = NULL;
730 } else {
731 /* we suppose that malloc aligns to at least four bytes */
732 ptr = malloc(size + 1);
734 #else
735 /* we allocate one more byte to ensure the regions will be
736 separated by at least one byte. With the glibc malloc, it may
737 be in fact not necessary */
738 ptr = memalign(size + 1, align);
739 #endif
741 install_malloc_hooks();
743 if (!ptr)
744 return NULL;
745 __bound_new_region(ptr, size);
746 return ptr;
749 void __bound_free(void *ptr, const void *caller)
751 if (ptr == NULL)
752 return;
753 if (__bound_delete_region(ptr) != 0)
754 bound_error("freeing invalid region");
756 libc_free(ptr);
759 void *__bound_realloc(void *ptr, size_t size, const void *caller)
761 void *ptr1;
762 int old_size;
764 if (size == 0) {
765 __bound_free(ptr, caller);
766 return NULL;
767 } else {
768 ptr1 = __bound_malloc(size, caller);
769 if (ptr == NULL || ptr1 == NULL)
770 return ptr1;
771 old_size = get_region_size(ptr);
772 if (old_size == EMPTY_SIZE)
773 bound_error("realloc'ing invalid pointer");
774 memcpy(ptr1, ptr, old_size);
775 __bound_free(ptr, caller);
776 return ptr1;
780 #ifndef CONFIG_TCC_MALLOC_HOOKS
781 void *__bound_calloc(size_t nmemb, size_t size)
783 void *ptr;
784 size = size * nmemb;
785 ptr = __bound_malloc(size);
786 if (!ptr)
787 return NULL;
788 memset(ptr, 0, size);
789 return ptr;
791 #endif
793 #if 0
794 static void bound_dump(void)
796 BoundEntry *page, *e;
797 int i, j;
799 printf("region dump:\n");
800 for(i=0;i<BOUND_T1_SIZE;i++) {
801 page = __bound_t1[i];
802 for(j=0;j<BOUND_T2_SIZE;j++) {
803 e = page + j;
804 /* do not print invalid or empty entries */
805 if (e->size != EMPTY_SIZE && e->start != 0) {
806 printf("%08x:",
807 (i << (BOUND_T2_BITS + BOUND_T3_BITS)) +
808 (j << BOUND_T3_BITS));
809 do {
810 printf(" %08lx:%08lx", e->start, e->start + e->size);
811 e = e->next;
812 } while (e != NULL);
813 printf("\n");
818 #endif
820 /* some useful checked functions */
822 /* check that (p ... p + size - 1) lies inside 'p' region, if any */
823 static void __bound_check(const void *p, size_t size)
825 if (size == 0)
826 return;
827 p = __bound_ptr_add((void *)p, size);
828 if (p == INVALID_POINTER)
829 bound_error("invalid pointer");
832 void *__bound_memcpy(void *dst, const void *src, size_t size)
834 __bound_check(dst, size);
835 __bound_check(src, size);
836 /* check also region overlap */
837 if (src >= dst && src < dst + size)
838 bound_error("overlapping regions in memcpy()");
839 return memcpy(dst, src, size);
842 void *__bound_memmove(void *dst, const void *src, size_t size)
844 __bound_check(dst, size);
845 __bound_check(src, size);
846 return memmove(dst, src, size);
849 void *__bound_memset(void *dst, int c, size_t size)
851 __bound_check(dst, size);
852 return memset(dst, c, size);
855 /* XXX: could be optimized */
856 int __bound_strlen(const char *s)
858 const char *p;
859 int len;
861 len = 0;
862 for(;;) {
863 p = __bound_ptr_indir1((char *)s, len);
864 if (p == INVALID_POINTER)
865 bound_error("bad pointer in strlen()");
866 if (*p == '\0')
867 break;
868 len++;
870 return len;
873 char *__bound_strcpy(char *dst, const char *src)
875 int len;
876 len = __bound_strlen(src);
877 return __bound_memcpy(dst, src, len + 1);