DF-4352 / df4352.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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | /* * DF-4352 model proof - dce110_resource.c (DCE11 / Carrizo-Stoney) * ---------------------------------------------------------------- * dce110_link_encoder_create() (sys/dev/drm/amd/display/dc/dce110/ * dce110_resource.c:576-592) performs three const-table lookups using * VBIOS-derived enums with no validation: * * &link_enc_regs[enc_init_data->transmitter], // 7 entries (:588) * &link_enc_aux_regs[enc_init_data->channel - 1], // 6 entries (:589) * &link_enc_hpd_regs[enc_init_data->hpd_source] // 6 entries (:590) * * The three values are produced at link bring-up (core/dc_link.c): * transmitter : translate_encoder_to_transmitter() :1006-1024+ returns * TRANSMITTER_UNKNOWN (-1) for unrecognized encoder ids, * and NUTMEG/TRAvis/UNIPHY_G map to 6..9; * channel : get_ddc_line() :964-1004 returns * CHANNEL_ID_UNKNOWN (0) by default, DDC_VGA (7), * I2C_PAD (8) explicitly; * hpd_source : get_hpd_line() :925-962 returns * HPD_SOURCEID_UNKNOWN (7) by default. * * An out-of-range index reads a bogus 'struct dce110_link_enc_registers' * (or aux/hpd regs) from adjacent .rodata; dce110_link_encoder_construct * stores that pointer and every later REG_READ and REG_UPDATE on the * encoder dereferences it as an MMIO register base -> VBIOS-steerable * wild MMIO offsets (bring-up hang / machine check at minimum). * * Twin of DF-4312 (dcn10) / DF-4319 (dce80) at the dce110 instance, * with different table sizes (7/6/6) and a -1 shift on channel. * * This model reproduces the three index expressions against the real * enum domains and reports every OOB case. */ #include <stdio.h> /* enum domains verbatim from include/grph_object_defs.h:45-94 */ enum hpd_source_id { HPD_SOURCEID1 = 0, HPD_SOURCEID2, HPD_SOURCEID3, HPD_SOURCEID4, HPD_SOURCEID5, HPD_SOURCEID6, HPD_SOURCEID_COUNT, /* 6 */ HPD_SOURCEID_UNKNOWN /* 7 */ }; 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, /* 7 */ CHANNEL_ID_I2C_PAD, /* 8 */ CHANNEL_ID_COUNT }; enum transmitter { TRANSMITTER_UNKNOWN = (-1L), /* -1 */ TRANSMITTER_UNIPHY_A, /* 0 */ TRANSMITTER_UNIPHY_B, TRANSMITTER_UNIPHY_C, TRANSMITTER_UNIPHY_D, TRANSMITTER_UNIPHY_E, TRANSMITTER_UNIPHY_F, TRANSMITTER_NUTMEG_CRT, /* 6 */ TRANSMITTER_TRAVIS_CRT, /* 7 */ TRANSMITTER_TRAVIS_LCD, /* 8 */ TRANSMITTER_UNIPHY_G, /* 9 */ TRANSMITTER_COUNT }; #define link_enc_regs_N 7 /* dce110_resource.c:259-267 */ #define link_enc_aux_regs_N 6 /* dce110_resource.c:230-237 */ #define link_enc_hpd_regs_N 6 /* dce110_resource.c:244-251 */ static int oob(long idx, long n, const char *what, const char *val) { if (idx < 0 || idx >= n) { printf(" OOB %-24s = %-3ld -> index %ld into %d-entry table\n", what, idx, idx, (int)n); printf(" value: %s\n", val); return 1; } return 0; } int main(void) { int bad = 0, tested = 0; printf("DF-4352 model: dce110_link_encoder_create const-table index " "domains\n"); printf("lookup expressions verbatim from dce110_resource.c:588-590\n"); printf("table sizes: link_enc_regs=%d, link_enc_aux_regs=%d, " "link_enc_hpd_regs=%d\n\n", link_enc_regs_N, link_enc_aux_regs_N, link_enc_hpd_regs_N); /* ---- transmitter domain (dc_link.c translate_encoder_to_transmitter) ---- */ { long dom[] = { TRANSMITTER_UNKNOWN, 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 }; const char *nm[] = { "TRANSMITTER_UNKNOWN (unrecognized encoder id)", "UNIPHY_A", "UNIPHY_B", "UNIPHY_C", "UNIPHY_D", "UNIPHY_E", "UNIPHY_F", "NUTMEG_CRT (external enc)", "TRAVIS_CRT (external enc)", "TRAVIS_LCD (external enc)", "UNIPHY_G" }; printf("link_enc_regs[transmitter]:\n"); for (unsigned i = 0; i < sizeof(dom)/sizeof(dom[0]); i++) { tested++; bad += oob(dom[i], link_enc_regs_N, "transmitter", nm[i]); } } /* ---- channel domain (dc_link.c get_ddc_line) ---- */ { long dom[] = { CHANNEL_ID_UNKNOWN, 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 }; const char *nm[] = { "CHANNEL_ID_UNKNOWN (default when no ddc pin)", "DDC1", "DDC2", "DDC3", "DDC4", "DDC5", "DDC6", "CHANNEL_ID_DDC_VGA (VGA connector)", "CHANNEL_ID_I2C_PAD (I2C pad connector)" }; printf("\nlink_enc_aux_regs[channel - 1]:\n"); for (unsigned i = 0; i < sizeof(dom)/sizeof(dom[0]); i++) { tested++; bad += oob(dom[i] - 1, link_enc_aux_regs_N, "channel-1", nm[i]); } } /* ---- hpd domain (dc_link.c get_hpd_line) ---- */ { long dom[] = { HPD_SOURCEID1, HPD_SOURCEID2, HPD_SOURCEID3, HPD_SOURCEID4, HPD_SOURCEID5, HPD_SOURCEID6, HPD_SOURCEID_COUNT, HPD_SOURCEID_UNKNOWN }; const char *nm[] = { "HPD_SOURCEID1", "HPD_SOURCEID2", "HPD_SOURCEID3", "HPD_SOURCEID4", "HPD_SOURCEID5", "HPD_SOURCEID6", "HPD_SOURCEID_COUNT", "HPD_SOURCEID_UNKNOWN (default: no HPD gpio)" }; printf("\nlink_enc_hpd_regs[hpd_source]:\n"); for (unsigned i = 0; i < sizeof(dom)/sizeof(dom[0]); i++) { tested++; bad += oob(dom[i], link_enc_hpd_regs_N, "hpd_source", nm[i]); } } printf("\n%d of %d reachable enum values index out of bounds\n", bad, tested); if (bad > 0) printf("CONST-TABLE OOB READ CONFIRMED (model): each OOB case reads " "adjacent .rodata that becomes the encoder's MMIO register " "base (wild MMIO on every later REG_*)\n"); return bad ? 0 : 1; } |