blob: 1da22e7a22228e72e34e9c779a2db557e99f9bca [file] [log] [blame]
/* GNU C dependencies:
Checker support hooks
ISO C 9x array element initialization
void-pointer arithmetic */
#include "driver.h"
int verbose = 0;
int debug = 0;
int bad_accesses = 0;
const char *const memory_use_strings[] =
{
#define INIT(x) [x] = #x
INIT (MEMORY_USE_BAD),
INIT (MEMORY_USE_DONT),
INIT (MEMORY_USE_RO),
INIT (MEMORY_USE_RW),
INIT (MEMORY_USE_TW),
INIT (MEMORY_USE_WO),
#undef INIT
};
/* This won't be used for any really huge test cases, so a simple
linked list is adequate. We won't even worry about overlapping
regions; the matching entry that comes up first wins. */
const char *const access_mode_strings[] =
{
"none", "ro", "wo", "rw",
};
struct access_node
{
struct access_node *next;
const void *addr;
size_t sz;
enum access_mode mode;
};
static struct access_node *access_list;
void
mark_region (const void *addr, size_t sz, enum access_mode mode)
{
struct access_node *a;
if (debug)
printf ("mark_region (%p, %ld, %s)\n", addr, (long) sz,
access_mode_strings[mode]);
a = malloc (sizeof (struct access_node));
a->next = access_list;
a->addr = addr;
a->sz = sz;
a->mode = mode;
access_list = a;
}
void report_bad_access (void *, size_t, enum memory_use_mode) NOCHECK;
void
report_bad_access (void *addr, size_t sz, enum memory_use_mode mode)
{
if (++bad_accesses > 100)
bad_accesses = 100;
if (verbose)
{
static char x[100];
const char *mode_str;
if (mode >= 0
&& mode < sizeof (memory_use_strings) / sizeof (*memory_use_strings)
&& memory_use_strings[mode] != 0)
mode_str = memory_use_strings[mode];
else
{
sprintf (x, "<bad mode %d>", mode);
mode_str = x;
}
printf ("bad access (%p, %ld, %s)\n", addr, (long) sz, mode_str);
}
}
int verify1 (void *, size_t, enum access_mode, struct access_node *) NOCHECK;
int
verify1 (void *addr, size_t sz, enum access_mode mode,
struct access_node *a)
{
while (a && (addr + sz <= a->addr || addr >= a->addr + a->sz))
a = a->next;
if (a == 0)
return 0;
if (debug)
printf ("verify1 (%p, %ld, %s)\n", addr, (long) sz,
access_mode_strings[mode]);
if (mode & ~a->mode)
return 0;
if (addr < a->addr)
if (verify1 (a, a->addr - addr, mode, a->next) == 0)
return 0;
if (addr + sz > a->addr + a->sz)
if (verify1 (a->addr + a->sz, (addr + sz) - (a->addr + a->sz), mode, a->next) == 0)
return 0;
/* All regions okay. */
return 1;
}
int verify_range_permission (void *, size_t, enum access_mode) NOCHECK;
int
verify_range_permission (void *addr, size_t sz, enum access_mode mode)
{
if (debug)
printf ("verify_range_permission (%p, %ld, %s)\n", addr, (long) sz,
access_mode_strings[mode]);
return verify1 (addr, sz, mode, access_list);
}
void chkr_check_addr (void *, size_t, int) NOCHECK;
void
chkr_check_addr (void *addr, size_t sz, int mode)
{
switch (mode)
{
case MEMORY_USE_BAD:
case MEMORY_USE_DONT:
default:
report_bad_access (addr, sz, mode);
return;
case MEMORY_USE_RO:
/* verify range readable */
if (!verify_range_permission (addr, sz, ACCESS_RO))
report_bad_access (addr, sz, mode);
return;
case MEMORY_USE_WO:
/* verify writeable, set writeable and readable */
if (!verify_range_permission (addr, sz, ACCESS_WO))
report_bad_access (addr, sz, mode);
mark_region (addr, sz, ACCESS_RW);
return;
case MEMORY_USE_RW:
/* verify readable and writeable, no change */
if (!verify_range_permission (addr, sz, ACCESS_RW))
report_bad_access (addr, sz, mode);
return;
case MEMORY_USE_TW:
/* verify writeable, no change */
if (!verify_range_permission (addr, sz, ACCESS_WO))
report_bad_access (addr, sz, mode);
return;
}
/* All branches should return. */
abort ();
}
void copy1 (void *, void *, size_t, struct access_node *) NOCHECK;
void
copy1 (void *dest, void *src, size_t sz, struct access_node *a)
{
while (a && (src + sz <= a->addr || src >= a->addr + a->sz))
a = a->next;
if (a == 0)
{
report_bad_access (src, sz, MEMORY_USE_RO);
return;
}
if (debug)
printf ("copy1 (%p, %p, %ld)\n", dest, src, (long) sz);
{
void *start, *end;
start = src;
if (start < a->addr)
start = a->addr;
end = src + sz;
if (end > a->addr + a->sz)
end = a->addr + a->sz;
mark_region (dest + (start - src), end - start, a->mode);
}
if (src < a->addr)
copy1 (dest, src, a->addr - src, a->next);
if (src + sz > a->addr + a->sz)
copy1 (dest + (a->addr + a->sz - src), a->addr + a->sz,
(src + sz) - (a->addr + a->sz), a->next);
}
void chkr_copy_bitmap (void *, void *, size_t) NOCHECK;
void
chkr_copy_bitmap (void *dest, void *src, size_t sz)
{
if (verify_range_permission (dest, sz, MEMORY_USE_WO) == 0)
report_bad_access (dest, sz, MEMORY_USE_WO);
copy1 (dest, src, sz, access_list);
}
void chkr_set_right (void *, size_t, enum access_mode) NOCHECK;
void
chkr_set_right (void *addr, size_t sz, enum access_mode mode)
{
mark_region (addr, sz, mode);
}
int main () NOCHECK;
int
main ()
{
setup ();
test ();
bad_accesses = !!bad_accesses; /* get 0 or 1 */
if (bad_accesses == expect_error)
exit (0);
else
abort ();
return 0;
}
struct malloc_node
{
struct malloc_node *next;
void *addr;
size_t sz;
unsigned is_free : 1;
};
static struct malloc_node *malloc_list;
void *
c_malloc (size_t sz)
{
void *p;
struct malloc_node *m;
if (sz == 0)
return 0;
p = malloc (sz);
if (p == 0)
{
if (verbose)
printf ("malloc(%ld) failed\n", (long) sz);
exit (1);
}
m = malloc (sizeof (struct malloc_node));
if (m == 0)
{
if (verbose)
printf ("malloc(%ld) failed\n", (long) sizeof (struct malloc_node));
exit (1);
}
mark_region (p, sz, ACCESS_WO);
m->addr = p;
m->sz = sz;
m->is_free = 0;
m->next = malloc_list;
malloc_list = m;
return p;
}
void
c_free (void *p)
{
struct malloc_node *m;
if (p == 0)
return;
for (m = malloc_list; m; m = m->next)
if (m->addr == p)
break;
if (m == 0 || m->is_free)
/* Test is broken. */
abort ();
m->is_free = 1;
free (p);
}