DF-5706 / memcchr_instr.c
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 113 114 115 116 117 118 119 120 121 122 | /* * Load-instrumented copy of sys/libkern/memcchr.c -- identical control flow. * * The two longword load sites of the pristine source are wrapped by LDC(): * - memcchr.c:83-85 first-word align-down load (`*lp++') * - memcchr.c:90-91 word-walk load (`*lp') * and every TESTBYTE byte load by LDB(). NOTE() records each loaded range * against the caller region [g_lo, g_hi) = [begin, begin+n): * - loads starting below begin (backward over-read) * - loads extending past end (forward over-read) * - loads escaping the enclosing word granules [align_down(begin), * align_up(end)) (containment invariant: must NEVER happen) * plus the done-filter observer: `goto done' reached with p >= end * (mismatch found only in over-read bytes; memcchr.c:114 must reject it). * * Semantics are cross-checked against a naive reference by instr_driver.c: * zero differential mismatches proves the instrumentation did not change * behavior. */ #include "prelude.h" #define LONGPTR_MASK (sizeof(long) - 1) unsigned long g_below_start, g_past_end, g_in; unsigned long g_max_below, g_max_past; unsigned long g_filtered; /* done reached with p >= end */ unsigned long g_outside_granule; /* load outside enclosing granules: must stay 0 */ const unsigned char *g_lo, *g_hi; #define NOTE(p, sz) do { \ const unsigned char *_p = (const unsigned char *)(p); \ if (_p < g_lo) { \ g_below_start++; \ { unsigned long _d = (unsigned long)(g_lo - _p); \ if (_d > g_max_below) g_max_below = _d; } \ } else if (_p + (sz) > g_hi) { \ g_past_end++; \ { unsigned long _d = (unsigned long)(_p + (sz) - g_hi); \ if (_d > g_max_past) g_max_past = _d; } \ } else g_in++; \ if (_p < (const unsigned char *)((uintptr_t)g_lo & ~(uintptr_t)7) || \ _p + (sz) > \ (const unsigned char *)(((uintptr_t)g_hi + 7) & ~(uintptr_t)7)) \ g_outside_granule++; \ } while (0) /* statement-expression loads: record, then perform the exact original load */ #define LDC(e) ({ const unsigned long *_q = (e); \ NOTE((const unsigned char *)_q, sizeof(unsigned long)); *_q; }) #define LDB(e) ({ const unsigned char *_q = (e); NOTE(_q, 1); *_q; }) static int g_c; #define TESTBYTE_I \ do { \ if (LDB(p) != (unsigned char)g_c) \ goto done; \ p++; \ } while (0) void * memcchr_instr(const void *begin, int c, size_t n) { const unsigned long *lp; const unsigned char *p, *end; unsigned long word, first; g_c = c; g_lo = (const unsigned char *)begin; g_hi = (const unsigned char *)begin + n; /* Four or eight repetitions of `c'. */ word = (unsigned char)c; word |= word << 8; word |= word << 16; #if LONG_BIT >= 64 word |= word << 32; #endif /* Don't perform memory I/O when passing a zero-length buffer. */ if (n == 0) return (NULL); lp = (const unsigned long *)((uintptr_t)begin & ~LONGPTR_MASK); end = (const unsigned char *)begin + n; first = LDC(lp); lp++; if (first != word) for (p = begin; p < (const unsigned char *)lp;) TESTBYTE_I; /* Now compare the data one word at a time. */ for (; (const unsigned char *)lp < end; lp++) { unsigned long w = LDC(lp); if (w != word) { p = (const unsigned char *)lp; TESTBYTE_I; TESTBYTE_I; TESTBYTE_I; #if LONG_BIT >= 64 TESTBYTE_I; TESTBYTE_I; TESTBYTE_I; TESTBYTE_I; #endif goto done; } } return (NULL); done: /* * If the end of the buffer is not word aligned, the previous * loops may obtain an address that's beyond the end of the * buffer. */ if (p >= end) g_filtered++; if (p < end) return (__DECONST(void *, p)); return (NULL); } |