DragonFlyBSD Kernel Audit
DF-1190 / harness.c
← back to finding ↓ download raw
/*
 * DF-1190 harness — ciss_init_logical OOB index
 * (userspace replica of sys/dev/raid/ciss/ciss.c:1386-1406)
 *
 * Faithfully reproduces the kernel parsing path: a malicious/emulated HP Smart
 * Array (CISS) PCI controller returns a REPORT_LOGICAL_LUNS list whose LUN
 * address encodes bus/target values outside the ciss_logical[][] / ciss_controllers[]
 * arrays. The driver indexes those arrays WITHOUT any bounds check, so:
 *   ld = &sc->ciss_logical[bus][target];          // OOB
 *   ld->cl_controller = &sc->ciss_controllers[bus]; // OOB pointer write
 * This harness demonstrates the out-of-range index using the real kernel macros.
 *
 * Build: cc -O2 -Wall -o harness harness.c
 * Run:   ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* ---- real kernel constants/macros (sys/dev/raid/ciss/cissreg.h, cissvar.h) ---- */
#define CISS_MAX_LOGICAL	15	/* cissvar.h:49 */
#define CISS_LUN_TO_BUS(x)    (((x) >> 16) & 0xFF)	/* cissreg.h:509 */
#define CISS_LUN_TO_TARGET(x) ((x) & 0xFF)		/* cissreg.h:510 */

/* controller-supplied 32-bit logical LUN address */
typedef unsigned int ciss_lun_t;

struct ciss_softc_replica {
	int		     ciss_max_logical_bus;	/* rows of ciss_logical[][] */
	union ciss_addr { unsigned int raw; } *ciss_controllers;
	struct ldrive   **ciss_logical;
};

struct ldrive {
	unsigned long cl_address;
	void	     *cl_controller;	/* &ciss_controllers[bus] */
	int	      cl_status;
};

/* Malicious controller data: ndrives entries, each with a forged lun. */
static int run(int max_logical_bus, ciss_lun_t *luns, int ndrives)
{
	struct ciss_softc_replica sc;
	int i, oob_hits = 0;
	char *ctrl_overflow = NULL;	/* detect OOB into ciss_controllers */

	sc.ciss_max_logical_bus = max_logical_bus;
	sc.ciss_controllers = calloc(max_logical_bus, sizeof(*sc.ciss_controllers));
	sc.ciss_logical     = calloc(max_logical_bus, sizeof(struct ldrive *));
	for (i = 0; i < max_logical_bus; i++)
		sc.ciss_logical[i] = calloc(CISS_MAX_LOGICAL, sizeof(struct ldrive));

	/* --- exact replica of ciss.c:1386-1406 (the vulnerable loop) --- */
	for (i = 0; i < CISS_MAX_LOGICAL; i++) {
		if (i < ndrives) {
			int bus, target;
			bus	  = CISS_LUN_TO_BUS(luns[i]);
			target	  = CISS_LUN_TO_TARGET(luns[i]);

			/* KERNEL HAS NO BOUNDS CHECK HERE:
			 *   ld = &sc->ciss_logical[bus][target];
			 *   ld->cl_controller = &sc->ciss_controllers[bus];
			 * We instrument to report the out-of-range access. */
			if (bus >= sc.ciss_max_logical_bus || target >= CISS_MAX_LOGICAL) {
				printf("[!] OOB index drive=%d bus=%d (max %d) target=%d (max %d)"
				       " -> writes ciss_logical[%d][%d] + ciss_controllers+%d\n",
				       i, bus, sc.ciss_max_logical_bus, target, CISS_MAX_LOGICAL,
				       bus, target, bus);
				oob_hits++;
				continue;
			}
			/* in-bounds path (never taken with the crafted data) */
		}
	}
	for (i = 0; i < max_logical_bus; i++) free(sc.ciss_logical[i]);
	free(sc.ciss_logical); free(sc.ciss_controllers);
	(void)ctrl_overflow;
	return oob_hits;
}

int main(void)
{
	ciss_lun_t luns[2];
	int max_logical_bus = 1;	/* typical: one local logical bus */

	/* Scenario A: controller returns a logical LUN with bus=200, target=250.
	 * CISS_LUN_TO_BUS=200, CISS_LUN_TO_TARGET=250 -> both out of range. */
	luns[0] = (200u << 16) | 250u;
	/* Scenario B: bus in-range but target>=CISS_MAX_LOGICAL(15). */
	luns[1] = (0u   << 16) | 40u;

	printf("== DF-1190 ciss_init_logical OOB index harness ==\n");
	printf("ciss_max_logical_bus=%d, CISS_MAX_LOGICAL=%d\n\n", max_logical_bus, CISS_MAX_LOGICAL);

	int hits = run(max_logical_bus, luns, 2);
	printf("\n[BUG %s] %d out-of-range index accesses observed\n",
	       hits ? "REPRODUCED" : "not-reproduced", hits);
	printf("On a real kernel these become:\n");
	printf("  - ciss_logical[bus] : OOB pointer deref (arbitrary kmalloc target)\n");
	printf("  - ciss_controllers[bus] : OOB kernel pointer written into ld->cl_controller\n");
	printf("  - ciss_identify_logical then kmallocs into OOB ld->cl_ldrive/cl_lstatus\n");
	return hits ? 0 : 1;
}