DF-5678 / guardalloc.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | /* * guardalloc.h — rig-only instrumentation for the DF-5678 differential. * * Redirects the four libprop userland allocation macros (_PROP_MALLOC/ * _PROP_CALLOC/_PROP_REALLOC/_PROP_FREE) to a page-granular guarded * allocator that places each block at the END of a mapped region with an * unmapped guard page immediately after the last byte of the LAST page. * A block whose size ends exactly at a page boundary therefore faults on * the FIRST byte written past its end. * * Used to prove the out-of-bounds accesses in prop_array_set() * (prop_array.c:650 read / :654 write) and prop_array_remove() * (prop_array.c:709 read) with instruction-level precision on the same * libprop source the kernel compiles, without needing a sanitizer. * * Applied by patching ONLY the rig copy of prop_object_impl.h (see * build.sh); sys/ is never modified. */ #ifndef DF5678_GUARDALLOC_H #define DF5678_GUARDALLOC_H #include <sys/mman.h> #include <stddef.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define GA_PS 4096UL /* * layout: [ meta+data pages ... ][ PROT_NONE guard page ] * meta (2*size_t: total mapping size, usable size) at the start of the * first page; user pointer = base + GA_META rounded so that * (user + sz) may end flush with the page before the guard. */ #define GA_META 16UL static void * ga_alloc(size_t sz, int zero) { size_t mapsz; char *m; void *p; if (sz == 0) sz = 1; /* round (META + sz) up to whole pages; guard page follows */ mapsz = ((GA_META + sz + GA_PS - 1) / GA_PS) * GA_PS; m = mmap(NULL, mapsz + GA_PS, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (m == MAP_FAILED) return (NULL); if (mprotect(m + mapsz, GA_PS, PROT_NONE) == -1) { munmap(m, mapsz + GA_PS); return (NULL); } ((size_t *)m)[0] = mapsz + GA_PS; /* total (incl. guard) */ ((size_t *)m)[1] = sz; /* usable size */ p = m + GA_META; if (zero) memset(p, 0, sz); /* * NOTE: the user block runs [p, p+sz). It ends flush against the * guard only when GA_META + sz is a multiple of the page size; the * harness picks CAP so that it is (510*8 + 16 == 4096). */ return (p); } static void ga_free(void *p) { char *base; size_t total; if (p == NULL) return; base = (char *)p - GA_META; total = ((size_t *)base)[0]; munmap(base, total); } static void * ga_realloc(void *old, size_t sz) { void *p; size_t oldsz; if (old == NULL) return (ga_alloc(sz, 0)); oldsz = ((size_t *)((char *)old - GA_META))[1]; p = ga_alloc(sz, 0); if (p == NULL) return (NULL); memcpy(p, old, oldsz < sz ? oldsz : sz); ga_free(old); return (p); } static void * ga_malloc(size_t sz) { return (ga_alloc(sz, 0)); } static void * ga_calloc(size_t n, size_t sz) { return (ga_alloc(n * sz, 1)); } #endif /* DF5678_GUARDALLOC_H */ |