DF-1182 / harness.c
/* * DF-1182 harness: demonstrates the missing bounds check on the * state-record clock indices (ucSocClockIndexLow/High, * ucGfxClockIndexLow/High, ucMemClockIndexLow/High — each u8 0-255) * used to index into the VBIOS dependency tables in * vega10_get_pp_table_entry_callback_func() * at vega10_hwmgr.c:2970-3063. * * The function is reached only when amdgpu powerplay enumerates power * states from the VBIOS PowerPlay table — runs only on a real Vega10 GPU. * * Confirmed sites (vega10_hwmgr.c): * 3042-3043: socclk_dep_table->entries[state_entry->ucSocClockIndexLow].ulClk * 3044-3045: gfxclk_dep_table->entries[state_entry->ucGfxClockIndexLow].ulClk * 3046-3047: mclk_dep_table->entries[state_entry->ucMemClockIndexLow].ulMemClk * 3051-3052: socclk_dep_table->entries[state_entry->ucSocClockIndexHigh].ulClk * 3054-3055 / 3057-3058: gfxclk_dep_table->entries[state_entry->ucGfxClockIndexHigh] * 3061-3062: mclk_dep_table->entries[state_entry->ucMemClockIndexHigh].ulMemClk * * Same class as DF-1168 (smu7). The read values become the power-state * clocks and are exposed via sysfs. * * Compile: cc -O2 -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> struct dep_record { uint8_t vddInd; uint8_t pad[3]; uint32_t clk; }; struct dep_table { uint8_t ucNumEntries; uint8_t pad[3]; struct dep_record entries[1]; }; static struct dep_table *alloc_dep(unsigned n) { size_t sz = sizeof(uint32_t) + sizeof(struct dep_record) * n; struct dep_table *t = calloc(1, sz); if (!t) { perror("calloc"); exit(1); } t->ucNumEntries = (uint8_t)n; for (unsigned i = 0; i < n; i++) t->entries[i].clk = 1000 + i; return t; } static void check(const char *name, struct dep_table *t, uint8_t idx) { if (idx >= t->ucNumEntries) printf("[OOB] %s->entries[%u] (ucNumEntries=%u) reads past %u-byte " "allocation (CWE-125)\n", name, idx, t->ucNumEntries, (unsigned)(sizeof(uint32_t) + sizeof(struct dep_record) * t->ucNumEntries)); else printf("[ok] %s[%u].clk = %u\n", name, idx, t->entries[idx].clk); } int main(void) { struct dep_table *soc = alloc_dep(2); /* VBIOS supplies only 2 levels */ struct dep_table *gfx = alloc_dep(3); struct dep_table *mclk = alloc_dep(1); /* Malicious state record from VBIOS: u8 indices out of range. */ uint8_t ucSocClockIndexLow = 200; uint8_t ucGfxClockIndexLow = 100; uint8_t ucMemClockIndexLow = 5; uint8_t ucSocClockIndexHigh = 255; uint8_t ucGfxClockIndexHigh = 50; uint8_t ucMemClockIndexHigh = 99; check("socclk", soc, ucSocClockIndexLow); check("gfxclk", gfx, ucGfxClockIndexLow); check("mclk", mclk, ucMemClockIndexLow); check("socclk", soc, ucSocClockIndexHigh); check("gfxclk", gfx, ucGfxClockIndexHigh); check("mclk", mclk, ucMemClockIndexHigh); printf("\n=== DF-1182 logic-level result ===\n"); printf("Six u8 clock indices are used to index dep tables with NO\n"); printf("bounds check against ucNumEntries. Malicious VBIOS reads\n"); printf("kernel heap past the small dep-table allocation.\n"); printf("Path runs only when amdgpu powerplay attaches to a real Vega10\n"); printf("GPU - no AMD hardware on this guest, so unreachable here.\n"); free(soc); free(gfx); free(mclk); return 0; } |