⬢ DragonFlyBSD Kernel Audit
DF-3932 / kmallocsink_replica.c
← back to finding ↓ download raw
/*
 * kmallocsink.c - replica of the mly MLYIO_COMMAND size-handling sink.
 *
 * mly_user_command (sys/dev/raid/mly/mly.c:2889-2891):
 *   mc->mc_length = (uc->DataTransferLength >= 0) ?
 *       uc->DataTransferLength : -uc->DataTransferLength;
 *   ...
 *   mc->mc_data = kmalloc(mc->mc_length, M_DEVBUF, M_INTWAIT);
 *
 * DataTransferLength == INT_MIN (mlyio.h:42: int) negates to itself
 * (int wrap) -> sign-extends into size_t as 0xFFFFFFFF80000000.
 * mc_length > 0 is TRUE at that magnitude, so kmalloc(M_INTWAIT) is
 * entered with ~16EB:
 *   kern_slaballoc.c:942  size >= ZoneLimit -> kmem_slab_alloc(...)
 *   kern_slaballoc.c:1721 vm_map_findspace(kernel_map, ...) fails
 *   kern_slaballoc.c:1724 panic("kmem_slab_alloc(): kernel_map ran
 *                                     out of space!")
 *
 * This module reproduces exactly that call. Trigger on real hardware:
 *   ioctl(open("/dev/mly0", O_RDWR), MLYIO_COMMAND, &uc) with
 *   uc.DataTransferLength = INT_MIN  (root-only device, 0600 root:operator).
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/systm.h>

static int
fire_sysctl(SYSCTL_HANDLER_ARGS)
{
	int error, v = 0;
	size_t sz;
	void *p;

	error = sysctl_handle_int(oidp, &v, 0, req);
	if (error || req->newptr == NULL)
		return (error);
	if (v != 1)
		return (0);

	static volatile int dtl = -2147483647 - 1;	/* INT_MIN */

	sz = (size_t)(-dtl);				/* mly.c:2889 negation */
	kprintf("kmallocsink: kmalloc(0x%016jx, M_DEVBUF, M_INTWAIT) ...\n",
	    (uintmax_t)sz);
	p = kmalloc(sz, M_DEVBUF, M_INTWAIT);
	kprintf("kmallocsink: returned %p (NO panic)\n", p);
	if (p != NULL)
		kfree(p, M_DEVBUF);
	return (0);
}
SYSCTL_PROC(_kern, OID_AUTO, kmallocsink, CTLTYPE_INT | CTLFLAG_RW, 0, 0,
    fire_sysctl, "I", "mly MLYIO_COMMAND DataTransferLength=INT_MIN sink");

static int
kmallocsink_load(struct module *m, int what, void *arg)
{
	switch (what) {
	case MOD_LOAD:
		kprintf("kmallocsink: loaded\n");
		return (0);
	case MOD_UNLOAD:
		return (0);
	}
	return (0);
}

static moduledata_t kmallocsink_mod = { "kmallocsink", kmallocsink_load, NULL };
DECLARE_MODULE(kmallocsink, kmallocsink_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);