/*
 * DF-1284 harness: unbounded scatter-gather loop overflows pmap->psg[17] in
 * hptmv's OsSendCommand CAM_SCATTER_VALID path (entry.c).
 *
 * Object-level proof. The hptmv driver attaches to HighPoint RocketRAID
 * controllers; none are present in the audit QEMU guest, so the SIM action
 * path is not runtime-reachable here. This harness replays the exact loop
 * with a CAM CCB whose sglist_cnt (u16) exceeds MAX_SG_DESCRIPTORS (17).
 *
 * Cited path: sys/dev/raid/hptmv/entry.c:2800 (pSgTable=pmap->psg),
 *   :2852 CAM_SCATTER_VALID branch, :2859 for(idx<sglist_cnt) writes
 *   pSgTable[idx], :2860-2862 field writes. psg is SCAT_GATH[17]
 *   (osbsd.h:143, MAX_SG_DESCRIPTORS=17 global.h:47). sglist_cnt is
 *   u_int16_t (cam_ccb.h:604), NOT validated against MAX_SG_DESCRIPTORS.
 *
 * Build: cc -O2 -o harness harness.c
 * Run:   ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

typedef uint64_t ULONG_PTR;
typedef uint32_t ULONG;
typedef uint16_t USHORT;
typedef uint8_t  UCHAR;
typedef ULONG_PTR PVOID;

#define MAX_SG_DESCRIPTORS 17          /* global.h:47 */
#define SG_FLAG_EOT        0x8000      /* global.h:119 */

/* Mirror of sys/dev/raid/hptmv/global.h:103 SCAT_GATH */
typedef struct _SCAT_GATH {
	ULONG_PTR dSgAddress;
	USHORT    wSgSize;
	USHORT    wSgFlag;
} SCAT_GATH;

/* Mirror of sys/dev/raid/hptmv/osbsd.h:138 BUS_DMAMAP (the allocation that
 * holds psg[]; pmap points at one of these). What follows psg[] in the real
 * struct is the next slab object / the IALAdapter linkage -- the guard here
 * stands in for the adjacent heap victim. */
typedef struct _BUS_DMAMAP {
	struct _BUS_DMAMAP *next;
	void               *pAdapter;     /* struct IALAdapter * */
	void               *dma_map;      /* bus_dmamap_t */
	SCAT_GATH           psg[MAX_SG_DESCRIPTORS];
	/* ---- end of the real struct; guard below simulates adjacent slab ---- */
	SCAT_GATH           guard[64];
} BUS_DMAMAP;

/* bus_dma_segment_t is { uint32_t ds_addr (or uint64_t); bus_size_t ds_len } */
typedef struct { uint64_t ds_addr; uint64_t ds_len; } bus_dma_segment_t;

int main(void)
{
	BUS_DMAMAP *pmap = calloc(1, sizeof(*pmap));
	if (!pmap) { perror("calloc"); return 1; }
	SCAT_GATH *pSgTable = pmap->psg;          /* entry.c:2800 */

	/* paint the guard so we can detect overflow */
	memset(pmap->guard, 0xAA, sizeof(pmap->guard));

	printf("[DF-1284] psg[%d] in BUS_DMAMAP, sizeof(SCAT_GATH)=%zu, "
	       "psg spans bytes 0..%zu\n", MAX_SG_DESCRIPTORS, sizeof(SCAT_GATH),
	       sizeof(pmap->psg));

	/* attacker: a CAM CCB with CAM_SCATTER_VALID and a huge sglist_cnt.
	 * sglist_cnt is u_int16_t, range 0..65535. XPT does NOT clamp it to the
	 * SIM's segment limit. */
	uint16_t sglist_cnt = 64;                 /* >> MAX_SG_DESCRIPTORS(17) */
	bus_dma_segment_t *sgList = calloc(sglist_cnt, sizeof(*sgList));
	for (uint16_t i = 0; i < sglist_cnt; i++) { sgList[i].ds_addr = 0x1000+i; sgList[i].ds_len = 4096; }

	printf("[DF-1284] attacker sglist_cnt=%u (max allowed for psg[]=%d)\n",
	       sglist_cnt, MAX_SG_DESCRIPTORS);

	/* === entry.c:2859-2863 (the unbounded loop) === */
	int idx;
	for (idx = 0; idx < sglist_cnt; idx++) {
		pSgTable[idx].dSgAddress = (ULONG_PTR)(UCHAR *)sgList[idx].ds_addr; /* :2860 */
		pSgTable[idx].wSgSize    = (USHORT)sgList[idx].ds_len;              /* :2861 */
		pSgTable[idx].wSgFlag    = (idx == sglist_cnt-1) ? SG_FLAG_EOT : 0;  /* :2862 */
	}

	/* count guard entries clobbered (== adjacent slab object(s) overwritten) */
	unsigned hit = 0;
	for (unsigned g = 0; g < 64; g++) {
		uint8_t *p = (uint8_t *)&pmap->guard[g];
		uint8_t *e = p + sizeof(pmap->guard[g]);
		for (; p < e; p++) if (*p != 0xAA) { hit++; break; }
	}
	printf("[DF-1284] overflow past psg[17]: %s (%u guard entries clobbered; "
	       "%zd bytes of adjacent heap overwritten)\n",
	       hit ? "YES" : "no", hit, (ssize_t)((sglist_cnt - MAX_SG_DESCRIPTORS) * sizeof(SCAT_GATH)));

	puts("\n--- Fix demonstration ---");
	/* entry.c already has, in the *other* path (hptmv_dmamap_callback:2652):
	 *   HPT_ASSERT(nsegs <= MAX_SG_DESCRIPTORS);
	 * The CAM_SCATTER_VALID path needs the same clamp: */
	if (sglist_cnt > MAX_SG_DESCRIPTORS) {
		printf("[DF-1284] FIX: clamp/REJECT sglist_cnt=%u > MAX_SG_DESCRIPTORS=%d "
		       "-> no overflow\n", sglist_cnt, MAX_SG_DESCRIPTORS);
	}

	free(sgList); free(pmap);
	return hit ? 0 : 2;
}
