0,0 → 1,867 |
/* |
* Tiny C Memory and bounds checker |
* |
* Copyright (c) 2002 Fabrice Bellard |
* |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation; either version 2 of the License, or |
* (at your option) any later version. |
* |
* This program is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
* GNU General Public License for more details. |
* |
* You should have received a copy of the GNU General Public License |
* along with this program; if not, write to the Free Software |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
*/ |
#include <stdlib.h> |
#include <stdio.h> |
#include <stdarg.h> |
#include <string.h> |
#ifndef __FreeBSD__ |
#include <malloc.h> |
#endif |
|
//#define BOUND_DEBUG |
|
/* define so that bound array is static (faster, but use memory if |
bound checking not used) */ |
//#define BOUND_STATIC |
|
/* use malloc hooks. Currently the code cannot be reliable if no hooks */ |
#define CONFIG_TCC_MALLOC_HOOKS |
|
#define HAVE_MEMALIGN |
|
#if defined(__FreeBSD__) || defined(__dietlibc__) |
#warning Bound checking not fully supported on FreeBSD |
#undef CONFIG_TCC_MALLOC_HOOKS |
#undef HAVE_MEMALIGN |
#endif |
|
#define BOUND_T1_BITS 13 |
#define BOUND_T2_BITS 11 |
#define BOUND_T3_BITS (32 - BOUND_T1_BITS - BOUND_T2_BITS) |
|
#define BOUND_T1_SIZE (1 << BOUND_T1_BITS) |
#define BOUND_T2_SIZE (1 << BOUND_T2_BITS) |
#define BOUND_T3_SIZE (1 << BOUND_T3_BITS) |
#define BOUND_E_BITS 4 |
|
#define BOUND_T23_BITS (BOUND_T2_BITS + BOUND_T3_BITS) |
#define BOUND_T23_SIZE (1 << BOUND_T23_BITS) |
|
|
/* this pointer is generated when bound check is incorrect */ |
#define INVALID_POINTER ((void *)(-2)) |
/* size of an empty region */ |
#define EMPTY_SIZE 0xffffffff |
/* size of an invalid region */ |
#define INVALID_SIZE 0 |
|
typedef struct BoundEntry { |
unsigned long start; |
unsigned long size; |
struct BoundEntry *next; |
unsigned long is_invalid; /* true if pointers outside region are invalid */ |
} BoundEntry; |
|
/* external interface */ |
void __bound_init(void); |
void __bound_new_region(void *p, unsigned long size); |
int __bound_delete_region(void *p); |
|
#define FASTCALL __attribute__((regparm(3))) |
|
void *__bound_malloc(size_t size, const void *caller); |
void *__bound_memalign(size_t size, size_t align, const void *caller); |
void __bound_free(void *ptr, const void *caller); |
void *__bound_realloc(void *ptr, size_t size, const void *caller); |
static void *libc_malloc(size_t size); |
static void libc_free(void *ptr); |
static void install_malloc_hooks(void); |
static void restore_malloc_hooks(void); |
|
#ifdef CONFIG_TCC_MALLOC_HOOKS |
static void *saved_malloc_hook; |
static void *saved_free_hook; |
static void *saved_realloc_hook; |
static void *saved_memalign_hook; |
#endif |
|
/* linker definitions */ |
extern char _end; |
|
/* TCC definitions */ |
extern char __bounds_start; /* start of static bounds table */ |
/* error message, just for TCC */ |
const char *__bound_error_msg; |
|
/* runtime error output */ |
extern void rt_error(unsigned long pc, const char *fmt, ...); |
|
#ifdef BOUND_STATIC |
static BoundEntry *__bound_t1[BOUND_T1_SIZE]; /* page table */ |
#else |
static BoundEntry **__bound_t1; /* page table */ |
#endif |
static BoundEntry *__bound_empty_t2; /* empty page, for unused pages */ |
static BoundEntry *__bound_invalid_t2; /* invalid page, for invalid pointers */ |
|
static BoundEntry *__bound_find_region(BoundEntry *e1, void *p) |
{ |
unsigned long addr, tmp; |
BoundEntry *e; |
|
e = e1; |
while (e != NULL) { |
addr = (unsigned long)p; |
addr -= e->start; |
if (addr <= e->size) { |
/* put region at the head */ |
tmp = e1->start; |
e1->start = e->start; |
e->start = tmp; |
tmp = e1->size; |
e1->size = e->size; |
e->size = tmp; |
return e1; |
} |
e = e->next; |
} |
/* no entry found: return empty entry or invalid entry */ |
if (e1->is_invalid) |
return __bound_invalid_t2; |
else |
return __bound_empty_t2; |
} |
|
/* print a bound error message */ |
static void bound_error(const char *fmt, ...) |
{ |
__bound_error_msg = fmt; |
*(int *)0 = 0; /* force a runtime error */ |
} |
|
static void bound_alloc_error(void) |
{ |
bound_error("not enough memory for bound checking code"); |
} |
|
/* currently, tcc cannot compile that because we use GNUC extensions */ |
#if !defined(__TINYC__) |
|
/* return '(p + offset)' for pointer arithmetic (a pointer can reach |
the end of a region in this case */ |
void * FASTCALL __bound_ptr_add(void *p, int offset) |
{ |
unsigned long addr = (unsigned long)p; |
BoundEntry *e; |
#if defined(BOUND_DEBUG) |
printf("add: 0x%x %d\n", (int)p, offset); |
#endif |
|
e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; |
e = (BoundEntry *)((char *)e + |
((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); |
addr -= e->start; |
if (addr > e->size) { |
e = __bound_find_region(e, p); |
addr = (unsigned long)p - e->start; |
} |
addr += offset; |
if (addr > e->size) |
return INVALID_POINTER; /* return an invalid pointer */ |
return p + offset; |
} |
|
/* return '(p + offset)' for pointer indirection (the resulting must |
be strictly inside the region */ |
#define BOUND_PTR_INDIR(dsize) \ |
void * FASTCALL __bound_ptr_indir ## dsize (void *p, int offset) \ |
{ \ |
unsigned long addr = (unsigned long)p; \ |
BoundEntry *e; \ |
\ |
e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; \ |
e = (BoundEntry *)((char *)e + \ |
((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & \ |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); \ |
addr -= e->start; \ |
if (addr > e->size) { \ |
e = __bound_find_region(e, p); \ |
addr = (unsigned long)p - e->start; \ |
} \ |
addr += offset + dsize; \ |
if (addr > e->size) \ |
return INVALID_POINTER; /* return an invalid pointer */ \ |
return p + offset; \ |
} |
|
#ifdef __i386__ |
/* return the frame pointer of the caller */ |
#define GET_CALLER_FP(fp)\ |
{\ |
unsigned long *fp1;\ |
__asm__ __volatile__ ("movl %%ebp,%0" :"=g" (fp1));\ |
fp = fp1[0];\ |
} |
#else |
#error put code to extract the calling frame pointer |
#endif |
|
/* called when entering a function to add all the local regions */ |
void FASTCALL __bound_local_new(void *p1) |
{ |
unsigned long addr, size, fp, *p = p1; |
GET_CALLER_FP(fp); |
for(;;) { |
addr = p[0]; |
if (addr == 0) |
break; |
addr += fp; |
size = p[1]; |
p += 2; |
__bound_new_region((void *)addr, size); |
} |
} |
|
/* called when leaving a function to delete all the local regions */ |
void FASTCALL __bound_local_delete(void *p1) |
{ |
unsigned long addr, fp, *p = p1; |
GET_CALLER_FP(fp); |
for(;;) { |
addr = p[0]; |
if (addr == 0) |
break; |
addr += fp; |
p += 2; |
__bound_delete_region((void *)addr); |
} |
} |
|
#else |
|
void __bound_local_new(void *p) |
{ |
} |
void __bound_local_delete(void *p) |
{ |
} |
|
void *__bound_ptr_add(void *p, int offset) |
{ |
return p + offset; |
} |
|
#define BOUND_PTR_INDIR(dsize) \ |
void *__bound_ptr_indir ## dsize (void *p, int offset) \ |
{ \ |
return p + offset; \ |
} |
#endif |
|
BOUND_PTR_INDIR(1) |
BOUND_PTR_INDIR(2) |
BOUND_PTR_INDIR(4) |
BOUND_PTR_INDIR(8) |
BOUND_PTR_INDIR(12) |
BOUND_PTR_INDIR(16) |
|
static BoundEntry *__bound_new_page(void) |
{ |
BoundEntry *page; |
int i; |
|
page = libc_malloc(sizeof(BoundEntry) * BOUND_T2_SIZE); |
if (!page) |
bound_alloc_error(); |
for(i=0;i<BOUND_T2_SIZE;i++) { |
/* put empty entries */ |
page[i].start = 0; |
page[i].size = EMPTY_SIZE; |
page[i].next = NULL; |
page[i].is_invalid = 0; |
} |
return page; |
} |
|
/* currently we use malloc(). Should use bound_new_page() */ |
static BoundEntry *bound_new_entry(void) |
{ |
BoundEntry *e; |
e = libc_malloc(sizeof(BoundEntry)); |
return e; |
} |
|
static void bound_free_entry(BoundEntry *e) |
{ |
libc_free(e); |
} |
|
static inline BoundEntry *get_page(int index) |
{ |
BoundEntry *page; |
page = __bound_t1[index]; |
if (page == __bound_empty_t2 || page == __bound_invalid_t2) { |
/* create a new page if necessary */ |
page = __bound_new_page(); |
__bound_t1[index] = page; |
} |
return page; |
} |
|
/* mark a region as being invalid (can only be used during init) */ |
static void mark_invalid(unsigned long addr, unsigned long size) |
{ |
unsigned long start, end; |
BoundEntry *page; |
int t1_start, t1_end, i, j, t2_start, t2_end; |
|
start = addr; |
end = addr + size; |
|
t2_start = (start + BOUND_T3_SIZE - 1) >> BOUND_T3_BITS; |
if (end != 0) |
t2_end = end >> BOUND_T3_BITS; |
else |
t2_end = 1 << (BOUND_T1_BITS + BOUND_T2_BITS); |
|
#if 0 |
printf("mark_invalid: start = %x %x\n", t2_start, t2_end); |
#endif |
|
/* first we handle full pages */ |
t1_start = (t2_start + BOUND_T2_SIZE - 1) >> BOUND_T2_BITS; |
t1_end = t2_end >> BOUND_T2_BITS; |
|
i = t2_start & (BOUND_T2_SIZE - 1); |
j = t2_end & (BOUND_T2_SIZE - 1); |
|
if (t1_start == t1_end) { |
page = get_page(t2_start >> BOUND_T2_BITS); |
for(; i < j; i++) { |
page[i].size = INVALID_SIZE; |
page[i].is_invalid = 1; |
} |
} else { |
if (i > 0) { |
page = get_page(t2_start >> BOUND_T2_BITS); |
for(; i < BOUND_T2_SIZE; i++) { |
page[i].size = INVALID_SIZE; |
page[i].is_invalid = 1; |
} |
} |
for(i = t1_start; i < t1_end; i++) { |
__bound_t1[i] = __bound_invalid_t2; |
} |
if (j != 0) { |
page = get_page(t1_end); |
for(i = 0; i < j; i++) { |
page[i].size = INVALID_SIZE; |
page[i].is_invalid = 1; |
} |
} |
} |
} |
|
void __bound_init(void) |
{ |
int i; |
BoundEntry *page; |
unsigned long start, size; |
int *p; |
|
/* save malloc hooks and install bound check hooks */ |
install_malloc_hooks(); |
|
#ifndef BOUND_STATIC |
__bound_t1 = libc_malloc(BOUND_T1_SIZE * sizeof(BoundEntry *)); |
if (!__bound_t1) |
bound_alloc_error(); |
#endif |
__bound_empty_t2 = __bound_new_page(); |
for(i=0;i<BOUND_T1_SIZE;i++) { |
__bound_t1[i] = __bound_empty_t2; |
} |
|
page = __bound_new_page(); |
for(i=0;i<BOUND_T2_SIZE;i++) { |
/* put invalid entries */ |
page[i].start = 0; |
page[i].size = INVALID_SIZE; |
page[i].next = NULL; |
page[i].is_invalid = 1; |
} |
__bound_invalid_t2 = page; |
|
/* invalid pointer zone */ |
start = (unsigned long)INVALID_POINTER & ~(BOUND_T23_SIZE - 1); |
size = BOUND_T23_SIZE; |
mark_invalid(start, size); |
|
#if !defined(__TINYC__) && defined(CONFIG_TCC_MALLOC_HOOKS) |
/* malloc zone is also marked invalid. can only use that with |
hooks because all libs should use the same malloc. The solution |
would be to build a new malloc for tcc. */ |
start = (unsigned long)&_end; |
size = 128 * 0x100000; |
mark_invalid(start, size); |
#endif |
|
/* add all static bound check values */ |
p = (int *)&__bounds_start; |
while (p[0] != 0) { |
__bound_new_region((void *)p[0], p[1]); |
p += 2; |
} |
} |
|
static inline void add_region(BoundEntry *e, |
unsigned long start, unsigned long size) |
{ |
BoundEntry *e1; |
if (e->start == 0) { |
/* no region : add it */ |
e->start = start; |
e->size = size; |
} else { |
/* already regions in the list: add it at the head */ |
e1 = bound_new_entry(); |
e1->start = e->start; |
e1->size = e->size; |
e1->next = e->next; |
e->start = start; |
e->size = size; |
e->next = e1; |
} |
} |
|
/* create a new region. It should not already exist in the region list */ |
void __bound_new_region(void *p, unsigned long size) |
{ |
unsigned long start, end; |
BoundEntry *page, *e, *e2; |
int t1_start, t1_end, i, t2_start, t2_end; |
|
start = (unsigned long)p; |
end = start + size; |
t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS); |
t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS); |
|
/* start */ |
page = get_page(t1_start); |
t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) & |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS); |
t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) & |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS); |
#ifdef BOUND_DEBUG |
printf("new %lx %lx %x %x %x %x\n", |
start, end, t1_start, t1_end, t2_start, t2_end); |
#endif |
|
e = (BoundEntry *)((char *)page + t2_start); |
add_region(e, start, size); |
|
if (t1_end == t1_start) { |
/* same ending page */ |
e2 = (BoundEntry *)((char *)page + t2_end); |
if (e2 > e) { |
e++; |
for(;e<e2;e++) { |
e->start = start; |
e->size = size; |
} |
add_region(e, start, size); |
} |
} else { |
/* mark until end of page */ |
e2 = page + BOUND_T2_SIZE; |
e++; |
for(;e<e2;e++) { |
e->start = start; |
e->size = size; |
} |
/* mark intermediate pages, if any */ |
for(i=t1_start+1;i<t1_end;i++) { |
page = get_page(i); |
e2 = page + BOUND_T2_SIZE; |
for(e=page;e<e2;e++) { |
e->start = start; |
e->size = size; |
} |
} |
/* last page */ |
page = get_page(t1_end); |
e2 = (BoundEntry *)((char *)page + t2_end); |
for(e=page;e<e2;e++) { |
e->start = start; |
e->size = size; |
} |
add_region(e, start, size); |
} |
} |
|
/* delete a region */ |
static inline void delete_region(BoundEntry *e, |
void *p, unsigned long empty_size) |
{ |
unsigned long addr; |
BoundEntry *e1; |
|
addr = (unsigned long)p; |
addr -= e->start; |
if (addr <= e->size) { |
/* region found is first one */ |
e1 = e->next; |
if (e1 == NULL) { |
/* no more region: mark it empty */ |
e->start = 0; |
e->size = empty_size; |
} else { |
/* copy next region in head */ |
e->start = e1->start; |
e->size = e1->size; |
e->next = e1->next; |
bound_free_entry(e1); |
} |
} else { |
/* find the matching region */ |
for(;;) { |
e1 = e; |
e = e->next; |
/* region not found: do nothing */ |
if (e == NULL) |
break; |
addr = (unsigned long)p - e->start; |
if (addr <= e->size) { |
/* found: remove entry */ |
e1->next = e->next; |
bound_free_entry(e); |
break; |
} |
} |
} |
} |
|
/* WARNING: 'p' must be the starting point of the region. */ |
/* return non zero if error */ |
int __bound_delete_region(void *p) |
{ |
unsigned long start, end, addr, size, empty_size; |
BoundEntry *page, *e, *e2; |
int t1_start, t1_end, t2_start, t2_end, i; |
|
start = (unsigned long)p; |
t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS); |
t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) & |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS); |
|
/* find region size */ |
page = __bound_t1[t1_start]; |
e = (BoundEntry *)((char *)page + t2_start); |
addr = start - e->start; |
if (addr > e->size) |
e = __bound_find_region(e, p); |
/* test if invalid region */ |
if (e->size == EMPTY_SIZE || (unsigned long)p != e->start) |
return -1; |
/* compute the size we put in invalid regions */ |
if (e->is_invalid) |
empty_size = INVALID_SIZE; |
else |
empty_size = EMPTY_SIZE; |
size = e->size; |
end = start + size; |
|
/* now we can free each entry */ |
t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS); |
t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) & |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS); |
|
delete_region(e, p, empty_size); |
if (t1_end == t1_start) { |
/* same ending page */ |
e2 = (BoundEntry *)((char *)page + t2_end); |
if (e2 > e) { |
e++; |
for(;e<e2;e++) { |
e->start = 0; |
e->size = empty_size; |
} |
delete_region(e, p, empty_size); |
} |
} else { |
/* mark until end of page */ |
e2 = page + BOUND_T2_SIZE; |
e++; |
for(;e<e2;e++) { |
e->start = 0; |
e->size = empty_size; |
} |
/* mark intermediate pages, if any */ |
/* XXX: should free them */ |
for(i=t1_start+1;i<t1_end;i++) { |
page = get_page(i); |
e2 = page + BOUND_T2_SIZE; |
for(e=page;e<e2;e++) { |
e->start = 0; |
e->size = empty_size; |
} |
} |
/* last page */ |
page = get_page(t2_end); |
e2 = (BoundEntry *)((char *)page + t2_end); |
for(e=page;e<e2;e++) { |
e->start = 0; |
e->size = empty_size; |
} |
delete_region(e, p, empty_size); |
} |
return 0; |
} |
|
/* return the size of the region starting at p, or EMPTY_SIZE if non |
existant region. */ |
static unsigned long get_region_size(void *p) |
{ |
unsigned long addr = (unsigned long)p; |
BoundEntry *e; |
|
e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; |
e = (BoundEntry *)((char *)e + |
((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); |
addr -= e->start; |
if (addr > e->size) |
e = __bound_find_region(e, p); |
if (e->start != (unsigned long)p) |
return EMPTY_SIZE; |
return e->size; |
} |
|
/* patched memory functions */ |
|
static void install_malloc_hooks(void) |
{ |
#ifdef CONFIG_TCC_MALLOC_HOOKS |
saved_malloc_hook = __malloc_hook; |
saved_free_hook = __free_hook; |
saved_realloc_hook = __realloc_hook; |
saved_memalign_hook = __memalign_hook; |
__malloc_hook = __bound_malloc; |
__free_hook = __bound_free; |
__realloc_hook = __bound_realloc; |
__memalign_hook = __bound_memalign; |
#endif |
} |
|
static void restore_malloc_hooks(void) |
{ |
#ifdef CONFIG_TCC_MALLOC_HOOKS |
__malloc_hook = saved_malloc_hook; |
__free_hook = saved_free_hook; |
__realloc_hook = saved_realloc_hook; |
__memalign_hook = saved_memalign_hook; |
#endif |
} |
|
static void *libc_malloc(size_t size) |
{ |
void *ptr; |
restore_malloc_hooks(); |
ptr = malloc(size); |
install_malloc_hooks(); |
return ptr; |
} |
|
static void libc_free(void *ptr) |
{ |
restore_malloc_hooks(); |
free(ptr); |
install_malloc_hooks(); |
} |
|
/* XXX: we should use a malloc which ensure that it is unlikely that |
two malloc'ed data have the same address if 'free' are made in |
between. */ |
void *__bound_malloc(size_t size, const void *caller) |
{ |
void *ptr; |
|
/* we allocate one more byte to ensure the regions will be |
separated by at least one byte. With the glibc malloc, it may |
be in fact not necessary */ |
ptr = libc_malloc(size + 1); |
|
if (!ptr) |
return NULL; |
__bound_new_region(ptr, size); |
return ptr; |
} |
|
void *__bound_memalign(size_t size, size_t align, const void *caller) |
{ |
void *ptr; |
|
restore_malloc_hooks(); |
|
#ifndef HAVE_MEMALIGN |
if (align > 4) { |
/* XXX: handle it ? */ |
ptr = NULL; |
} else { |
/* we suppose that malloc aligns to at least four bytes */ |
ptr = malloc(size + 1); |
} |
#else |
/* we allocate one more byte to ensure the regions will be |
separated by at least one byte. With the glibc malloc, it may |
be in fact not necessary */ |
ptr = memalign(size + 1, align); |
#endif |
|
install_malloc_hooks(); |
|
if (!ptr) |
return NULL; |
__bound_new_region(ptr, size); |
return ptr; |
} |
|
void __bound_free(void *ptr, const void *caller) |
{ |
if (ptr == NULL) |
return; |
if (__bound_delete_region(ptr) != 0) |
bound_error("freeing invalid region"); |
|
libc_free(ptr); |
} |
|
void *__bound_realloc(void *ptr, size_t size, const void *caller) |
{ |
void *ptr1; |
int old_size; |
|
if (size == 0) { |
__bound_free(ptr, caller); |
return NULL; |
} else { |
ptr1 = __bound_malloc(size, caller); |
if (ptr == NULL || ptr1 == NULL) |
return ptr1; |
old_size = get_region_size(ptr); |
if (old_size == EMPTY_SIZE) |
bound_error("realloc'ing invalid pointer"); |
memcpy(ptr1, ptr, old_size); |
__bound_free(ptr, caller); |
return ptr1; |
} |
} |
|
#ifndef CONFIG_TCC_MALLOC_HOOKS |
void *__bound_calloc(size_t nmemb, size_t size) |
{ |
void *ptr; |
size = size * nmemb; |
ptr = __bound_malloc(size, NULL); |
if (!ptr) |
return NULL; |
memset(ptr, 0, size); |
return ptr; |
} |
#endif |
|
#if 0 |
static void bound_dump(void) |
{ |
BoundEntry *page, *e; |
int i, j; |
|
printf("region dump:\n"); |
for(i=0;i<BOUND_T1_SIZE;i++) { |
page = __bound_t1[i]; |
for(j=0;j<BOUND_T2_SIZE;j++) { |
e = page + j; |
/* do not print invalid or empty entries */ |
if (e->size != EMPTY_SIZE && e->start != 0) { |
printf("%08x:", |
(i << (BOUND_T2_BITS + BOUND_T3_BITS)) + |
(j << BOUND_T3_BITS)); |
do { |
printf(" %08lx:%08lx", e->start, e->start + e->size); |
e = e->next; |
} while (e != NULL); |
printf("\n"); |
} |
} |
} |
} |
#endif |
|
/* some useful checked functions */ |
|
/* check that (p ... p + size - 1) lies inside 'p' region, if any */ |
static void __bound_check(const void *p, size_t size) |
{ |
if (size == 0) |
return; |
p = __bound_ptr_add((void *)p, size); |
if (p == INVALID_POINTER) |
bound_error("invalid pointer"); |
} |
|
void *__bound_memcpy(void *dst, const void *src, size_t size) |
{ |
__bound_check(dst, size); |
__bound_check(src, size); |
/* check also region overlap */ |
if (src >= dst && src < dst + size) |
bound_error("overlapping regions in memcpy()"); |
return memcpy(dst, src, size); |
} |
|
void *__bound_memmove(void *dst, const void *src, size_t size) |
{ |
__bound_check(dst, size); |
__bound_check(src, size); |
return memmove(dst, src, size); |
} |
|
void *__bound_memset(void *dst, int c, size_t size) |
{ |
__bound_check(dst, size); |
return memset(dst, c, size); |
} |
|
/* XXX: could be optimized */ |
int __bound_strlen(const char *s) |
{ |
const char *p; |
int len; |
|
len = 0; |
for(;;) { |
p = __bound_ptr_indir1((char *)s, len); |
if (p == INVALID_POINTER) |
bound_error("bad pointer in strlen()"); |
if (*p == '\0') |
break; |
len++; |
} |
return len; |
} |
|
char *__bound_strcpy(char *dst, const char *src) |
{ |
int len; |
len = __bound_strlen(src); |
return __bound_memcpy(dst, src, len + 1); |
} |
|