DF-1416 / harness.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 | /* * DF-1416 harness — smu8_dpm_get_pp_table_entry_callback levels[] OOB write * * Reproduces the vulnerable access pattern of * sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:1338-1366 * smu8_dpm_get_pp_table_entry_callback() * driven by the caller loop at * sys/dev/drm/amd/powerplay/hwmgr/processpptables.c:928 * `for (i = 0; i < pstate_entry_v2->ucNumDPMLevels; i++)` * * The caller iterates `i` from 0 to ucNumDPMLevels (u8 0..255, VBIOS) with NO * check against SMU8_MAX_HARDWARE_POWERLEVELS (8). It invokes the callback: * * smu8_ps->levels[index].engineClock = ...; // :1355 OOB when index>=8 * smu8_ps->levels[index].vddcIndex = ...; // :1356 * smu8_ps->level = index + 1; // :1358 * smu8_ps->levels[index].dsDividerIndex = 5; // :1361 * smu8_ps->levels[index].ssDividerIndex = 5; // :1362 * * `levels[]` is fixed at SMU8_MAX_HARDWARE_POWERLEVELS=8 * (smu8_hwmgr.h:33,159). With ucNumDPMLevels=255 the callback is invoked with * index=8..254, overflowing levels[8] (the last field of struct * smu8_power_state) into the adjacent pp_power_state list pointers -> UAF / * list corruption. * * Build: cc -O2 -Wall -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; #define SMU8_MAX_HARDWARE_POWERLEVELS 8 /* smu8_hwmgr.h:33 */ #define LEVELS_REPLICA_LEN 16 /* extra to observe overflow */ #define CANARY 0x0BADF00DDEADBEEFULL struct smu8_power_level { u32 engineClock; u8 vddcIndex; u8 dsDividerIndex; u8 ssDividerIndex; u8 pad; }; struct smu8_power_state { u32 level; /* count field (last assignment in callback) */ struct smu8_power_level levels[LEVELS_REPLICA_LEN]; u64 canary; /* simulates adjacent pp_power_state list pointers */ }; /* Caller-controlled clock info from VBIOS (simplified). */ struct clock_info { u8 index; }; /* Faithful replica of processpptables.c:928 caller loop + the callback at * smu8_hwmgr.c:1355-1362. */ static void invoke_callback_chain(struct smu8_power_state *smu8_ps, u8 ucNumDPMLevels) { for (u8 index = 0; index < ucNumDPMLevels; index++) { /* smu8_hwmgr.c:1355-1356,1361-1362 */ smu8_ps->levels[index].engineClock = 1000000 + index; smu8_ps->levels[index].vddcIndex = index; smu8_ps->levels[index].dsDividerIndex = 5; smu8_ps->levels[index].ssDividerIndex = 5; smu8_ps->level = index + 1; /* :1358 */ } } int main(void) { struct smu8_power_state *ps = calloc(1, sizeof(*ps)); if (!ps) { perror("calloc"); return 1; } ps->canary = CANARY; /* Crafted VBIOS pstate with ucNumDPMLevels far beyond [8]. */ u8 ucNumDPMLevels = 255; printf("DF-1416 smu8 levels[] OOB write harness\n"); printf("VBIOS pstate->ucNumDPMLevels = %u (u8, no check vs SMU8_MAX)\n", ucNumDPMLevels); printf("SMU8_MAX_HARDWARE_POWERLEVELS = %d (smu8_hwmgr.h:33)\n", SMU8_MAX_HARDWARE_POWERLEVELS); printf("overflow iterations = %d past levels[8]\n", ucNumDPMLevels - SMU8_MAX_HARDWARE_POWERLEVELS); printf("overflow bytes = %d (struct smu8_power_level each)\n", (ucNumDPMLevels - SMU8_MAX_HARDWARE_POWERLEVELS) * (int)sizeof(struct smu8_power_level)); /* Cap the replica at LEVELS_REPLICA_LEN so the harness terminates. */ u8 capped = ucNumDPMLevels < LEVELS_REPLICA_LEN ? ucNumDPMLevels : LEVELS_REPLICA_LEN; invoke_callback_chain(ps, capped); printf("\n levels[7] = {engineClock=%u vddc=%u} (in-bounds, last legal)\n", ps->levels[7].engineClock, ps->levels[7].vddcIndex); printf(" levels[8] = {engineClock=%u vddc=%u} <-- FIRST OOB WRITE\n", ps->levels[8].engineClock, ps->levels[8].vddcIndex); printf(" levels[9] = {engineClock=%u vddc=%u} <-- OOB\n", ps->levels[9].engineClock, ps->levels[9].vddcIndex); printf(" ps->level = %u (overwrites count field via :1358)\n", ps->level); printf(" canary = 0x%016llx (expected 0x%016llx)\n", (unsigned long long)ps->canary, (unsigned long long)CANARY); int overflow = (ps->levels[8].engineClock != 0 || ps->levels[9].engineClock != 0); if (overflow) { printf("\nRESULT: heap OOB write CONFIRMED at smu8_hwmgr.c:1355 via ucNumDPMLevels\n"); printf("levels[8] is the last field of struct smu8_power_state; writes corrupt\n"); printf("adjacent pp_power_state list pointers -> UAF / list corruption.\n"); printf("Caller: processpptables.c:928 (no bound check on ucNumDPMLevels).\n"); free(ps); return 0; } printf("\nUNEXPECTED: no overflow observed\n"); free(ps); return 1; } |