DF-4666 / find_closest_vddci_zero.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 | /* * DF-4666 harness โ verbatim kernel function + 1:1 struct layout. * * Bug: sys/dev/drm/amd/powerplay/hwmgr/smu_helper.c:426-437 * phm_find_closest_vddci() is the ONLY member of its family with no * empty-table guard: * phm_get_voltage_index (:393) asserts count != 0 * phm_get_voltage_id (:413) asserts count != 0 * phm_find_closest_vddci (:426) NOTHING * With count == 0 the loop never runs and the fallback * return vddci_table->entries[i-1].value; (i == 0, uint32_t) * reads entries[0xFFFFFFFF] => base + 0xFFFFFFFF*8 = +34,359,738,360 * (~32 GiB) โ wild kernel read, guaranteed fault on any real mapping. * * count==0 provenance (crafted VBIOS, all in-tree): * ppatomctrl.c:546-563 atomctrl_get_voltage_table_v3 only asserts * ucGpioEntryNum <= PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES(32) โ 0 passes; * it then sets voltage_table->count = ucGpioEntryNum = 0 and returns 0. * smu7_hwmgr.c:276-282 accepts this (result == 0) when * vddci_control == SMU7_VOLTAGE_CONTROL_BY_GPIO. * smu7_hwmgr.c:337-342 only TRIMS when count > SMU_MAX_LEVELS_VDDCI. * Later, polaris10_smumgr.c:377/402 (also vegam_smumgr.c:626/646/1227/1341, * fiji_smumgr.c:380/405) call phm_find_closest_vddci whenever a dep-table * entry has vddci == 0 and vddci_control != NONE => wild read. * * Modes: * safe โ compute the exact wild address without dereferencing. * deref โ actually run the verbatim function (dies SIGSEGV). * normal โ control: populated table, normal path returns sane value. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <sys/wait.h> #include <unistd.h> /* ---- verbatim: ppatomctrl.h:45,200-213 ---- */ #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]; }; /* ---- VERBATIM: smu_helper.c:426-437 ---- */ uint16_t phm_find_closest_vddci(struct pp_atomctrl_voltage_table *vddci_table, uint16_t vddci) { uint32_t i; for (i = 0; i < vddci_table->count; i++) { if (vddci_table->entries[i].value >= vddci) return vddci_table->entries[i].value; } printf("vddci is larger than max value in vddci_table\n"); return vddci_table->entries[i-1].value; } /* ---- end verbatim ---- */ int main(int argc, char **argv) { const char *mode = (argc > 1) ? argv[1] : "safe"; struct pp_atomctrl_voltage_table *t = calloc(1, sizeof(*t)); if (!t) { perror("calloc"); exit(1); } if (!strcmp(mode, "normal")) { /* control: GPIO LUT with 4 entries; nothing exceeds request */ t->count = 4; for (uint32_t k = 0; k < 4; k++) t->entries[k].value = (uint16_t)(800 + 10 * k); uint16_t r = phm_find_closest_vddci(t, 9999); printf("[normal] count=4 -> returns entries[3].value = %u (in bounds)\n", r); return 0; } /* the crafted-VBIOS state: ucGpioEntryNum == 0 -> count == 0 */ t->count = 0; if (!strcmp(mode, "deref")) { uint16_t r; printf("[deref] calling verbatim phm_find_closest_vddci(count=0)...\n"); fflush(stdout); r = phm_find_closest_vddci(t, 1000); /* not reached on failure */ printf("[deref] SURVIVED, returned %u (unexpected)\n", r); return 1; } /* safe: exact address math for the fallback read, no deref */ { uint32_t i = t->count; /* == 0 after the loop */ char *base = (char *)&t->entries[0]; char *wild = (char *)&t->entries[(uint32_t)(i - 1)]; printf("[safe] count=%u -> fallback reads entries[i-1] = entries[0x%08x]\n", t->count, (uint32_t)(i - 1)); printf("[safe] entries base = %p\n", base); printf("[safe] wild read address = %p\n", wild); printf("[safe] effective byte offset = %llu (0x%llx)\n", (unsigned long long)(wild - base), (unsigned long long)(wild - base)); printf("[safe] entry size = %zu -> offset = 0xFFFFFFFF * %zu\n", sizeof(struct pp_atomctrl_voltage_table_entry), sizeof(struct pp_atomctrl_voltage_table_entry)); printf("WILD-READ CONFIRMED (address math): kernel would read ~32 GiB " "past the heap object -> non-canonical/unmapped -> page fault panic\n"); } return 0; } |