⬢ DragonFlyBSD Kernel Audit
DF-3568 / guard_rij_badbits.c
← back to finding ↓ download raw
/*
 * guard_rij_badbits.c - prove rijndael_set_key (api wrapper, no validation)
 * passes attacker-controlled keyBits straight through and that
 * rijndaelKeySetupEnc then reads key material beyond keyBits/8 bytes
 * when keyBits is invalid (KASSERT compiled out = production kernel
 * without INVARIANTS; userland mirrors that with -DNDEBUG).
 *
 *   sys/crypto/rijndael/rijndael-api.c:38-43   (no bounds check, void return)
 *   sys/crypto/rijndael/rijndael-alg-fst.c:738 (KASSERT only)
 *   sys/crypto/rijndael/rijndael-alg-fst.c:740-785 (unconditional
 *       rk[0..7] = key[0..31] loads regardless of keyBits)
 *
 * mode "safe": 32-byte buffer, bits=64 -> prints ctx.Nr (expect 0: silent
 *              garbage schedule, no error path)
 * mode "guard": 8-byte buffer at end of a page before a PROT_NONE guard;
 *              bits=64 -> first OOB byte load at key+8 faults.
 *              Exit 42 = fault confirmed.
 *
 * Build: cc -DNDEBUG -I/usr/src/sys ... rijndael-api.c rijndael-alg-fst.c
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <crypto/rijndael/rijndael.h>

static void
handler(int sig, siginfo_t *si, void *uc)
{
	(void)uc;
	printf("FAULT-CONFIRMED: sig=%d si_addr=%p (expected key+8 on guard page)\n",
	    sig, si->si_addr);
	fflush(stdout);
	_exit(42);
}

int
main(int argc, char **argv)
{
	rijndael_ctx ctx;
	unsigned char pt[16], ct[16];
	int i;

	if (argc < 2)
		return 2;

	if (strcmp(argv[1], "safe") == 0) {
		unsigned char key32[32];
		for (i = 0; i < 32; i++)
			key32[i] = i;
		rijndael_set_key(&ctx, key32, 64);
		printf("safe mode: bits=64 accepted silently, ctx.Nr=%d "
		    "(0 = unusable schedule, no error surfaced)\n", ctx.Nr);
		memset(pt, 0xAA, sizeof(pt));
		rijndael_encrypt(&ctx, pt, ct);
		printf("safe mode: rijndael_encrypt ran with Nr=%d and produced "
		    "garbage without any failure indication\n", ctx.Nr);
		return 0;
	}

	{
		unsigned char *base, *key8;
		struct sigaction sa;

		base = mmap(NULL, 2 * 4096, PROT_READ | PROT_WRITE,
		    MAP_ANON | MAP_PRIVATE, -1, 0);
		if (base == MAP_FAILED ||
		    mprotect(base + 4096, 4096, PROT_NONE) != 0) {
			perror("mmap/mprotect");
			return 2;
		}
		key8 = base + 4096 - 8;	/* 8-byte key, guard right after */
		for (i = 0; i < 8; i++)
			key8[i] = i;

		memset(&sa, 0, sizeof(sa));
		sa.sa_sigaction = handler;
		sa.sa_flags = SA_SIGINFO;
		sigaction(SIGSEGV, &sa, NULL);
		sigaction(SIGBUS, &sa, NULL);

		printf("calling rijndael_set_key(&ctx, key8=%p, bits=64) "
		    "(guard page at %p)\n", (void *)key8, (void *)(base + 4096));
		fflush(stdout);
		rijndael_set_key(&ctx, key8, 64);
		printf("BUG-NOT-PRESENT: survived bits=64 with 8-byte key\n");
		return 0;
	}
}