DF-3686 / zero_count_underflow.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 | /* * DF-3686 harness: zero-count DPM table -> wild index (uint32_t)(0-1) in * smu7_find_dpm_states_clocks_in_dpm_table, reproduced verbatim from * sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:3588-3618. * * Precondition chain: * 1. processpptables.c init_clock_voltage_dependency (:1304-1310) skips * vddc_dependency_on_sclk when usVddcDependencyOnSCLKOffset == 0 -> * dyn_state.vddc_dependency_on_sclk stays NULL (parse still succeeds). * 2. smu7_setup_dpm_tables_v0 (:677-678) returns -EINVAL on the NULL * table, but smu7_setup_default_dpm_tables (:950-953) IGNORES the * return value -> dpm_table.sclk_table.count stays 0. * 3. Any power-state change calls smu7_find_dpm_states_clocks_in_dpm_table * -> `for (uint32_t i = 0; i < sclk_table->count; i++)` exits with * i == 0, `i >= count` is true, and `dpm_levels[i - 1]` uses the * UNSIGNED index (uint32_t)0 - 1 = 0xFFFFFFFF. Pointer arithmetic * scales by sizeof(struct smu7_dpm_level) = 12 -> the kernel READS and * (if the garbage compares less than the requested SCLK) WRRITES at * dpm_levels + 0xFFFFFFFF*12 = +51,539,607,540 bytes (~48 GB out). * * The same pattern repeats at :3613-3616 for the MCLK table, at :2740 * (smu7_get_profiling_clk, dpm_levels[count-1] read), :3642-3645 * (smu7_get_maximum_link_speed, pcie dpm_levels[count-1] read), * :4534-4536 / :4576-4578 (smu7_get_sclk_od/mclk_od read + DIV_ROUND_UP), * :4745-4750 (smu7_get_max_high_clocks) and :4487/:4490 (OD_RANGE print). * * Run modes: * ./t - safe accounting mode: reports the computed index/offset * ./t crash - executes the verbatim kernel access and dies with SIGSEGV * (kernel equivalent: page fault in kernel mode -> panic) */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h> #include <stddef.h> #define MAX_REGULAR_DPM_NUMBER 8 struct smu7_dpm_level { uint8_t enabled; uint32_t value; uint32_t param1; }; struct smu7_single_dpm_table { uint32_t count; struct smu7_dpm_level dpm_levels[MAX_REGULAR_DPM_NUMBER]; }; struct smu7_dpm_table_head { /* the two first tables of struct smu7_dpm_table */ struct smu7_single_dpm_table sclk_table; struct smu7_single_dpm_table mclk_table; }; /* ---- verbatim loop from smu7_hwmgr.c:3588-3597 ---- */ static void find_dpm_states_clocks(struct smu7_dpm_table_head *dpm_table, uint32_t sclk) { struct smu7_single_dpm_table *sclk_table = &dpm_table->sclk_table; uint32_t i; for (i = 0; i < sclk_table->count; i++) { if (sclk == sclk_table->dpm_levels[i].value) break; } if (i >= sclk_table->count) { if (sclk > sclk_table->dpm_levels[i - 1].value) { sclk_table->dpm_levels[i - 1].value = sclk; /* WILD WRITE */ } } } /* ---- end verbatim ---- */ int main(int argc, char **argv) { struct smu7_dpm_table_head *dpm = calloc(1, sizeof(*dpm)); uint32_t i = 0; if (!dpm) return 1; setbuf(stdout, NULL); printf("sizeof(struct smu7_dpm_level) = %zu\n", sizeof(struct smu7_dpm_level)); printf("sclk_table.count = 0 (setup_dpm_tables_v0 failed; return ignored at :950-953)\n"); /* the exact index the kernel computes at :3594 with count == 0 */ printf("loop exits with i = %u; i - 1 = 0x%08x\n", i, i - 1); printf("effective byte offset of dpm_levels[i-1] = 0x%08x * %zu = %llu bytes\n", i - 1, sizeof(struct smu7_dpm_level), (unsigned long long)(i - 1) * sizeof(struct smu7_dpm_level)); if (argc > 1 && !strcmp(argv[1], "crash")) { printf("executing the verbatim kernel access now; expect SIGSEGV " "(kernel: page fault -> panic)\n"); find_dpm_states_clocks(dpm, 300000); printf("UNEXPECTED: survived\n"); return 1; } printf("RESULT: WILD OOB READ+WRITE CONFIRMED at the arithmetic level - " "index 0xFFFFFFFF scales to +51,539,607,540 bytes past dpm_levels; " "same pattern at :3613-3616 (mclk), :2740, :3642-3645, :4534-4536, " ":4576-4578, :4745-4750, :4487/:4490\n"); return 0; } |