⬢ DragonFlyBSD Kernel Audit
DF-3789 / harness_frames0.c
← back to finding ↓ download raw
/*
 * DF-3789 -- METEORSETGEO with frames=0 bypasses the bigbuf resize check
 *           -> capture geometry (rows/cols) stored unvalidated against the
 *              existing allocation -> OOB RISC-DMA write AND OOB read()
 *              heap disclosure. (Distinct trigger from DF-1216: no
 *              METEORSACTPIXFMT involved; the hole is the missing
 *              lower-bound check on geo->frames at bktr_core.c:1642.)
 *
 * Source-level harness of the exact arithmetic of METEORSETGEO
 * (bktr_core.c:1657-1689), start_capture (3519-3524) and video_read
 * (bktr_core.c:1084-1106).
 */
#include <stdio.h>

#define PAGE_SIZE 4096
#define PAGE_SHIFT 12
#define BROOKTREE_ALLOC_PAGES (217*4)		/* bktr_core.c:157-160 */
#define BROOKTREE_ALLOC (BROOKTREE_ALLOC_PAGES * PAGE_SIZE)
#define btoc(x) (((x) + PAGE_SIZE - 1) >> PAGE_SHIFT)

struct meteor_geomet { unsigned short rows, columns, frames; };

static void setgeo(int alloc_pages, struct meteor_geomet *geo, int Bpp,
		   unsigned long ofmt_rgb24)
{
	unsigned int temp;

	/* bktr_core.c:1657-1681 verbatim */
	if ((temp = (geo->rows * geo->columns * geo->frames * 2))) {
		if (ofmt_rgb24) temp = temp * 2;
		if (geo->frames > 1) temp += PAGE_SIZE;
		temp = btoc(temp);
		if ((int)temp > alloc_pages) {
			printf("  realloc to %u pages (buffer GROWS with it)\n",
			       temp);
			alloc_pages = (int)temp;	/* kernel does this */
		} else
			printf("  no realloc (temp=%u <= alloc_pages=%d)\n",
			       temp, alloc_pages);
	} else {
		printf("  temp == 0 -> allocation block SKIPPED ENTIRELY "
		       "(frames==0)\n");
	}
	/* bktr_core.c:1687-1689: geometry stored regardless */
	printf("  stored: rows=%u cols=%u frames=%u\n",
	       geo->rows, geo->columns, geo->frames);

	/* capture footprint (RISC builders use rows/cols/Bpp only) */
	{
		unsigned long foot = (unsigned long)geo->rows * geo->columns * Bpp;
		unsigned long alloc = (unsigned long)alloc_pages * PAGE_SIZE;
		printf("  DMA footprint = %lu B vs allocation = %lu B "
		       "-> OOB write of %lu B past bigbuf\n",
		       foot, alloc, foot > alloc ? foot - alloc : 0);
		/* video_read count = rows*cols*Bpp (bktr_core.c:1084) */
		printf("  video_read uiomove count = %lu B -> OOB read of "
		       "%lu B past bigbuf (kernel heap disclosure)\n",
		       foot, foot > alloc ? foot - alloc : 0);
	}
}

int main(void)
{
	struct meteor_geomet geo;
	int Bpp = 2;		/* default METEOR_GEO_RGB16 compat format */

	printf("default allocation: %d pages (%d bytes)\n\n",
	       BROOKTREE_ALLOC_PAGES, BROOKTREE_ALLOC);

	printf("[*] METEORSETGEO{rows=2046, columns=1022, frames=0, RGB16}\n");
	geo.rows = 2046; geo.columns = 1022; geo.frames = 0;
	setgeo(BROOKTREE_ALLOC_PAGES, &geo, Bpp, 0);

	printf("\n[*] control: frames=1 (same geometry) reallocates correctly\n");
	geo.rows = 2046; geo.columns = 1022; geo.frames = 1;
	setgeo(BROOKTREE_ALLOC_PAGES, &geo, Bpp, 0);

	return 0;
}