DF-4054 / trigger.c
/* * DF-4054 replica — vega12_setup_single_dpm_table fill loop, host-side. * * Reproduces the exact store pattern of * sys/dev/drm/amd/powerplay/hwmgr/vega12_hwmgr.c:507-530 with the real * struct geometry from vega12_hwmgr.h (MAX_REGULAR_DPM_NUMBER=16, * struct vega12_dpm_level {bool enabled; uint32 value; uint32 param1;}). * * The SMU-fed num_of_levels (smum_get_argument -> raw uint32 from the * firmware response register, only checked > 0 at :481) becomes * dpm_table->count at :518 and the loop stores dpm_levels[i] for * i < num_of_levels at :520-527 — past dpm_levels[16]. * * This replica proves the arithmetic: with num_of_levels=20 the loop * writes 4 x 12 bytes past the end of soc_table.dpm_levels[16] into the * following struct members (gfx_table.count / dpm_state / dpm_levels on * real hardware; the guard band in this replica catches it). On the * driver the same walk continues through the whole kzalloc'd * struct vega12_hwmgr backend and past its end for large firmware values. * * Build: cc -O2 -Wall -o trigger trigger.c * Run: ./trigger [num_of_levels] */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <stdbool.h> #define MAX_REGULAR_DPM_NUMBER 16 struct vega12_dpm_state { uint32_t soft_min_level; uint32_t soft_max_level; uint32_t hard_min_level; uint32_t hard_max_level; }; struct vega12_dpm_level { bool enabled; /* vega12_hwmgr.h:90 */ uint32_t value; /* :91 */ uint32_t param1; /* :92 */ }; struct vega12_single_dpm_table { /* vega12_hwmgr.h:107 */ uint32_t count; struct vega12_dpm_state dpm_state; struct vega12_dpm_level dpm_levels[MAX_REGULAR_DPM_NUMBER]; }; struct vega12_dpm_table { /* vega12_hwmgr.h:125 */ struct vega12_single_dpm_table soc_table; struct vega12_single_dpm_table gfx_table; struct vega12_single_dpm_table mem_table; /* ... 7 more tables + pcie_table on real hardware ... */ }; #define GUARD 128 static unsigned char backing[sizeof(struct vega12_dpm_table) + GUARD]; static unsigned char *guard_start; int main(int argc, char **argv) { struct vega12_dpm_table *tbl = (void *)backing; uint32_t num_of_levels = (argc > 1) ? (uint32_t)strtoul(argv[1], NULL, 0) : 20; uint32_t i, over = 0; memset(backing, 0, sizeof(backing)); guard_start = backing + sizeof(struct vega12_dpm_table); memset(guard_start, 0xAA, GUARD); /* post-object guard band */ printf("sizeof(struct vega12_dpm_table)=%zu soc.dpm_levels at +%zu ends at +%zu\n", sizeof(struct vega12_dpm_table), (size_t)((unsigned char *)&tbl->soc_table.dpm_levels[0] - backing), (size_t)((unsigned char *)&tbl->soc_table.dpm_levels[MAX_REGULAR_DPM_NUMBER] - backing)); printf("SMU-fed num_of_levels = %u (checked only >0 by the driver)\n", num_of_levels); tbl->soc_table.count = num_of_levels; /* :518 */ for (i = 0; i < num_of_levels; i++) { /* :520 */ uint32_t clk = 0xdeadbe00 + i; /* smum_get_argument() */ tbl->soc_table.dpm_levels[i].value = clk; /* :525 */ tbl->soc_table.dpm_levels[i].enabled = true; /* :526 */ } /* count stores that landed past the end of the backing object */ for (i = 0; i < GUARD; i++) if (guard_start[i] != 0xAA) over++; printf("stores executed: %u x {enabled=true,value=SMU-fed}\n", num_of_levels); printf("bytes written past end of object (guard band): %u %s\n", over, over ? "<-- HEAP OVERFLOW (replica)" : ""); printf("in-object cross-field smash: gfx_table.count = 0x%08x " "(expected 0; = value store %u)\n", tbl->gfx_table.count, 16 /* entry 16 lands on gfx_table.count */); return (over || tbl->gfx_table.count) ? 0 : 1; } |