DF-5700 / guard_harness.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 123 124 125 126 127 | /* * Guard-page containment rig for sys/libkern/bcmp.c (compiled verbatim as * df_bcmp). Maps each buffer inside a single RW page with PROT_NONE guard * pages on BOTH sides, then sweeps lengths/offsets/placements/content. * * Property under test (bcmp.c:53-56,85,89,91,110-111,115,128-129): * every longword load is naturally aligned (8 | page size) and starts at * an address holding >= 1 in-bounds byte => the overread (up to 7 bytes * below start / past end) NEVER leaves the buffer's first/last valid page. * * Any violation => SIGSEGV => harness dies with the case recorded on disk. * Clean exit = containment empirically confirmed over the whole sweep. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <sys/mman.h> #include <unistd.h> int df_bcmp(const void *, const void *, size_t); #define PS 4096 static unsigned char *P1, *P2; /* usable pages, guarded both sides */ static FILE *journal; static char jline[256]; static void jwrite(void) { fclose(journal); fprintf(stderr, "DIED IN: %s", jline); } static uint64_t s_state = 0x243f6a8885a308d3ULL; static uint64_t prng(void) { s_state ^= s_state << 13; s_state ^= s_state >> 7; s_state ^= s_state << 17; return s_state; } static void one(int s1, int s2, size_t n, int mode) { snprintf(jline, sizeof(jline), "s1=%d s2=%d n=%zu mode=%d\n", s1, s2, n, mode); for (int i = 0; i < PS; i++) { unsigned char v; switch (mode) { case 0: v = (unsigned char)prng(); break; case 1: v = 0; break; case 2: v = (unsigned char)(i * 31); break; default: v = (unsigned char)(i & 1 ? 0xaa : 0x55); break; } P1[i] = v; P2[i] = v; /* keep both pages identical content */ } /* mismatch variations that exercise the last byte / early exit paths */ if (n && (mode & 1)) P1[s1 + n - 1] ^= 1; if (n && !(mode & 1)) P2[s2 + n - 1] ^= 1; volatile int r = df_bcmp(P1 + s1, P2 + s2, n); (void)r; } int main(void) { /* two independent guarded arenas: [guard][usable][guard] */ unsigned char *a1 = mmap(NULL, 3 * PS, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); unsigned char *a2 = mmap(NULL, 3 * PS, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (a1 == MAP_FAILED || a2 == MAP_FAILED) { perror("mmap"); return 2; } P1 = a1 + PS; P2 = a2 + PS; if (mprotect(a1, PS, PROT_NONE) || mprotect(a1 + 2 * PS, PS, PROT_NONE) || mprotect(a2, PS, PROT_NONE) || mprotect(a2 + 2 * PS, PS, PROT_NONE)) { perror("mprotect"); return 2; } journal = fopen("journal.txt", "w"); if (!journal) { perror("fopen"); return 2; } setvbuf(journal, NULL, _IOLBF, 0); long cases = 0; /* exhaustive small: n 1..96, every start offset in the low/high 16 bytes * of the page (page-corner coverage), 4 content modes */ for (size_t n = 1; n <= 96; n++) { for (int s = 0; s < 16 && s + n <= PS; s++) { int s2m = PS - n - (s % 16); if (s2m < 0) s2m = 0; for (int mode = 0; mode < 4; mode++) { fprintf(journal, "s1=%d s2=%d n=%zu mode=%d\n", s, s2m, n, mode); one(s, s2m, n, mode); cases++; fprintf(journal, "s1=%d s2=%d n=%zu mode=%d\n", s2m, s, n, mode); one(s2m, s, n, mode); cases++; } } } printf("phase1 small sweep: %ld cases OK\n", cases); /* page-scale: lengths around PS with buffers ending exactly at the page * boundary and starting exactly at the page start (the two corners where * an uncontained overread would hit a guard page) */ long c2 = 0; size_t lens[] = { PS - 8, PS - 7, PS - 3, PS - 2, PS - 1, PS, PS + 1, PS + 2, PS + 7, PS + 8, 2 * PS, 4096 + 123, 8192 }; for (size_t i = 0; i < sizeof(lens) / sizeof(lens[0]); i++) { size_t n = lens[i]; if (n > PS) continue; /* single usable page per buffer */ int starts[] = { 0, 1, 7, 8, (int)(PS - n), (int)(PS - n > 0 ? PS - n : 0) }; for (unsigned a = 0; a < sizeof(starts) / sizeof(starts[0]); a++) { int s = starts[a]; if (s < 0 || s + n > PS) continue; for (int mode = 0; mode < 4; mode++) { fprintf(journal, "s1=%d s2=%d n=%zu mode=%d\n", s, PS - n, n, mode); one(s, (int)(PS - n), n, mode); c2++; fprintf(journal, "s1=%d s2=%d n=%zu mode=%d\n", (int)(PS - n), s, n, mode); one((int)(PS - n), s, n, mode); c2++; } } } printf("phase2 page-corner sweep: %ld cases OK\n", c2); /* randomized placement across the whole page */ for (long it = 0; it < 500000; it++) { size_t n = (size_t)(prng() % PS) + 1; int s1 = (int)(prng() % (PS - n + 1)); int s2 = (int)(prng() % (PS - n + 1)); fprintf(journal, "s1=%d s2=%d n=%zu mode=r\n", s1, s2, n); one(s1, s2, n, (int)(prng() % 4)); } printf("phase3 randomized: 500000 cases OK\n"); printf("RESULT: PASS — no load escaped the buffer's valid pages " "(%ld + %ld + 500000 cases)\n", cases, c2); fclose(journal); return 0; } |