DragonFlyBSD Kernel Audit
DF-0677 / slc_oob.c
← back to finding ↓ download raw
/*
 * DF-0677 harness: prove sl_compress_init(comp, max_state) performs an
 * out-of-bounds write past struct slcompress when max_state > MAX_STATES-1,
 * and that the clamp fix prevents it. Uses a guard region + sentinel so the
 * OOB (or its absence) is detected deterministically rather than depending on
 * an incidental page-fault.
 *
 * Mirrors sppp: sys/net/sppp/if_spppsubr.c:964 kmalloc(sizeof(struct slcompress),M_TEMP)
 *               sys/net/sppp/if_spppsubr.c:2976 sl_compress_init(sp->pp_comp, p[4])
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <net/slcompress.h>

#define GUARD_BYTES  (64 * 1024)	/* well past the ~32KB worst-case overflow */
#define SENTINEL     0xAA

static int max_state = 255;
TUNABLE_INT("hw.slc_oob.max_state", &max_state);

static int
slc_oob_load(module_t mod, int what, void *arg)
{
	char *buf;
	struct slcompress *comp;
	size_t over, scanned, hit;

	if (what != MOD_LOAD)
		return (0);

	kprintf("DF0677: sizeof(struct slcompress)=%zu  sizeof(struct cstate)=%zu  MAX_STATES=%d\n",
	    sizeof(struct slcompress), sizeof(struct cstate), MAX_STATES);

	/* Place the slcompress at the very start of a large guard buffer, then
	 * fill everything PAST the struct with a sentinel. Any OOB write into
	 * tstate[>=MAX_STATES] lands in the sentinel region. */
	buf = kmalloc(sizeof(struct slcompress) + GUARD_BYTES, M_TEMP, M_WAITOK | M_ZERO);
	comp = (struct slcompress *)buf;
	memset(buf + sizeof(struct slcompress), SENTINEL, GUARD_BYTES);

	kprintf("DF0677: comp=%p max_state=%d  guard=[%p..%p) sentinel=0x%02x\n",
	    comp, max_state, buf + sizeof(struct slcompress),
	    buf + sizeof(struct slcompress) + GUARD_BYTES, SENTINEL);

	sl_compress_init(comp, max_state);

	/* Scan the guard region for any byte that differs from SENTINEL.
	 * (cast to unsigned char: 0xAA is >127 so a signed-char compare lies) */
	hit = 0; scanned = 0;
	for (over = 0; over < GUARD_BYTES; over++) {
		if ((unsigned char)buf[sizeof(struct slcompress) + over] != SENTINEL) {
			scanned = over + 1;
			hit++;
		}
	}
	if (hit) {
		kprintf("DF0677: RESULT=OVERFLOW_DETECTED  %zu guard bytes clobbered, "
		    "farthest write at +%zu bytes past struct (tstate index ~%zu)\n",
		    hit, scanned, (scanned + sizeof(struct cstate) - 1) / sizeof(struct cstate) + MAX_STATES);
		/* Hexdump the first cstate-sized slot of the guard region: it should
		 * hold a kernel pointer (cs_next) at +0 and a controlled byte
		 * (cs_id) at +10, proving attacker-influenceable content. */
		{
			size_t k;
			kprintf("DF0677: guard[0..15]:");
			for (k = 0; k < 16; k++)
				kprintf(" %02x", (unsigned char)buf[sizeof(struct slcompress) + k]);
			kprintf("\n");
		}
	} else {
		kprintf("DF0677: RESULT=NO_OVERFLOW  guard region untouched (clamp held, max_state=%d)\n",
		    max_state);
	}

	kfree(buf, M_TEMP);
	return (0);
}

static moduledata_t slc_oob_mod = { "slc_oob", slc_oob_load, NULL };
DECLARE_MODULE(slc_oob, slc_oob_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
MODULE_VERSION(slc_oob, 1);