/*
 * DF-3739 harness: radeon_atombios_i2c_init() OOB write past rdev->i2c_bus[16]
 *
 * Replicates VERBATIM the loop arithmetic of sys/dev/drm/radeon/radeon_atombios.c:169-188
 *   num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / sizeof(ATOM_GPIO_I2C_ASSIGMENT);
 *   for (i = 0; i < num_indices; i++) { ... if (i2c.valid) rdev->i2c_bus[i] = radeon_i2c_create(...); }
 * against the real fixed kernel array  rdev->i2c_bus[RADEON_MAX_I2C_BUS]   (radeon.h:2440,
 * RADEON_MAX_I2C_BUS == 16, radeon_mode.h:96).
 *
 * size comes straight from the VBIOS GPIO_I2C_Info data table via atom_parse_data_header()
 * (atom.c:1366-1385) which performs NO bounds validation of the u16 size field.
 *
 * The destination is modelled as i2c_bus[16] embedded in a struct immediately followed by a
 * canary region (in the kernel: radeon_device.debugfs[32], .debugfs_count, .vm_manager,
 * .gpu_clock_mutex, ... — radeon.h:2441+). i2c.valid is modelled as BIOS-controlled
 * (usClkMaskRegisterIndex != 0 -> valid, radeon_atombios.c:113-116).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>

typedef uint8_t  u8;  typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64;

/* --- ATOM struct replicas (atombios.h:206, :3203-3222) --- */
typedef struct { u16 usStructureSize; u8 ucTableFormatRevision, ucTableContentRevision; }
	ATOM_COMMON_TABLE_HEADER;
typedef union { u16 bfAccess; struct { u16 x:8; }; u8 ucAccess; } ATOM_I2C_ID_CONFIG_ACCESS;
typedef struct {
	u16 usClkMaskRegisterIndex, usClkEnRegisterIndex, usClkY_RegisterIndex, usClkA_RegisterIndex;
	u16 usDataMaskRegisterIndex, usDataEnRegisterIndex, usDataY_RegisterIndex, usDataA_RegisterIndex;
	ATOM_I2C_ID_CONFIG_ACCESS sucI2cId;
	u8 ucClkMaskShift, ucClkEnShift, ucClkY_Shift, ucClkA_Shift;
	u8 ucDataMaskShift, ucDataEnShift, ucDataY_Shift, ucDataA_Shift;
	u8 ucReserved1, ucReserved2;
} ATOM_GPIO_I2C_ASSIGMENT;

#define RADEON_MAX_I2C_BUS 16			/* radeon_mode.h:96 */

/* model of struct radeon_device tail: radeon.h:2440-2442 */
struct radeon_device_model {
	void    *pad;					/* ...hyperz_filp etc. */
	struct { void *p; } i2c_bus[RADEON_MAX_I2C_BUS];	/* radeon.h:2440 */
	u64      canary[16];				/* models debugfs[] etc. that follow */
};

static unsigned long oob_writes, oob_bytes;
#define GUARD 0xDEADBEEFCAFEF00DUL

/* radeon_i2c_create() stand-in: returns non-NULL heap-ish pointer */
static void *fake_i2c_create(unsigned idx) { return (void *)(0x1000UL + idx * 0x40UL); }

int main(void)
{
	printf("sizeof(ATOM_GPIO_I2C_ASSIGMENT) = %zu (kernel expects 28)\n", sizeof(ATOM_GPIO_I2C_ASSIGMENT));
	if (sizeof(ATOM_GPIO_I2C_ASSIGMENT) != 28 || sizeof(ATOM_COMMON_TABLE_HEADER) != 4) {
		fprintf(stderr, "layout replica mismatch\n"); return 2;
	}

	/* crafted VBIOS GPIO_I2C_Info table: self-declared size = 0x0FFC (4092) bytes.
	 * atom_parse_data_header() returns it verbatim: no validation against image length. */
	u16 bios_size_field = 4092;
	int num_indices = (bios_size_field - sizeof(ATOM_COMMON_TABLE_HEADER)) /
		sizeof(ATOM_GPIO_I2C_ASSIGMENT);			/* == radeon_atombios.c:172-173 */
	printf("BIOS size field = %u -> num_indices = %d (i2c_bus[%d])\n",
	       bios_size_field, num_indices, RADEON_MAX_I2C_BUS);

	struct radeon_device_model rdev;
	memset(&rdev, 0, sizeof(rdev));
	for (int i = 0; i < 16; i++) rdev.canary[i] = GUARD;

	/* verbatim loop body, radeon_atombios.c:176-187 */
	for (int i = 0; i < num_indices; i++) {
		/* radeon_lookup_i2c_gpio_quirks(): no-op for this model.
		 * i2c = radeon_get_bus_rec_for_i2c_gpio(gpio):
		 *   i2c.valid = (gpio->usClkMaskRegisterIndex != 0)  [:113-116]
		 * BIOS attacker sets it nonzero -> valid -> store performed. */
		int valid = 1;					/* fully BIOS-controlled */
		if (valid) {
			/* rdev->i2c_bus[i] = radeon_i2c_create(...)  [:183] */
			if (i >= RADEON_MAX_I2C_BUS) { oob_writes++; oob_bytes += sizeof(void *); }
			if (i < RADEON_MAX_I2C_BUS + 16)	/* stop before smashing our own canary base */
				((void **)&rdev.i2c_bus[0])[i] = fake_i2c_create(i);
		}
	}

	unsigned long smashed = 0;
	for (int i = 0; i < 16; i++) if (rdev.canary[i] != GUARD) smashed++;
	printf("kernel-store rdev->i2c_bus[i] executed for i=0..%d\n", num_indices - 1);
	printf("OOB pointer writes past i2c_bus[%d] : %lu (%lu bytes) into subsequent radeon_device fields\n",
	       RADEON_MAX_I2C_BUS, oob_writes, oob_bytes);
	printf("canary words corrupted in this model : %lu\n", smashed);

	/* worst case: size = 0xFFFF */
	u16 worst = 0xFFFF;
	int worst_n = (worst - 4) / 28;
	printf("worst case size=0xFFFF -> num_indices = %d -> %d OOB pointer writes (%d bytes)\n",
	       worst_n, worst_n > 16 ? worst_n - 16 : 0, (worst_n - 16) * (int)sizeof(void *));

	if (oob_writes == 0) { printf("FAIL: no OOB demonstrated\n"); return 1; }
	printf("REPRODUCED (harness): BIOS-count-driven write past i2c_bus[16] in radeon_device\n");
	return 0;
}
