DF-3858 / od_div0.c
/* * DF-3858 PoC — arithmetic-level reproduction of the two defects in * vega20_get_sclk_od() / vega20_get_mclk_od() * (sys/dev/drm/amd/powerplay/hwmgr/vega20_hwmgr.c:1318-1381), * the DF-3686 / DF-3707 twins for Vega20. * * int value = sclk_table->dpm_levels[sclk_table->count - 1].value; :1326 * int golden_value = golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value; :1327-1328 * value -= golden_value; * value = DIV_ROUND_UP(value * 100, golden_value); :1332 <-- #DE * * Case A (divide-by-zero, DF-3707 twin): * DPM_GFXCLK feature disabled -> vega20_setup_gfxclk_dpm_table() takes the * boot-clock branch: count=1, dpm_levels[0].value = * data->vbios_boot_state.gfx_clock / 100 (:546), where gfx_clock is the raw * VBIOS bootup_sclk_in10khz copied with no zero check * (ppatomfwctrl.c:523/:556 -> vega20_hwmgr.c:745). A crafted/blank VBIOS * value of 0 makes golden_value == 0 -> DIV_ROUND_UP traps #DE. * Reach: world-readable sysfs pp_sclk_od / pp_mclk_od * (amdgpu_pm.c:1019-1022, S_IRUGO) -> amdgpu_dpm_get_sclk_od -> * pp_dpm_get_sclk_od (amd_powerplay.c:757, only !hwmgr||!pm_en guard; * pm_en is set at powerplay create time, amd_powerplay.c:51). * * Case B (count-1 unsigned underflow, wild OOB read, DF-3686 twin): * If dpm bring-up aborts inside vega20_setup_default_dpm_tables() * (e.g. SMU message failure), the failure is tolerated by the late_init * error path but dpm_table/golden_dpm_table stay the memset() zero copy * (vega20_hwmgr.c:588) -> count == 0 -> index (uint32_t)0 - 1 = 0xFFFFFFFF * -> dpm_levels[0xFFFFFFFF].value is a read at * offsetof(golden_dpm_table.gfx_table.dpm_levels) + 0xFFFFFFFF * 8 bytes * (~34 GB past the kzalloc'd vega20_hwmgr backend). If the wild read lands * on a zero, the DIV_ROUND_UP then also divides by zero -> #DE. * * This userspace replica reproduces both arithmetic facts: case A traps with * SIGFPE (signal 8 = the same #DE fault class the kernel would take), and * case B computes the effective wild index/offset and performs the verbatim * access (trapping SIGSEGV, proving the access is out of the object). */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <signal.h> #include <stdbool.h> #define MAX_REGULAR_DPM_NUMBER 16 #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) /* verbatim struct shapes from vega20_hwmgr.h:140-171 */ struct vega20_dpm_level { bool enabled; uint32_t value; }; struct vega20_single_dpm_table { uint32_t count; uint32_t dpm_state[4]; struct vega20_dpm_level dpm_levels[MAX_REGULAR_DPM_NUMBER]; }; static struct vega20_single_dpm_table table; /* stand-in for golden table */ /* verbatim vega20_get_sclk_od() body, vega20_hwmgr.c:1318-1335 */ static int get_sclk_od(void) { int value = table.dpm_levels[table.count - 1].value; /* :1326 */ int golden_value = table.dpm_levels[table.count - 1].value; /* :1327 */ value -= golden_value; value = DIV_ROUND_UP(value * 100, golden_value); /* :1332 */ return value; } int main(int argc, char **argv) { int mode = argc > 1 ? atoi(argv[1]) : 0; if (mode == 0) { /* Case A: DPM_GFXCLK disabled, VBIOS bootup_sclk_in10khz == 0 * -> count=1, dpm_levels[0].value = 0/100 = 0 (vega20_hwmgr.c:546) */ table.count = 1; table.dpm_levels[0].value = 0; printf("case A: golden top clock = 0 (VBIOS boot clock 0, " "DPM_GFXCLK disabled) -> DIV_ROUND_UP(x, 0)\n"); fflush(stdout); int v = get_sclk_od(); printf("UNEXPECTED: returned %d without trapping\n", v); return 1; } /* Case B: golden table still the memset-zero copy after aborted * vega20_setup_default_dpm_tables() -> count == 0 */ table.count = 0; printf("case B: golden gfx_table.count = 0 -> index (uint32_t)0-1 = %u\n", (uint32_t)(table.count - 1)); printf(" effective read offset = %llu bytes past dpm_levels[0] " "(elem size %zu)\n", (unsigned long long)(uint64_t)(table.count - 1) * sizeof(struct vega20_dpm_level), sizeof(struct vega20_dpm_level)); fflush(stdout); int v = get_sclk_od(); /* verbatim access: traps SIGSEGV or feeds 0/garbage * into the case-A divide */ printf("wild read returned %d (no trap; garbage leaked into OD math)\n", v); return 0; } |