⬢ DragonFlyBSD Kernel Audit
DF-2319 / rig_shims.h
← back to finding ↓ download raw
/*
 * rig_shims.h -- KERNEL-ENVIRONMENT EMULATION for the verbatim pccard CIS rig.
 *
 *   *** NOT VERBATIM *** — everything the machine-extracted production parser
 *   (pccard_cis_core.inc, sed'ed byte-identical from the guest's
 *   /usr/src/sys/bus/pccard/pccard_cis.c; see extract.sh) needs from the
 *   kernel environment, emulated in userland with guard/canary buffers and
 *   full instrumentation.
 *
 * Emulation fidelity notes:
 *   - bus_alloc_resource(..., PCCARD_CIS_SIZE, ...) -> a struct resource whose
 *     handle points at a 64 KiB guard buffer; the actual kernel mapping is
 *     PCCARD_CIS_SIZE (4096) bytes (pccard_cis.c:62,130-131). Bytes in
 *     [0,4096) hold the crafted CIS image; [4096,65536) are a 0xA5 canary
 *     zone standing in for whatever the bridge maps adjacent to the CIS
 *     window; offsets >= 65536 are recorded but never dereferenced (the
 *     userland analogue of a CardBus master abort on a wild bus read).
 *   - CARD_SET_RES_FLAGS(attr/common) swaps the active image (the rig_*
 *     implementation of it lives at the bottom of rig_main.c, AFTER the
 *     verbatim PCCARD_A_MEM_* enum is visible).
 *   - kmalloc/kfree are instrumented: every block is canary-banded and enters
 *     a ledger, so leaks (DF-5361) and per-iteration growth (DF-5362) are
 *     counted exactly.
 */
#ifndef RIG_SHIMS_H
#define RIG_SHIMS_H

#include <sys/types.h>
#include <sys/queue.h>	/* byte-identical to kernel sys/sys/queue.h (hash-checked) */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>

/* ---------------- kernel types ---------------- */
struct resource {
	unsigned char *handle;	/* active image base == bus_space_handle_t */
	size_t         size;	/* PCCARD_CIS_SIZE */
};
typedef struct resource *bus_space_tag_t;
typedef unsigned char   *bus_space_handle_t;
typedef unsigned long    bus_addr_t;
typedef unsigned long    bus_size_t;
typedef void            *device_t;
typedef void             driver_intr_t(void);

/* ---------------- debug knob (owned here; verbatim code externs it) ----- */
int pccard_cis_debug = 0;

#define	kprintf	printf
#define	device_printf(d, ...)	printf("pccard0: " __VA_ARGS__)

static inline void
rig_panic(const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	printf("RIG KERNEL PANIC: ");
	vprintf(fmt, ap);
	printf("\n");
	va_end(ap);
	abort();
}
#define	panic	rig_panic
#ifndef	howmany
#define	howmany(x, y)	(((x)+((y)-1))/(y))
#endif
#define	bzero(p, n)	memset((p), 0, (n))

/* ---------------- allocation ledger (DF-5361 / DF-5362 evidence) --------- */
#define	RIG_MAX_BLOCKS	65536
#define	RIG_CANARY	0xC3
#define	M_DEVBUF	0
#define	M_NOWAIT	1
#define	M_ZERO		2

struct rig_block {
	void       *p;
	size_t      size;	/* requested size (kernel view) */
	int         live;
	const char *site;	/* caller __func__ */
};
static struct rig_block rig_blocks[RIG_MAX_BLOCKS];
static int   rig_nblocks;
static long  rig_alloc_fail;
static size_t rig_sz_cfe, rig_sz_pf;	/* filled by main after types known */

static void *
rig_kmalloc(size_t sz, int flags, const char *site)
{
	unsigned char *p, *q;
	size_t tot = sz + 16;
	size_t i;

	if (rig_nblocks >= RIG_MAX_BLOCKS) { rig_alloc_fail++; return NULL; }
	p = malloc(tot);
	if (p == NULL) { rig_alloc_fail++; return NULL; }
	for (i = 0; i < 8; i++) p[i] = RIG_CANARY;
	q = p + 8 + sz;
	for (i = 0; i < 8; i++) q[i] = RIG_CANARY;
	if (flags & M_ZERO)
		memset(p + 8, 0, sz);
	rig_blocks[rig_nblocks].p = p + 8;
	rig_blocks[rig_nblocks].size = sz;
	rig_blocks[rig_nblocks].live = 1;
	rig_blocks[rig_nblocks].site = site;
	rig_nblocks++;
	return p + 8;
}
#define	kmalloc(sz, type, flags)	rig_kmalloc((sz), (flags), __func__)

static void
rig_kfree(void *p, const char *site)
{
	int i;
	unsigned char *raw;
	size_t k, sz;

	if (p == NULL)
		return;
	for (i = rig_nblocks - 1; i >= 0; i--)
		if (rig_blocks[i].p == p)
			break;
	if (i < 0) {
		printf("RIG kfree(%p) of UNREGISTERED block (%s)\n", p, site);
		abort();
	}
	if (!rig_blocks[i].live) {
		printf("RIG DOUBLE FREE of %p (%s)\n", p, site);
		abort();
	}
	sz = rig_blocks[i].size;
	raw = (unsigned char *)p - 8;
	for (k = 0; k < 8; k++)
		if (raw[k] != RIG_CANARY || raw[sz + 8 + k] != RIG_CANARY) {
			printf("RIG HEAP SMASH on block %p (%zu bytes, %s)\n",
			    p, sz, rig_blocks[i].site);
			abort();
		}
	rig_blocks[i].live = 0;
	free(raw);
}
#define	kfree(p, type)	rig_kfree((p), __func__)

/* ---------------- bus space / rman emulation ---------------- */
#define	SYS_RES_MEMORY	0
#define	RF_ACTIVE	0x01
#define	rman_make_alignment_flags(x)	0

#define	RIG_MAPSIZE	4096	/* == PCCARD_CIS_SIZE, the real kernel mapping */
#define	RIG_TOTBUF	65536	/* guard buffer: [4096,65536) = canary zone */

static unsigned char rig_attr_img[RIG_TOTBUF];
static unsigned char rig_com_img[RIG_TOTBUF];
static struct resource rig_res;
static int  rig_space;		/* current window: 0=attribute 1=common */
static long rig_flag_switches;

/* OOB accounting */
static long          rig_reads;		/* total bus_space_read_1 calls */
static long          rig_oob;		/* reads at offset >= RIG_MAPSIZE */
static long          rig_oob_wild;	/* reads >= RIG_TOTBUF (never deref'd) */
static unsigned long rig_oob_first, rig_oob_max;
static long          rig_cap = -1;	/* >=0: stop scan after this many reads */
static int           rig_cap_hit;
static sigjmp_buf    rig_jmp;
static int           rig_jmp_ok;

/* ring of recent read offsets for cycle detection (DF-5362) */
#define	RIG_RING	8192
static unsigned long rig_ring[RIG_RING];
static int rig_ring_n;

static void
rig_reset_counters(void)
{
	rig_reads = rig_oob = rig_oob_wild = rig_flag_switches = 0;
	rig_oob_first = rig_oob_max = 0;
	rig_cap_hit = 0;
	rig_ring_n = 0;
}

static inline uint8_t
bus_space_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t off)
{
	uint8_t v;

	rig_reads++;
	if (rig_cap >= 0 && rig_reads > rig_cap && rig_jmp_ok) {
		rig_cap_hit = 1;
		siglongjmp(rig_jmp, 1);
		/* NOTREACHED */
	}
	if (rig_ring_n < RIG_RING)
		rig_ring[rig_ring_n++] = (unsigned long)off;
	if (off >= RIG_TOTBUF) {			/* wild: no dereference */
		rig_oob++; rig_oob_wild++;
		if (rig_oob == 1) rig_oob_first = off;
		if (off > rig_oob_max) rig_oob_max = off;
		return 0xFF;				/* master-abort read */
	}
	v = h[off];					/* real fetch */
	if (off >= RIG_MAPSIZE) {
		rig_oob++;
		if (rig_oob == 1) rig_oob_first = off;
		if (off > rig_oob_max) rig_oob_max = off;
	}
	return v;
}

static device_t rig_parent_dev = (device_t)&rig_parent_dev;
static device_t	device_get_parent(device_t d) { (void)d; return rig_parent_dev; }

static struct resource *
bus_alloc_resource(device_t dev, int type, int *rid, bus_addr_t start,
    bus_addr_t end, bus_size_t size, u_int flags)
{
	(void)dev; (void)type; (void)rid; (void)start; (void)end; (void)flags;
	rig_res.size = size;
	rig_res.handle = rig_attr_img;
	rig_space = 0;
	return &rig_res;
}
static int
bus_release_resource(device_t d, int t, int r, struct resource *res)
{ (void)d; (void)t; (void)r; (void)res; return 0; }

static bus_space_tag_t	rman_get_bustag(struct resource *r) { (void)r; return r; }
static bus_space_handle_t	rman_get_bushandle(struct resource *r) { return r->handle; }
static unsigned long	rman_get_start(struct resource *r) { (void)r; return 0xD0000; }

/*
 * CARD_SET_RES_FLAGS: proto+macro here (used by the verbatim scan code);
 * the definition lives at the bottom of rig_main.c so the verbatim
 * PCCARD_A_MEM_COM / PCCARD_A_MEM_ATTR enum (pccardvar_rig.inc, from
 * pccardvar.h:357-363) is visible.
 */
static int rig_CARD_SET_RES_FLAGS(device_t, device_t, int, int, int);
#define	CARD_SET_RES_FLAGS(p, d, t, r, f) \
	rig_CARD_SET_RES_FLAGS((p), (d), (t), (r), (f))

#endif /* RIG_SHIMS_H */