⬢ DragonFlyBSD Kernel Audit
DF-3583 / trigger_df3583.c
← back to finding ↓ download raw
/*
 * DF-3583 trigger logic — extraction of the kernel loop in isp_intr():
 *
 *   } else if (etype == RQSTYPE_RIO1) {
 *           isp_rio1_t *rio = (isp_rio1_t *) qe;     // uint8_t qe[QENTRY_LEN]
 *           isp_get_rio1(isp, (isp_rio1_t *) hp, rio);
 *           for (i = 0; i < rio->req_header.rqs_seqno; i++) {
 *                   isp_fastpost_complete(isp, rio->req_handles[i]);  <-- OOB
 *           }
 *
 * isp_rio1_t is exactly QENTRY_LEN (64) bytes: 4-byte header +
 * uint32_t req_handles[15]. rqs_seqno is an 8-bit, card/DMA-controlled byte
 * (0..255). Nothing clamps it to 15, so a card-supplied seqno of N > 15 makes
 * the loop read (N-15)*4 bytes past the 64-byte stack buffer 'qe'.
 *
 * Harness: place qe[64] at the end of an RW page followed by PROT_NONE and
 * run the identical loop with seqno = 0xff -> faults at qe+64 (i == 16),
 * i.e. up to 960 bytes of kernel stack can be walked.
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <signal.h>
#include <unistd.h>
#include <setjmp.h>

typedef void *XS_T;
typedef uint64_t XS_DMA_ADDR_T;
typedef struct { uint64_t t; } NANOTIME_T;
typedef struct { uint16_t param[16]; } mbreg_t;
#define MAILBOX_STORAGE 16
struct isposinfo { char pad[128]; };
#define ISP_KERNEL 1
#include <dev/disk/isp/isp_stds.h>
#include <dev/disk/isp/ispmbox.h>
#include <dev/disk/isp/ispvar.h>

static sigjmp_buf jb;
static char faultbuf[64];
static void *fault_at;

static void
handler(int sig, siginfo_t *si, void *uc)
{
	snprintf(faultbuf, sizeof (faultbuf), "SIGSEGV/%s", sig == SIGBUS ? "BUS" : "SEGV");
	fault_at = si->si_addr;
	siglongjmp(jb, 1);
}

int
main(void)
{
	size_t pagesz = 4096;
	uint8_t *map, *qe;
	struct sigaction sa;
	volatile uint32_t sink = 0;
	int i;

	printf("sizeof(isp_rio1_t) = %zu (header 4 + req_handles[%zu])\n",
	    sizeof(isp_rio1_t),
	    sizeof(((isp_rio1_t *)0)->req_handles) / sizeof(uint32_t));
	printf("rqs_seqno is 8 bits => card may claim up to 255 handles\n");
	printf("max OOB stack read = (255-15)*4 = %d bytes past qe[64]\n\n", (255 - 15) * 4);

	map = mmap(NULL, 2 * pagesz, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
	if (map == MAP_FAILED) { perror("mmap"); return (1); }
	if (mprotect(map, pagesz, PROT_READ|PROT_WRITE) != 0) {
		perror("mprotect"); return (1);
	}
	/* the kernel stack frame's qe[QENTRY_LEN], flush against a guard page */
	qe = map + pagesz - QENTRY_LEN;
	memset(qe, 0, QENTRY_LEN);

	memset(&sa, 0, sizeof (sa));
	sa.sa_sigaction = handler;
	sa.sa_flags = SA_SIGINFO;
	sigaction(SIGSEGV, &sa, NULL);
	sigaction(SIGBUS, &sa, NULL);

	{
		isp_rio1_t *rio = (isp_rio1_t *) qe;
		rio->req_header.rqs_seqno = 0xff;	/* card-controlled */
		if (sigsetjmp(jb, 1) != 0) {
			printf("FAULT (%s) at %p => %zd bytes past the end of qe[64] "
			    "(loop reached i == %d, array holds 15)\n",
			    faultbuf, fault_at, (ssize_t)((uint8_t *)fault_at - (qe + QENTRY_LEN)),
			    (int)(((uint8_t *)fault_at - qe) / 4) - 1 + 1);
			printf("kernel impact: read of up to 960 bytes of kernel stack,\n"
			    "     values consumed as completion handles (isp_fastpost_complete)\n");
			return (0);
		}
		/* the kernel loop, verbatim */
		for (i = 0; i < rio->req_header.rqs_seqno; i++) {
			sink += rio->req_handles[i];
		}
	}
	printf("ERROR: no fault occurred (sink=%u)\n", sink);
	return (1);
}