⬢ DragonFlyBSD Kernel Audit
DF-3812 / vpidx_oob_arith.c
← back to finding ↓ download raw
/*
 * DF-3812 — offset-arithmetic demonstrator (host-compiled, no kernel, no HW).
 *
 * Replicates the exact index chain of the bug to show how far out of the
 * kmalloc'd isp_param array a firmware-controlled in_vpidx lands:
 *
 *   isp_freebsd.c:2627   chan = ISP_GET_VPIDX(isp, inot->in_vpidx);
 *                        ISP_GET_VPIDX(isp, tag) = (MULTIID fw) ? tag : 0
 *                        (ispmbox.h:861-862)   -> chan is a RAW 8-bit value
 *   isp_freebsd.c:2677   isp_add_wwn_entry(isp, chan, wwn, nphdl, portid);
 *   isp_library.c:2398   fcp = FCPARAM(isp, chan);
 *   ispvar.h:644         FCPARAM(isp, chan) = &((fcparam *)isp->isp_param)[chan]
 *   isp_pci.c:824-832    isp_param = kmalloc(sizeof(fcparam) * isp_nchan),
 *                        isp_nchan in {1, 2} (isp_pci.c:662/732/749)
 *
 * The struct below is a size-faithful replica of ispvar.h's fcparam
 * (MAX_FC_TARG = 512, MAX_NPORT_HANDLE = 2048) only for the arithmetic; every
 * field is char padding. Build: cc -o vpidx_oob_arith vpidx_oob_arith.c
 */
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>

#define MAX_FC_TARG      512
#define MAX_NPORT_HANDLE 2048

typedef struct { char _[8 + 4 + 4 + 4 + 2 + 2 + 1 + 1 + 8 + 8 + 4 + 4 + 4 + 4 + 4]; } fcportdb_t; /* ~58+ B; real one is >= this */

typedef struct {
	char              head[64];                          /* scalars, pointers, maps */
	uint16_t          isp_tgt_map[MAX_NPORT_HANDLE];     /* 4096 B — real offset ~64KB into fcp */
	char              portdb[MAX_FC_TARG * 128];         /* >= 512 * 128 = 64KB (real fcportdb_t is larger than 128B) */
} fcparam_replica;

int main(void)
{
	int nchan, chan;
	size_t alloc_sz;

	printf("sizeof(fcparam) replica (LOWER BOUND; real is larger): %zu\n", sizeof(fcparam_replica));
	for (nchan = 1; nchan <= 2; nchan++) {
		alloc_sz = sizeof(fcparam_replica) * nchan;
		printf("\nisp_nchan=%d  kmalloc size >= %zu bytes\n", nchan, alloc_sz);
		for (chan = 1; chan <= 255; chan += 51) {
			ptrdiff_t off = (ptrdiff_t)sizeof(fcparam_replica) * chan;
			printf("  in_vpidx=%3d -> FCPARAM offset %11td bytes -> %s by %td\n",
			    chan, off,
			    off >= (ptrdiff_t)alloc_sz ? "OUT OF BOUNDS" : "in bounds",
			    off - (ptrdiff_t)alloc_sz);
		}
		printf("  in_vpidx=255 (max uint8_t) -> offset %td bytes past a %zu-byte block\n",
		    (ptrdiff_t)sizeof(fcparam_replica) * 255, alloc_sz);
	}
	printf("\nWritable via that OOB pointer (isp_library.c:2407-2497):\n");
	printf("  fcp->isp_tgt_map[nphdl]      (read + write i+1)\n");
	printf("  lp->portid = s_id            (write, remote-controlled)\n");
	printf("  lp->port_wwn = ini           (write, remote-controlled 8B)\n");
	printf("  lp->target_mode/handle/etc.\n");
	return 0;
}