/*
 * DF-4495 — dce100_link_encoder_create unvalidated VBIOS table indexes
 * ---------------------------------------------------------------------
 * Guest-side model proof (no AMD GPU required, no sanitizers required).
 *
 * Faithful index-domain model of:
 *   sys/dev/drm/amd/display/dc/dce100/dce100_resource.c:560-577
 *
 * 	static struct link_encoder *dce100_link_encoder_create(
 * 			const struct encoder_init_data *enc_init_data)
 * 	{
 * 		struct dce110_link_encoder *enc110 = kzalloc(...);
 * 		if (!enc110) return NULL;
 * 		dce110_link_encoder_construct(enc110, enc_init_data,
 * 				&link_enc_feature,
 * 				&link_enc_regs[enc_init_data->transmitter],      // :573
 * 				&link_enc_aux_regs[enc_init_data->channel - 1],  // :574
 * 				&link_enc_hpd_regs[enc_init_data->hpd_source]);  // :575
 * 		...
 *
 * Table sizes in the file:
 *   link_enc_regs[]      7 entries  (:232-240, link_regs(0)..link_regs(6))
 *   link_enc_aux_regs[]  6 entries  (:204-211, aux_regs(0)..aux_regs(5))
 *   link_enc_hpd_regs[]  6 entries  (:218-225, hpd_regs(0)..hpd_regs(5))
 *
 * Index domains verbatim from
 *   sys/dev/drm/amd/display/include/grph_object_defs.h:81-94 (transmitter)
 *   sys/dev/drm/amd/display/include/grph_object_defs.h:58-69 (channel_id)
 *   sys/dev/drm/amd/display/include/grph_object_defs.h:45-55 (hpd_source_id)
 *
 * and the producers in sys/dev/drm/amd/display/dc/core/dc_link.c:
 *   get_hpd_line                       (:925-962)  -> 1..6 or UNKNOWN(7)
 *   get_ddc_line                       (:964-1004) -> 0..8 (UNKNOWN=0,
 *                                                    DDC1..6=1..6, VGA=7,
 *                                                    I2C_PAD=8)
 *   translate_encoder_to_transmitter   (:1006-1069) -> -1 (UNKNOWN),
 *                                                    0..5 (UNIPHY_A..F),
 *                                                    6 (NUTMEG_CRT),
 *                                                    7 (TRAVIS_CRT),
 *                                                    8 (TRAVIS_LCD),
 *                                                    9 (UNIPHY_G)
 * All three come straight from the GPU VBIOS object tables with no
 * validation anywhere on the path.
 *
 * Build:  cc -O2 -Wall -o df4495 df4495.c
 * Run:    ./df4495
 * Success criterion (defect modelled): the verbatim index expressions are
 * out of bounds for reachable enum values (4/11 transmitter, 3/9 channel,
 * 2/8 hpd_source = 9/28).
 */
#include <stdio.h>

/* verbatim from grph_object_defs.h:81-94 */
enum transmitter {
	TRANSMITTER_UNKNOWN = (-1),
	TRANSMITTER_UNIPHY_A,
	TRANSMITTER_UNIPHY_B,
	TRANSMITTER_UNIPHY_C,
	TRANSMITTER_UNIPHY_D,
	TRANSMITTER_UNIPHY_E,
	TRANSMITTER_UNIPHY_F,
	TRANSMITTER_NUTMEG_CRT,
	TRANSMITTER_TRAVIS_CRT,
	TRANSMITTER_TRAVIS_LCD,
	TRANSMITTER_UNIPHY_G,
	TRANSMITTER_COUNT
};

/* verbatim from grph_object_defs.h:58-69 */
enum channel_id {
	CHANNEL_ID_UNKNOWN = 0,
	CHANNEL_ID_DDC1,
	CHANNEL_ID_DDC2,
	CHANNEL_ID_DDC3,
	CHANNEL_ID_DDC4,
	CHANNEL_ID_DDC5,
	CHANNEL_ID_DDC6,
	CHANNEL_ID_DDC_VGA,
	CHANNEL_ID_I2C_PAD,
	CHANNEL_ID_COUNT
};

/* verbatim from grph_object_defs.h:45-55 */
enum hpd_source_id {
	HPD_SOURCEID1 = 0,
	HPD_SOURCEID2,
	HPD_SOURCEID3,
	HPD_SOURCEID4,
	HPD_SOURCEID5,
	HPD_SOURCEID6,
	HPD_SOURCEID_COUNT,
	HPD_SOURCEID_UNKNOWN
};

/* table extents exactly as declared in dce100_resource.c */
#define LINK_ENC_REGS_N     7	/* :232-240 */
#define LINK_ENC_AUX_REGS_N 6	/* :204-211 */
#define LINK_ENC_HPD_REGS_N 6	/* :218-225 */

static const struct { int pad; } link_enc_regs[LINK_ENC_REGS_N];
static const struct { int pad; } link_enc_aux_regs[LINK_ENC_AUX_REGS_N];
static const struct { int pad; } link_enc_hpd_regs[LINK_ENC_HPD_REGS_N];

/*
 * The verbatim index arithmetic of dce100_resource.c:573-575.
 * Bounds are checked only to keep THIS model defined; the kernel code
 * performs the &table[idx] formation with no check at all.
 */
static int tx_index(enum transmitter t)   { return (int)t; }              /* :573 */
static int aux_index(enum channel_id c)   { return (int)c - 1; }          /* :574 */
static int hpd_index(enum hpd_source_id h){ return (int)h; }              /* :575 */

static int oob(int idx, int n) { return idx < 0 || idx >= n; }

static const char *tx_name(enum transmitter t)
{
	static const char *n[] = { "UNIPHY_A","UNIPHY_B","UNIPHY_C","UNIPHY_D",
		"UNIPHY_E","UNIPHY_F","NUTMEG_CRT","TRAVIS_CRT","TRAVIS_LCD",
		"UNIPHY_G" };
	if ((int)t < 0) return "UNKNOWN";
	return n[(int)t];
}

int main(void)
{
	/* every value translate_encoder_to_transmitter can emit (dc_link.c:1006-1069) */
	const enum transmitter txv[] = {
		TRANSMITTER_UNKNOWN,		/* default arm + unknown enum_id */
		TRANSMITTER_UNIPHY_A, TRANSMITTER_UNIPHY_B, TRANSMITTER_UNIPHY_C,
		TRANSMITTER_UNIPHY_D, TRANSMITTER_UNIPHY_E, TRANSMITTER_UNIPHY_F,
		TRANSMITTER_NUTMEG_CRT,
		TRANSMITTER_TRAVIS_CRT, TRANSMITTER_TRAVIS_LCD,
		TRANSMITTER_UNIPHY_G
	};
	/* every value get_ddc_line can emit (dc_link.c:964-1004) */
	const enum channel_id chv[] = {
		CHANNEL_ID_UNKNOWN,		/* no ddc / unrecognized line */
		CHANNEL_ID_DDC1, CHANNEL_ID_DDC2, CHANNEL_ID_DDC3,
		CHANNEL_ID_DDC4, CHANNEL_ID_DDC5, CHANNEL_ID_DDC6,
		CHANNEL_ID_DDC_VGA,		/* GPIO_DDC_LINE_DDC_VGA */
		CHANNEL_ID_I2C_PAD		/* GPIO_DDC_LINE_I2C_PAD */
	};
	/* every value get_hpd_line can emit (dc_link.c:925-962) */
	const enum hpd_source_id hpv[] = {
		HPD_SOURCEID1, HPD_SOURCEID2, HPD_SOURCEID3,
		HPD_SOURCEID4, HPD_SOURCEID5, HPD_SOURCEID6,
		HPD_SOURCEID_UNKNOWN		/* default arm / NULL hpd gpio */
	};

	int tx_oob = 0, ch_oob = 0, hp_oob = 0;
	size_t i;

	printf("DF-4495 model: dce100_link_encoder_create "
	    "(dce100_resource.c:573-575) const-table index domains\n\n");

	printf("[link_enc_regs[%d]]  indexed by transmitter (:573)\n", LINK_ENC_REGS_N);
	for (i = 0; i < sizeof(txv)/sizeof(txv[0]); i++) {
		int idx = tx_index(txv[i]);
		int bad = oob(idx, LINK_ENC_REGS_N);
		printf("  transmitter=%-11s idx=%3d  %s\n", tx_name(txv[i]), idx,
		    bad ? "OOB READ" : "ok");
		tx_oob += bad;
	}

	printf("\n[link_enc_aux_regs[%d]]  indexed by channel-1 (:574)\n", LINK_ENC_AUX_REGS_N);
	for (i = 0; i < sizeof(chv)/sizeof(chv[0]); i++) {
		int idx = aux_index(chv[i]);
		int bad = oob(idx, LINK_ENC_AUX_REGS_N);
		printf("  channel=%-14d idx=%3d  %s\n", (int)chv[i], idx,
		    bad ? "OOB READ" : "ok");
		ch_oob += bad;
	}

	printf("\n[link_enc_hpd_regs[%d]]  indexed by hpd_source (:575)\n", LINK_ENC_HPD_REGS_N);
	for (i = 0; i < sizeof(hpv)/sizeof(hpv[0]); i++) {
		int idx = hpd_index(hpv[i]);
		int bad = oob(idx, LINK_ENC_HPD_REGS_N);
		printf("  hpd_source=%-9d idx=%3d  %s\n", (int)hpv[i], idx,
		    bad ? "OOB READ" : "ok");
		hp_oob += bad;
	}

	printf("\nreachable enum values: %zu transmitter, %zu channel, %zu hpd\n",
	    sizeof(txv)/sizeof(txv[0]), sizeof(chv)/sizeof(chv[0]),
	    sizeof(hpv)/sizeof(hpv[0]));
	printf("OOB: %d/%zu transmitter, %d/%zu channel, %d/%zu hpd "
	    "(= %d/%zu reachable values)\n",
	    tx_oob, sizeof(txv)/sizeof(txv[0]),
	    ch_oob, sizeof(chv)/sizeof(chv[0]),
	    hp_oob, sizeof(hpv)/sizeof(hpv[0]),
	    tx_oob+ch_oob+hp_oob,
	    sizeof(txv)/sizeof(txv[0]) + sizeof(chv)/sizeof(chv[0]) +
	    sizeof(hpv)/sizeof(hpv[0]));

	/* concrete VBIOS-craftable link: TRAVIS external encoder on a VGA
	 * connector with no HPD line — every one of the three lookups OOB */
	printf("\nexample craftable link: TRAVIS_CRT encoder + DDC_VGA line + "
	    "connector without HPD -> idx %d / %d / %d (all OOB)\n",
	    tx_index(TRANSMITTER_TRAVIS_CRT),
	    aux_index(CHANNEL_ID_DDC_VGA),
	    hpd_index(HPD_SOURCEID_UNKNOWN));

	if (tx_oob && ch_oob && hp_oob) {
		printf("\nCONST-TABLE OOB READ CONFIRMED (model): "
		    "dce100_resource.c:573-575 forms &link_enc_regs[tx], "
		    "&link_enc_aux_regs[ch-1], &link_enc_hpd_regs[hpd] with "
		    "unvalidated VBIOS-derived indexes — garbage becomes the "
		    "encoder's MMIO register bases.\n");
		return 0;
	}
	printf("\nmodel did not reproduce expectation\n");
	return 1;
}
