DF-0677 / slc_oob.c
/* * 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); |