⬢ DragonFlyBSD Kernel Audit
DF-4319 / df4319.c
← back to finding ↓ download raw
/*
 * DF-4319 — dce80_link_encoder_create unvalidated const-table indexing
 * ---------------------------------------------------------------------
 * Host-side reachability proof (no AMD GPU required).
 *
 * Exact model of sys/dev/drm/amd/display/dc/dce80/dce80_resource.c:661-678:
 *
 *     dce110_link_encoder_construct(enc110, enc_init_data,
 *                                   &link_enc_feature,
 *                                   &link_enc_regs[enc_init_data->transmitter],
 *                                   &link_enc_aux_regs[enc_init_data->channel - 1],
 *                                   &link_enc_hpd_regs[enc_init_data->hpd_source]);
 *
 * Table sizes in dce80_resource.c:
 *   link_enc_regs[]      : 7 entries  (dce80_resource.c:249-257, link_regs(0..6))
 *   link_enc_aux_regs[]  : 6 entries  (dce80_resource.c:221-228, aux_regs(0..5))
 *   link_enc_hpd_regs[]  : 6 entries  (dce80_resource.c:235-242, hpd_regs(0..5))
 *
 * Index provenance (values the DC core legitimately produces):
 *   transmitter  = translate_encoder_to_transmitter()  (dc_link.c:1006-1069)
 *       returns TRANSMITTER_UNKNOWN (-1) for ANY unrecognized encoder id/enum
 *       pair (the default arms), TRANSMITTER_NUTMEG_CRT(6) in range,
 *       TRANSMITTER_TRAVIS_CRT(7) / TRAVIS_LCD(8) / UNIPHY_G(9) OUT of range.
 *   channel      = get_ddc_line()  (dc_link.c:964-1004)
 *       returns CHANNEL_ID_UNKNOWN(0) when the DDC pin is missing/unknown
 *       (-> index -1), CHANNEL_ID_DDC_VGA(7) (-> index 6) and
 *       CHANNEL_ID_I2C_PAD(8) (-> index 7) for valid enum mappings.
 *   hpd_source   = get_hpd_line()  (dc_link.c:925-962)
 *       returns HPD_SOURCEID_UNKNOWN(6) when no HPD GPIO exists
 *       (-> index 6 on a 6-entry table).
 *
 * All three originate from the VBIOS object tables (GPU ROM) parsed by the
 * BIOS parser. The stored OOB pointers ARE dereferenced later:
 *   AUX_REG(AUX_CONTROL)/AUX_REG(AUX_DPHY_RX_CONTROL0)
 *       dce_link_encoder.c:494,501  -> dm_read_reg/dm_write_reg
 *   HPD_REG(DC_HPD_CONTROL)
 *       dce_link_encoder.c:1370,1384 -> dm_read_reg/dm_write_reg
 * producing register accesses at addresses read from adjacent static data.
 *
 * Build:  cc -O2 -Wall -o df4319 df4319.c
 * Run:    ./df4319
 * Success criterion: prints the reachable OOB index set per table.
 */
#include <stdio.h>

/* enum values transcribed from sys/dev/drm/amd/display/include/grph_object_defs.h */
enum transmitter {
	TRANSMITTER_UNKNOWN_DF = -1,           /* (:82) */
	TRANSMITTER_UNIPHY_A,                  /* 0 */
	TRANSMITTER_UNIPHY_B,                  /* 1 */
	TRANSMITTER_UNIPHY_C,                  /* 2 */
	TRANSMITTER_UNIPHY_D,                  /* 3 */
	TRANSMITTER_UNIPHY_E,                  /* 4 */
	TRANSMITTER_UNIPHY_F,                  /* 5 */
	TRANSMITTER_NUTMEG_CRT,                /* 6 */
	TRANSMITTER_TRAVIS_CRT,                /* 7 */
	TRANSMITTER_TRAVIS_LCD,                /* 8 */
	TRANSMITTER_UNIPHY_G,                  /* 9 */
};
enum channel_id {
	CHANNEL_ID_UNKNOWN_DF = 0,             /* (:59) */
	CHANNEL_ID_DDC1,                       /* 1 */
	CHANNEL_ID_DDC2, CHANNEL_ID_DDC3, CHANNEL_ID_DDC4, CHANNEL_ID_DDC5,
	CHANNEL_ID_DDC6,                       /* 6 */
	CHANNEL_ID_DDC_VGA,                    /* 7 */
	CHANNEL_ID_I2C_PAD,                    /* 8 */
};
enum hpd_source_id {
	HPD_SOURCEID1_DF = 0,                  /* (:46) */
	HPD_SOURCEID2_DF, HPD_SOURCEID3_DF, HPD_SOURCEID4_DF,
	HPD_SOURCEID5_DF,
	HPD_SOURCEID6_DF,                      /* 5 */
	HPD_SOURCEID_COUNT_DF = 6,
	HPD_SOURCEID_UNKNOWN_DF = 6,           /* (:54) — alias of COUNT */
};

#define LINK_ENC_REGS_N     7   /* dce80_resource.c:249-257 */
#define LINK_ENC_AUX_REGS_N 6   /* dce80_resource.c:221-228 */
#define LINK_ENC_HPD_REGS_N 6   /* dce80_resource.c:235-242 */

static void check(const char *table, int idx, int n)
{
	printf("  %-34s index %3d  vs %d entries : %s\n", table, idx, n,
	       (idx < 0 || idx >= n) ? "*** OUT OF BOUNDS ***" : "ok");
}

int main(void)
{
	int tx[]  = { TRANSMITTER_UNKNOWN_DF, TRANSMITTER_UNIPHY_A,
		      TRANSMITTER_UNIPHY_F, TRANSMITTER_NUTMEG_CRT,
		      TRANSMITTER_TRAVIS_CRT, TRANSMITTER_TRAVIS_LCD,
		      TRANSMITTER_UNIPHY_G };
	int ch[]  = { CHANNEL_ID_UNKNOWN_DF, CHANNEL_ID_DDC1, CHANNEL_ID_DDC6,
		      CHANNEL_ID_DDC_VGA, CHANNEL_ID_I2C_PAD };
	int hpd[] = { HPD_SOURCEID1_DF, HPD_SOURCEID6_DF,
		      HPD_SOURCEID_UNKNOWN_DF };
	unsigned i;

	puts("dce80_link_encoder_create index reachability "
	     "(values DC core produces for VBIOS objects):");

	puts("\n[1] &link_enc_regs[transmitter]:");
	for (i = 0; i < sizeof(tx)/sizeof(tx[0]); i++)
		check("link_enc_regs[transmitter]", tx[i], LINK_ENC_REGS_N);

	puts("\n[2] &link_enc_aux_regs[channel - 1]:");
	for (i = 0; i < sizeof(ch)/sizeof(ch[0]); i++)
		check("link_enc_aux_regs[channel-1]", ch[i] - 1,
		      LINK_ENC_AUX_REGS_N);

	puts("\n[3] &link_enc_hpd_regs[hpd_source]:");
	for (i = 0; i < sizeof(hpd)/sizeof(hpd[0]); i++)
		check("link_enc_hpd_regs[hpd_source]", hpd[i],
		      LINK_ENC_HPD_REGS_N);

	puts("\nDereference sites of the stored OOB pointers:");
	puts("  dce_link_encoder.c:494  AUX_REG(AUX_CONTROL)          -> dm_read_reg/dm_write_reg");
	puts("  dce_link_encoder.c:501  AUX_REG(AUX_DPHY_RX_CONTROL0) -> dm_read_reg/dm_write_reg");
	puts("  dce_link_encoder.c:1370 HPD_REG(DC_HPD_CONTROL)       -> dm_read_reg/dm_write_reg");
	puts("  dce_link_encoder.c:1384 HPD_REG(DC_HPD_CONTROL)       -> dm_read_reg/dm_write_reg");
	return 0;
}