/*
 * DF-1139 harness: radeon/si_dpm.c si_get_std_voltage_value
 *   -> OOB read when cac_leakage_table.count==0 (si_dpm.c:4158-4187)
 *
 * Userspace logic harness. r600_dpm.c:1039-1043 allocates the leakage table
 * with kzalloc(ucNumEntries * sizeof(entry)); when ucNumEntries==0, kzalloc(0)
 * returns ZERO_SIZE_PTR (==16), which is non-NULL. The guard at si_dpm.c:4158
 * `if (entries)` only checks for NULL, so the body executes with count==0;
 * the fallback at line 4172 reads entries[(u32)count - 1] = entries[0xFFFFFFFF]
 * -> ~64 GB OOB read -> page fault -> panic.
 *
 * Build:  cc -O2 -o harness harness.c
 * Run:    ./harness
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#define ZERO_SIZE_PTR ((void *)16)   /* matches Linux/DragonFly SMALL_ALLOC */

struct cac_leakage_entry { uint16_t vddc; uint16_t pad; uint32_t leakage; };
struct cac_leakage_table {
	struct cac_leakage_entry *entries;
	uint32_t count;
};

/* Mirror of buggy guard (si_dpm.c:4158) -- only checks non-NULL pointer */
static int buggy_read(struct cac_leakage_table *t, uint32_t v_index)
{
	if (t->entries) {
		/* fallthrough path si_dpm.c:4170-4172 -- when v_index >= count
		 * (always true when count==0), uses entries[count-1] */
		uint32_t back_idx = t->count - 1;   /* (u32)(0 - 1) */
		return t->entries[back_idx].vddc;   /* OOB read */
	}
	return -1;
}

/* Fixed guard -- also requires count > 0 */
static int fixed_read(struct cac_leakage_table *t, uint32_t v_index)
{
	if (t->entries && t->count > 0) {
		uint32_t back_idx = t->count - 1;
		return t->entries[back_idx].vddc;
	}
	return -1;
}

int main(void)
{
	/* Simulate a crafted VBIOS: ATOM_PPLIB_CAC_Leakage_Table with ucNumEntries==0 */
	struct cac_leakage_table crafted = {
		.entries = (struct cac_leakage_entry *)ZERO_SIZE_PTR,
		.count   = 0,
	};

	printf("=== DF-1139 harness: crafted VBIOS with cac_leakage_table.ucNumEntries==0 ===\n\n");
	printf("    r600_dpm.c:1039-1043 calls kzalloc(0 * sizeof(entry)) = kzalloc(0)\n");
	printf("    -> returns ZERO_SIZE_PTR == %p (NON-NULL)\n", ZERO_SIZE_PTR);
	printf("    -> count = 0 (loaded directly from BIOS u8)\n\n");

	printf("[A] Buggy guard (si_dpm.c:4158 `if (entries)`):\n");
	printf("    entries=%p is non-NULL, so body executes with count==u%d\n",
	       crafted.entries, 0);
	/* We do NOT actually dereference ZERO_SIZE_PTR+0xFFFFFFFF -- userspace would SIGSEGV
	 * just like the kernel would page-fault. We compute the offset to demonstrate. */
	uint32_t back_idx = (uint32_t)(crafted.count - 1);
	uint64_t fault_addr = (uint64_t)crafted.entries + back_idx * sizeof(struct cac_leakage_entry);
	printf("    reads entries[count-1] = entries[0x%08x]\n", back_idx);
	printf("    effective address = %p + 0x%08x * %zu = 0x%016llx\n",
	       crafted.entries, back_idx, sizeof(struct cac_leakage_entry),
	       (unsigned long long)fault_addr);
	printf("    >>> In kernel: page-fault at ~%llu GB offset -> panic <<<\n\n",
	       (unsigned long long)fault_addr / 1024 / 1024 / 1024);

	printf("[B] Fixed guard (fix.diff: `if (entries && count > 0)`):\n");
	int rc = fixed_read(&crafted, 0);
	printf("    rc=%d  (rejected, no OOB read)\n", rc);

	return 0;
}
