⬢ DragonFlyBSD Kernel Audit
DF-3747 / pun_harness.c
← back to finding ↓ download raw
/*
 * DF-3747 harness: models the sccngetch() static-layout type-pun store
 * (sys/dev/misc/syscons/syscons.c:2004-2059) and the unclamped bcopy.
 *
 * The kernel code:
 *      static struct fkeytab fkey;      // 17 bytes
 *      static int fkeycp;               // 4 bytes
 *      ...
 *      p = kbd_get_fkeystr(scp->sc->kbd, KEYCHAR(c), (size_t *)&fkeycp);
 *      fkey.len = fkeycp;
 *      if ((p != NULL) && (fkey.len > 0))
 *          bcopy(p, fkey.str, fkey.len);
 *
 * kbd_get_fkeystr -> genkbd_get_fkeystr() (sys/dev/misc/kbd/kbd.c:1123)
 * performs  *len = kbd->kb_fkeytab[fkey].len;   -- a size_t (8-byte) store.
 * On the pointer to the 4-byte static int fkeycp that store is 4 bytes OOB.
 *
 * Real kernel image layout (guest, nm -n /boot/kernel/kernel):
 *      fkeycp.13185 @ 0xffffffff817a6da0
 *      fkey.13184   @ 0xffffffff817a6db0      (next symbol is +0x10)
 * so the 8-byte store at &fkeycp spans [da0,da8): the int plus 4 bytes of
 * the inter-symbol alignment gap.
 *
 * This harness reproduces both stores verbatim against a canary-guarded
 * model of that layout and reports exactly which bytes are modified.
 */
#include <stdio.h>
#include <string.h>
#include <stddef.h>

struct fkeytab { unsigned char str[16]; unsigned char len; };

/* Layout model, mirroring the kernel image order/sizes:
 *   [guard 32][fkeycp int @32][gap 12][fkey 17 @48][guard 32]
 * (kernel: fkeycp @+0x00, gap, fkey @+0x10; guards stand in for the
 *  surrounding .bss objects)
 */
static struct {
	unsigned char guard0[32];
	int          fkeycp;
	unsigned char gap[12];
	struct fkeytab fkey;
	unsigned char guard1[32];
} M;

static void
dump_changed(const char *what, unsigned char *base, size_t off, size_t n)
{
	size_t i;
	int any = 0;

	for (i = 0; i < n; i++) {
		if (base[i] != 0xAA) {
			if (!any) {
				printf("  %s modified bytes:", what);
				any = 1;
			}
			printf(" [%zu]=%02x", off + i, base[i]);
		}
	}
	if (!any)
		printf("  %s: no bytes modified", what);
	printf("\n");
}

int
main(void)
{
	unsigned char src[256];
	size_t i;

	printf("sizeof(size_t)=%zu sizeof(int)=%zu sizeof(struct fkeytab)=%zu\n",
	       sizeof(size_t), sizeof(int), sizeof(struct fkeytab));
	printf("model: fkeycp @+%zu, fkey @+%zu, guard1 @+%zu\n",
	       offsetof(typeof(M), fkeycp), offsetof(typeof(M), fkey),
	       offsetof(typeof(M), guard1));

	/* ---- 1. the type-pun store (syscons.c:2054 via kbd.c:1123) ---- */
	memset(&M, 0xAA, sizeof(M));
	M.fkeycp = 0;			/* .bss zero-init */

	/* exactly what genkbd_get_fkeystr does through (size_t *)&fkeycp,
	 * with a legitimate SETFKEY-clamped length of 5.
	 * volatile => the 8-byte store is not optimized down to 32 bits
	 * (in the kernel the pun is cross-TU: genkbd_get_fkeystr kbd.c:1123
	 * compiles to a real 64-bit "mov %rax,(%r12)" at *len == &fkeycp) */
	*(volatile size_t *)(void *)&M.fkeycp = 5;

	printf("[1] 8-byte size_t store at &fkeycp (len=5):\n");
	printf("  fkeycp=%d (low 4 bytes: the length)\n", M.fkeycp);
	dump_changed("OOB bytes [fkeycp+4 .. fkeycp+8)",
	    (unsigned char *)&M.fkeycp + 4, 4, 4);
	printf("  => bytes 4..7 of the store land OUTSIDE the 4-byte int"
	    " (in the real kernel: the alignment gap before fkey.13184)\n");

	/* ---- 2. unclamped bcopy into fkey.str[16] with a corrupt len ----
	 * DF-1503 (kbd.c SETFKEY imin(-1,MAXFK) wrap) shows fkeytab[].len is
	 * corruptible to arbitrary u_char values; sccngetch then does
	 * fkey.len = fkeycp; bcopy(p, fkey.str, fkey.len);
	 * without any clamp to sizeof(fkey.str) == 16.
	 */
	memset(&M, 0xAA, sizeof(M));
	M.fkeycp = 0;
	for (i = 0; i < sizeof(src); i++)
		src[i] = (unsigned char)i;

	*(volatile size_t *)(void *)&M.fkeycp = 255;	/* corrupt fkeytab len */
	{
		int fkeycp = (int)M.fkeycp;	/* syscons.c:2005 type */
		unsigned char *p = src;		/* fkeytab[].str */

		M.fkey.len = (unsigned char)fkeycp;	/* syscons.c:2055 */
		if ((p != NULL) && (M.fkey.len > 0))
			memcpy(M.fkey.str, p, M.fkey.len);	/* syscons.c:2057 */
	}
	printf("[2] bcopy with corrupted len=255 into fkey.str[16]:\n");
	dump_changed("bytes after fkey.str[16] (kernel: .bss objects after"
	    " fkey.13184)", M.fkey.str + 16,
	    offsetof(typeof(M), fkey) + 16, 17 - 16 + 32);
	printf("  => %d bytes written past the end of the 17-byte static"
	    " fkey object\n", 255 - 16);

	return 0;
}