DF-5712 / diffharness.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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | /* * DF-5712 โ differential + guard-page harness for sys/libkern/index.c and * sys/libkern/rindex.c (DragonFlyBSD kernel strchr/strrchr backend). * * The kernel sources are compiled VERBATIM (only the top-level includes are * stripped by build.sh because they are kernel headers; the function bodies * are byte-identical) and renamed at the preprocessor level: * index -> k_index (sys/libkern/index.c) * rindex -> k_rindex (sys/libkern/rindex.c) * f_index/f_rindex are the same bodies with the one-character fix * if (*u.p == ch) -> if (*u.p == (char)ch) * * Part 1: differential k_index vs libc strchr, k_rindex vs libc strrchr, * ch = -300 .. +300 over a corpus incl. high-bit bytes. * Part 2: NUL-tail semantics (ch == 0) vs libc. * Part 3: guard-page proof โ string ends flush against a PROT_NONE page; * every scan must succeed without touching the guard page (the * primitives are byte-at-a-time and halt exactly at the NUL). * Plus the documented contract demo: buffer with NO NUL abutting * the guard page faults (consumer contract, not a primitive bug). * Part 4: same differential against the patched copies โ must be 0/0. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <signal.h> #include <setjmp.h> #include <unistd.h> #include <sys/mman.h> char *k_index(const char *, int); char *k_rindex(const char *, int); char *f_index(const char *, int); char *f_rindex(const char *, int); static sigjmp_buf jmp; static void segv(int sig) { (void)sig; siglongjmp(jmp, 1); } static int off(const char *base, const char *r) { return r ? (int)(r - base) : -1; } static const char *corpus[] = { "", "a", "abc", "a/b/c", "\x80" "abc", "abc\x80", "a\x80" "b\x80" "c", /* 0x80 = -128 signed char */ "\xe9" "abc", "abc\xe9", "\xff\xff", "x\xffy", /* high-bit extremes */ NULL }; static int diff_run(char *(*ki)(const char *, int), char *(*kr)(const char *, int), const char *tag, int print) { int mism_i = 0, mism_r = 0; for (int si = 0; corpus[si]; si++) { const char *s = corpus[si]; for (int ch = -300; ch <= 300; ch++) { int a = off(s, ki(s, ch)); int b = off(s, strchr(s, ch)); if (a != b) { mism_i++; if (print && mism_i <= 6) printf(" MISMATCH %s str[%d]=\"%s\" ch=%4d " "kernel=%d libc=%d\n", tag, si, s, ch, a, b); } a = off(s, kr(s, ch)); b = off(s, strrchr(s, ch)); if (a != b) { mism_r++; if (print && mism_r <= 6) printf(" MISMATCH %s str[%d]=\"%s\" ch=%4d " "kernel=%d libc=%d\n", tag, si, s, ch, a, b); } } } printf("DIFF %s: index %5d mismatches, rindex %5d mismatches\n", tag, mism_i, mism_r); return mism_i + mism_r; } int main(void) { long ps = sysconf(_SC_PAGESIZE); int total; setvbuf(stdout, NULL, _IONBF, 0); /* ---------------- Part 1: baseline differential ---------------- */ printf("== Part 1: kernel index/rindex vs libc strchr/strrchr ==\n"); total = diff_run(k_index, k_rindex, "baseline", 1); printf("BASELINE_TOTAL_MISMATCHES=%d\n", total); /* ---------------- Part 2: ch == 0 tail semantics ---------------- */ printf("== Part 2: ch==0 semantics (strchr/strrchr NUL tail) ==\n"); { int bad = 0; for (int si = 0; corpus[si]; si++) { const char *s = corpus[si]; size_t n = strlen(s); if (k_index(s, 0) != s + n) bad++; if (k_rindex(s, 0) != s + n) bad++; if (s + n != strchr(s, 0) || s + n != strrchr(s, 0)) bad++; } printf("CH0_SEMANTICS: %s (%d bad)\n", bad ? "WRONG" : "OK โ match libc exactly", bad); } /* ---------------- Part 3: guard page ---------------- */ printf("== Part 3: guard-page (pagesize=%ld) ==\n", ps); { char *base = mmap(NULL, (size_t)ps * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); char *guard; int fault = 0; if (base == MAP_FAILED) { perror("mmap"); return 2; } guard = base + ps; if (mprotect(guard, (size_t)ps, PROT_NONE) != 0) { perror("mprotect"); return 2; } signal(SIGSEGV, segv); signal(SIGBUS, segv); /* string whose NUL is the LAST byte before the guard page */ { const char *src = "abc\x80\xe9\xff/zz"; size_t n = strlen(src); char *s = guard - n - 1; /* room for NUL at guard-1 */ memcpy(s, src, n); s[n] = '\0'; if (sigsetjmp(jmp, 1) == 0) { char *r = k_index(s, '/'); printf("guard k_index '/' -> off %d %s", off(s, r), r ? "" : "(NULL)"); printf(" [no fault]\n"); } else { fault++; printf("guard k_index '/' -> FAULT\n"); } if (sigsetjmp(jmp, 1) == 0) { char *r = k_rindex(s, 'z'); printf("guard k_rindex 'z' -> off %d %s", off(s, r), r ? "" : "(NULL)"); printf(" [no fault]\n"); } else { fault++; printf("guard k_rindex 'z' -> FAULT\n"); } if (sigsetjmp(jmp, 1) == 0) { char *r = k_index(s, 0); /* must return guard-1 */ printf("guard k_index 0 -> off %d (want %zu) " "[no fault]\n", off(s, r), n); if (r != guard - 1) { printf(" WRONG POINTER\n"); fault++; } } else { fault++; printf("guard k_index 0 -> FAULT\n"); } if (sigsetjmp(jmp, 1) == 0) { char *r = k_rindex(s, 0); printf("guard k_rindex 0 -> off %d (want %zu) " "[no fault]\n", off(s, r), n); if (r != guard - 1) { printf(" WRONG POINTER\n"); fault++; } } else { fault++; printf("guard k_rindex 0 -> FAULT\n"); } if (sigsetjmp(jmp, 1) == 0) { char *r = k_index(s, 'Q'); /* absent char */ printf("guard k_index 'Q' -> %s [no fault]\n", r ? "HIT?!" : "NULL"); } else { fault++; printf("guard k_index 'Q' -> FAULT\n"); } } /* contract demo: NO NUL in the page at all โ scan must stop at * the guard page; this documents the NUL-termination contract * (a consumer bug would be needed to reach this in-kernel). */ memset(base, 'A', (size_t)ps); if (sigsetjmp(jmp, 1) == 0) { k_index(base, '/'); printf("guard NONUL k_index -> NO FAULT (unexpected)\n"); fault += 100; } else { printf("guard NONUL k_index -> FAULT at guard page " "(contract demo, expected)\n"); } printf("GUARDPAGE_RESULT: %s (%d unexpected faults)\n", fault ? "OVERREAD/OOPS" : "byte-exact, halts at NUL", fault); } signal(SIGSEGV, SIG_DFL); signal(SIGBUS, SIG_DFL); /* ---------------- Part 4: patched copies ---------------- */ printf("== Part 4: patched (*u.p == (char)ch) differential ==\n"); total = diff_run(f_index, f_rindex, "patched", 1); printf("PATCHED_TOTAL_MISMATCHES=%d\n", total); printf("HARNESS_DONE\n"); return 0; } |