/*
 * DF-3689 harness: unvalidated VBIOS vddInd (u8) used as an index into the
 * voltage-lookup table in smu7_patch_voltage_dependency_tables_with_lookup_table
 * (smu7_hwmgr.c:1865-1869, 1872-1876, 1878-1882) and
 * smu7_patch_voltage_workaround (:2135).
 *
 * The lookup table is allocated by get_vddc_lookup_table
 * (process_pptables_v1_0.c:160-190) for max_levels=16 records, while dep
 * records carry a raw ucVddcInd u8 (0..255) from VBIOS with no range check.
 * The harness reproduces the read arithmetic against the real allocation
 * geometry and shows OOB reads landing past the table.
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

struct lookup_record { uint16_t us_calculated, us_vdd, us_cac_low, us_cac_mid, us_cac_high; };
struct lookup_table {
	uint32_t count;			/* = ucNumEntries from VBIOS */
	struct lookup_record entries[16];	/* allocation per max_levels=16 */
};
struct dep_record { uint32_t clk; uint8_t vddInd; };
struct dep_table { uint32_t count; struct dep_record entries[255]; };

int main(void)
{
	/* allocation geometry as in process_pptables_v1_0.c:168-171 */
	struct lookup_table *lk = calloc(1, sizeof(uint32_t) + 16 * sizeof(struct lookup_record));
	struct dep_table dep;
	int i, oob_reads = 0;

	if (!lk)
		return 1;

	lk->count = 4;			/* VBIOS says 4 lookup entries */
	dep.count = 255;
	for (i = 0; i < 255; i++)
		dep.entries[i].vddInd = (uint8_t)(i * 7);	/* crafted u8 indices */

	printf("lookup table: %zu usable records (max_levels=16), count field=%u\n",
	       (size_t)(16), lk->count);

	/* verbatim loop shape from smu7_hwmgr.c:1865-1869 */
	for (i = 0; i < (int)dep.count; i++) {
		uint8_t voltage_id = dep.entries[i].vddInd;
		/* kernel does: vddc = vddc_lookup_table->entries[voltage_id].us_vdd; */
		size_t idx_bytes = sizeof(uint32_t) + 16 * sizeof(struct lookup_record);
		if ((size_t)voltage_id * sizeof(struct lookup_record) + sizeof(uint32_t) >= idx_bytes)
			oob_reads++;
	}

	printf("dep entries whose vddInd read lands past the 16-record kmalloc: %d / %d\n",
	       oob_reads, (int)dep.count);
	printf("max index 255 -> byte offset %llu past entries[0] (allocation holds "
	       "%zu bytes of entries)\n",
	       (unsigned long long)255 * sizeof(struct lookup_record),
	       (size_t)(16 * sizeof(struct lookup_record)));

	if (oob_reads > 0) {
		printf("RESULT: HEAP OOB READ CONFIRMED at the arithmetic level - u8 vddInd "
		       "indexes up to 255 into a 16-record table (also OOB vs count=%u)\n",
		       lk->count);
		return 0;
	}
	fprintf(stderr, "RESULT: OOB read not observed\n");
	return 1;
}
