DF-4495 / df4495.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 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | /* * 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; } |