DF-1797 / harness.c
/* * DF-1797 source-confirmation harness (smu_helper phm_get_svi2_* OOB write). * * The buggy kernel code lives in AMD GPU PowerPlay init and is only * reachable with AMD GPU hardware whose VBIOS advertises a voltage * dependency/lookup table with count > 32. This harness reproduces the * index-arithmetic primitive: the function writes entries[i] for * i = 0..count-1 into a fixed array of 32 entries without checking count. * * sys/dev/drm/amd/powerplay/hwmgr/smu_helper.c:258,286,314 sets * vol_table->count = dep_table->count; * then loops i=0..count-1 writing vol_table->entries[i] (lines 260-263, * 288-291, 316-319) into entries[PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES=32] * (ppatomctrl.h:211). Compare ppatomctrl.c:546-551 which DOES bound * ucGpioEntryNum <= 32 before its loop. * * Build: cc -O2 -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdlib.h> #include <string.h> #define PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES 32 /* ppatomctrl.h:45 */ struct entry { unsigned int value, smio_low; }; struct pp_atomctrl_voltage_table { unsigned int count; unsigned int mask_low, phase_delay; struct entry entries[PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES]; }; /* Model of phm_get_svi2_vdd_voltage_table (smu_helper.c:300-322): * NO upper-bound check on lookup->count before writing entries[i]. */ static void phm_get_svi2_vdd_voltage_table_model(struct pp_atomctrl_voltage_table *vol, unsigned int count, const struct entry *src) { int i; vol->mask_low = 0; vol->phase_delay = 0; vol->count = count; /* line 314 */ for (i = 0; i < (int)vol->count; i++) /* line 316 */ vol->entries[i] = src[i]; /* line 317 — OOB when count>32 */ } int main(void) { /* Allocate vol followed by a sentinel byte-region to detect OOB. */ unsigned char *blob = calloc(1, sizeof(struct pp_atomctrl_voltage_table) + 1024); struct pp_atomctrl_voltage_table *vol = (void *)blob; unsigned char *sentinel = blob + sizeof(struct pp_atomctrl_voltage_table); for (int i = 0; i < 1024; i++) sentinel[i] = 0xCC; /* Malicious VBIOS lookup table: 64 entries. */ unsigned int count = 64; struct entry src[64]; for (int i = 0; i < 64; i++) { src[i].value = 1100+i; src[i].smio_low = 0; } phm_get_svi2_vdd_voltage_table_model(vol, count, src); /* Count sentinel bytes that got overwritten by the OOB entries. */ int corrupted = 0; for (int i = 0; i < 1024; i++) if (sentinel[i] != 0xCC) corrupted++; printf("DF-1797: phm_get_svi2_vdd_voltage_table wrote %d entries past " "entries[32] (count=%u max=%d) -> %d bytes of trailing memory corrupted " "(sentinel bytes touched=%d)\n", (int)count - PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES, vol->count, PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES, (int)(count - PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES) * (int)sizeof(struct entry), corrupted); printf("Matches smu_helper.c:314 (count=lookup->count) + :317 (entries[i]=..) " "with no bound; contrast ppatomctrl.c:546-551 which DOES check.\n"); free(blob); return 0; } |