DF-4375 / df4375_poc.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | /* * 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; } |