โฌข DragonFlyBSD Kernel Audit
DF-5712 / diffharness.c
โ† back to finding โ†“ download raw
/*
 * 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;
}