/* 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, "", 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); }