/*
 * DF-1175 — userspace harness for Intel/nVidia RAID metadata parser OOB read.
 *
 * Reconstructs the checksum loops from:
 *   - sys/dev/disk/nata/ata-raid.c:2132-2154  (Intel parser, kmalloc(1536))
 *   - sys/dev/disk/nata/ata-raid.c:3028-3048  (nVidia parser, kmalloc(512))
 *
 * In both parsers `meta` is a small fixed-size kmalloc'd buffer, but the
 * checksum loop iterates a disk-controlled u32 field (`config_size`):
 *
 *   Intel  : count < meta->config_size/4      (4 bytes/iter)
 *   nVidia : count < meta->config_size        (4 bytes/iter)
 *
 * A large config_size drives the read past the kmalloc into unmapped kernel
 * memory -> panic.  The magic-string gate atIntel:2145 / nVidia:3039 runs
 * BEFORE the loop, but the checksum verification at Intel:2156 / nVidia:3049
 * runs AFTER, so a valid checksum is NOT needed to reach the bug -- only the
 * magic is.
 *
 * Build:  cc -O2 -o harness harness.c
 * Run:    ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <sys/types.h>

#define INTEL_MAGIC "Intel Raid ISM Cfg Sig. "
#define NV_MAGIC    "NVIDIA  "

/* Mirror the on-disk struct layouts (verified by in-guest sizeof() = 512
 * bytes each).  We keep only the fields the parsers actually touch. */
struct intel_raid_conf {
    uint8_t  intel_id[24];
    uint8_t  version[6];
    uint8_t  dummy_0[2];
    uint32_t checksum;
    uint32_t config_size;
    uint32_t config_id;
    uint32_t generation;
    uint32_t dummy_1[2];
    uint8_t  total_disks;
    uint8_t  total_volumes;
    uint8_t  dummy_2[2];
    uint32_t filler_0[39];
    /* rest is irrelevant to the loop; size up to 512 */
    uint8_t  rest[512 - 24 - 6 - 2 - 4*7 - 2 - 39*4];
} __attribute__((packed));

struct nvidia_raid_conf {
    uint8_t  nvidia_id[8];
    uint32_t config_size;
    uint32_t checksum;
    uint16_t version;
    uint8_t  disk_number;
    uint8_t  dummy_0;
    uint32_t total_sectors;
    uint32_t sector_size;
    uint8_t  serial[16];
    uint8_t  revision[4];
    uint32_t dummy_1;
    uint8_t  rest[512 - 8 - 4*5 - 2 - 2 - 16 - 4];
} __attribute__((packed));

static sigjmp_buf jb;
static volatile int got_sig;
static void handler(int s, siginfo_t *si, void *uc) {
	(void)si; (void)uc;
	got_sig = s;
	siglongjmp(jb, 1);
}

/* Reproduce the Intel checksum loop at ata-raid.c:2151-2154.
 * meta_bufsize is the kmalloc size (1536). */
static uint32_t intel_loop(struct intel_raid_conf *meta, size_t meta_bufsize)
{
	uint32_t checksum = 0;
	uint32_t *ptr = (uint32_t *)meta;
	uint32_t count;
	/* ata-raid.c:2151-2154 */
	for (count = 0; count < (meta->config_size / sizeof(uint32_t)); count++)
		checksum += *ptr++;
	(void)meta_bufsize;
	return checksum;
}

/* Reproduce the nVidia checksum loop at ata-raid.c:3046-3048. */
static uint32_t nvidia_loop(struct nvidia_raid_conf *meta, size_t meta_bufsize)
{
	uint32_t checksum = 0;
	uint32_t *ptr = (uint32_t *)meta;
	uint32_t count;
	/* ata-raid.c:3046-3048 */
	for (count = 0; count < meta->config_size; count++)
		checksum += *ptr++;
	(void)meta_bufsize;
	return checksum;
}

int main(void)
{
	struct sigaction sa;
	memset(&sa, 0, sizeof(sa));
	sa.sa_sigaction = handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_SIGINFO;
	sigaction(SIGSEGV, &sa, NULL);
	sigaction(SIGBUS,  &sa, NULL);

	/* ----- Case 1: Intel parser, oversized config_size ----- */
	puts("=== Case 1: Intel parser (ata-raid.c:2132-2154) ===");
	puts("  meta = kmalloc(1536); magic OK; config_size = 0x10000 (65536)");
	puts("  loop reads 65536 bytes from a 1536-byte buffer -> 64000-byte OOB read");

	/* Allocate exactly what the kernel does: 1536 bytes, zeroed. */
	char *intel_buf = calloc(1, 1536);
	struct intel_raid_conf *meta_i = (struct intel_raid_conf *)intel_buf;
	memcpy(meta_i->intel_id, INTEL_MAGIC, strlen(INTEL_MAGIC));
	/* magic check (ata-raid.c:2145) would PASS */
	if (strncmp((char *)meta_i->intel_id, INTEL_MAGIC, strlen(INTEL_MAGIC)) != 0) {
		puts("  magic mismatch -- harness setup error");
	} else {
		puts("  magic check: PASS (matches INTEL_MAGIC)");
	}
	meta_i->config_size = 0x10000;  /* disk-controlled u32 = 65536 */

	uint32_t iter_count_i = meta_i->config_size / sizeof(uint32_t);
	uint32_t bytes_read_i  = iter_count_i * sizeof(uint32_t);
	printf("  loop will run %u iterations, reading %u bytes "
	       "(buffer = 1536 bytes -> %u-byte OOB read)\n",
	       iter_count_i, bytes_read_i, bytes_read_i - 1536);
	fflush(stdout);

	got_sig = 0;
	if (sigsetjmp(jb, 1) == 0) {
		intel_loop(meta_i, 1536);
		/* In userspace with a single 1536-byte calloc we usually crash
		 * before completing; if not, the loop has read garbage which
		 * itself is the info-leak equivalent in the kernel. */
		puts("  (loop completed without SIGSEGV -- in the kernel this "
		     "would have consumed kmalloc trailing bytes = info leak)");
	} else {
		printf("  BUG: OOB read faulted (signal %d) -- kernel "
		       "equivalent: page fault past kmalloc(1536) in "
		       "ata_raid_intel_read_meta -> panic\n", got_sig);
	}
	free(intel_buf);

	/* ----- Case 2: nVidia parser, oversized config_size ----- */
	puts("");
	puts("=== Case 2: nVidia parser (ata-raid.c:3028-3048) ===");
	puts("  meta = kmalloc(sizeof(nvidia_raid_conf)=512); magic OK; config_size = 0x400 (1024)");
	puts("  loop reads 1024 u32 words = 4096 bytes from a 512-byte buffer -> 3584-byte OOB read");

	char *nv_buf = calloc(1, 512);
	struct nvidia_raid_conf *meta_n = (struct nvidia_raid_conf *)nv_buf;
	memcpy(meta_n->nvidia_id, NV_MAGIC, strlen(NV_MAGIC));
	if (strncmp((char *)meta_n->nvidia_id, NV_MAGIC, strlen(NV_MAGIC)) != 0) {
		puts("  magic mismatch -- harness setup error");
	} else {
		puts("  magic check: PASS (matches NV_MAGIC)");
	}
	meta_n->config_size = 0x400;  /* 1024 iterations = 4096 bytes */

	uint32_t bytes_read_n = meta_n->config_size * sizeof(uint32_t);
	printf("  loop will run %u iterations, reading %u bytes "
	       "(buffer = 512 bytes -> %u-byte OOB read)\n",
	       meta_n->config_size, bytes_read_n, bytes_read_n - 512);
	fflush(stdout);

	got_sig = 0;
	if (sigsetjmp(jb, 1) == 0) {
		nvidia_loop(meta_n, 512);
		puts("  (loop completed without SIGSEGV -- kernel equivalent: "
		     "info leak of slab trailing bytes)");
	} else {
		printf("  BUG: OOB read faulted (signal %d) -- kernel "
		       "equivalent: page fault past kmalloc(512) in "
		       "ata_raid_nvidia_read_meta -> panic\n", got_sig);
	}
	free(nv_buf);

	puts("");
	puts("DF-1175 harness: both OOB-read cases demonstrated.");
	puts("  Impact ceiling: info leak (small over-read) + DoS panic (large over-read).");
	return 0;
}
