โฌข DragonFlyBSD Kernel Audit
DF-4375 / df4375_poc.c
โ† back to finding โ†“ download raw
/*
 * DF-4375 PoC: dce112_link_encoder_create() const-table OOB index proof.
 *
 * Mirrors EXACTLY the index arithmetic of
 *   sys/dev/drm/amd/display/dc/dce112/dce112_resource.c:577-579
 *     &link_enc_regs[enc_init_data->transmitter]        (array of 7, dce112_resource.c:267-275)
 *     &link_enc_aux_regs[enc_init_data->channel - 1]    (array of 6, dce112_resource.c:239-246)
 *     &link_enc_hpd_regs[enc_init_data->hpd_source]     (array of 6, dce112_resource.c:253-260)
 * with the enum values from sys/dev/drm/amd/display/include/grph_object_defs.h
 * (transmitter/channel_id/hpd_source_id) that the DC core legitimately produces
 * in core/dc_link.c construct():
 *   transmitter = translate_encoder_to_transmitter()   (dc_link.c:1167-1168, 1006-1071)
 *     -> TRANSMITTER_UNKNOWN(-1) | TRAVIS_CRT(7) | TRAVIS_LCD(8) | UNIPHY_G(9)
 *   channel = get_ddc_line()                            (dc_link.c:1166, 964-1004)
 *     -> CHANNEL_ID_UNKNOWN(0) | DDC_VGA(7) | I2C_PAD(8)
 *   hpd_source = get_hpd_line()                         (dc_link.c:1165, 925-962)
 *     -> HPD_SOURCEID_UNKNOWN(7)
 *
 * The arrays here are instrumented (via -fsanitize=address): same sizes as the
 * real const tables. Indexing with the values above reads out of bounds โ€”
 * exactly what the kernel does with a VBIOS that describes such an encoder
 * (flashed/crafted VBIOS, or a dock/firmware enumerating TRAVIS/NUTMEG parts).
 *
 * Build:  cc -O1 -g -fsanitize=address -o df4375_poc df4375_poc.c
 * Run:    ./df4375_poc
 * Success criterion: AddressSanitizer reports heap-buffer-overflow (OOB read)
 * for every value pair listed below; the "in-bounds" sanity rows pass clean.
 */
#include <stdio.h>
#include <stdlib.h>

/* ===== grph_object_defs.h (verbatim values) ===== */
enum transmitter_x {
	TX_UNKNOWN = -1, TX_A = 0, TX_B, TX_C, TX_D, TX_E, TX_F,
	TX_NUTMEG_CRT, TX_TRAVIS_CRT, TX_TRAVIS_LCD, TX_UNIPHY_G
};
enum channel_x {
	CH_UNKNOWN = 0, CH_DDC1, CH_DDC2, CH_DDC3, CH_DDC4, CH_DDC5, CH_DDC6,
	CH_DDC_VGA, CH_I2C_PAD
};
enum hpd_x {
	HPD1 = 0, HPD2, HPD3, HPD4, HPD5, HPD6, HPD_COUNT, HPD_UNKNOWN
};

/* same element counts as dce112_resource.c:267-275 / 239-246 / 253-260 */
#define LINK_ENC_REGS_N   7
#define LINK_ENC_AUX_N    6
#define LINK_ENC_HPD_N    6

struct dummy_regs { unsigned long r[16]; };

static struct dummy_regs link_enc_regs   [LINK_ENC_REGS_N];
static struct dummy_regs link_enc_aux_regs[LINK_ENC_AUX_N];
static struct dummy_regs link_enc_hpd_regs[LINK_ENC_HPD_N];

/* heap copies so ASan can see OOB past the end of the allocation */
static struct dummy_regs *heap_link_enc, *heap_aux, *heap_hpd;

static volatile unsigned long g_sink;

static const char *tx_name(int t)
{
	switch (t) {
	case TX_UNKNOWN:    return "TRANSMITTER_UNKNOWN";
	case TX_A: case TX_B: case TX_C: case TX_D: case TX_E: case TX_F:
	case TX_NUTMEG_CRT: return "(in-bounds transmitter)";
	case TX_TRAVIS_CRT: return "TRANSMITTER_TRAVIS_CRT";
	case TX_TRAVIS_LCD: return "TRANSMITTER_TRAVIS_LCD";
	case TX_UNIPHY_G:   return "TRANSMITTER_UNIPHY_G";
	}
	return "?";
}

static void probe(int transmitter, int channel, int hpd_source, int expect_oob)
{
	printf("transmitter=%2d (%s)\n", transmitter, tx_name(transmitter));

	/* dce112_resource.c:577 โ€” &link_enc_regs[transmitter], array[7] */
	printf("  link_enc_regs[%d]  (array size %d) %s\n", transmitter,
	    LINK_ENC_REGS_N,
	    (transmitter < 0 || transmitter >= LINK_ENC_REGS_N) ? "<-- OOB" : "ok");
	/* touch it (heap copy = ASan-visible) */
	struct dummy_regs *p1 = &heap_link_enc[transmitter];
	g_sink += p1->r[0]; /* kernel READS the const-table fields (register offsets) */

	/* dce112_resource.c:578 โ€” &link_enc_aux_regs[channel - 1], array[6] */
	int idx2 = channel - 1;
	printf("  link_enc_aux_regs[%d (%d-1)] (array size %d) %s\n", idx2, channel,
	    LINK_ENC_AUX_N,
	    (idx2 < 0 || idx2 >= LINK_ENC_AUX_N) ? "<-- OOB" : "ok");
	struct dummy_regs *p2 = &heap_aux[idx2];
	g_sink += p2->r[0];

	/* dce112_resource.c:579 โ€” &link_enc_hpd_regs[hpd_source], array[6] */
	printf("  link_enc_hpd_regs[%d]  (array size %d) %s\n", hpd_source,
	    LINK_ENC_HPD_N,
	    (hpd_source < 0 || hpd_source >= LINK_ENC_HPD_N) ? "<-- OOB" : "ok");
	struct dummy_regs *p3 = &heap_hpd[hpd_source];
	g_sink += p3->r[0];

	if (expect_oob) {
		printf("  => kernel equivalent reads a struct beyond the const table;\n"
		       "     stored as the encoder's register base (wild MMIO offsets)\n\n");
	} else {
		printf("  => all in bounds (sanity row)\n\n");
	}
}

int main(void)
{
	heap_link_enc = calloc(LINK_ENC_REGS_N, sizeof(struct dummy_regs));
	heap_aux      = calloc(LINK_ENC_AUX_N,  sizeof(struct dummy_regs));
	heap_hpd      = calloc(LINK_ENC_HPD_N,  sizeof(struct dummy_regs));
	if (!heap_link_enc || !heap_aux || !heap_hpd)
		return 1;

	/* sanity: the values a well-formed Polaris VBIOS produces */
	probe(TX_F, CH_DDC6, HPD6, 0);

	/* the values the DC core legitimately produces and hands to dce112 */
	probe(TX_UNKNOWN, CH_DDC6, HPD6, 1);          /* translate_encoder default */
	probe(TX_UNIPHY_G, CH_DDC1, HPD1, 1);          /* ENCODER_ID_INTERNAL_UNIPHY3 */
	probe(TX_TRAVIS_CRT, CH_DDC_VGA, HPD_UNKNOWN, 1); /* external TRAVIS encoders */
	probe(TX_TRAVIS_LCD, CH_I2C_PAD, HPD_UNKNOWN, 1);
	probe(TX_A, CH_UNKNOWN, HPD_UNKNOWN, 1);      /* get_ddc_line/get_hpd_line defaults */

	printf("DONE: every OOB row above is an out-of-bounds read in\n"
	       "dce112_resource.c:577-579 (see ASan report; exit code follows)\n");
	return 0;
}