DF-1402 / trinity_vce_oob.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | /* * DF-1402 harness: trinity_parse_power_table VCE clk_idx OOB read. * * Replicates the EXACT vulnerable logic from: * sys/dev/drm/radeon/trinity_dpm.c:1809-1813 * for (i = 0; i < RADEON_MAX_VCE_LEVELS; i++) { * clock_array_index = rdev->pm.dpm.vce_states[i].clk_idx; // 6-bit 0..63, NO CHECK * clock_info = (union pplib_clock_info *) * &clock_info_array->clockInfo[clock_array_index * ucEntrySize]; // OOB read * sclk = le16_to_cpu(clock_info->sumo.usEngineClockLow); * ... * } * * clk_idx is a 6-bit field (0..63). With ucNumEntries=1 and clk_idx=63, the * index reads 63 entries past the single-entry clockInfo array: with * ucEntrySize ~16, that is ~1008 bytes OOB. The read value is stored into * vce_states[i].sclk -> wrong VCE engine clock (DoS / OOB info read). * * READ-ONLY primitive (no write-through), so no escalation chain — the ceiling * is OOB-info read / wrong clock programming. The QEMU guest has no AMD Trinity * GPU, so it is unreachable live here. * * Build: cc -O2 -o trinity_vce_oob trinity_vce_oob.c * Run: ./trinity_vce_oob */ #include <stdio.h> #include <string.h> #include <stdint.h> #define RADEON_MAX_VCE_LEVELS 4 struct sumo_clock { uint16_t usEngineClockLow; uint8_t ucEngineClockHigh; }; union pplib_clock_info { struct sumo_clock sumo; uint8_t raw[16]; }; /* VERBATIM from sys/dev/drm/radeon/pptable.h:446-454: * typedef struct _ClockInfoArray { * UCHAR ucNumEntries; * UCHAR ucEntrySize; * UCHAR clockInfo[1]; <-- BYTE array (UCHAR), VLA in reality * } ClockInfoArray; * So `clockInfo[idx * ucEntrySize]` is a BYTE offset (the kernel multiplies * by ucEntrySize precisely because clockInfo is a byte array). */ struct _ClockInfoArray { uint8_t ucNumEntries; uint8_t ucEntrySize; uint8_t clockInfo[1]; }; struct vce_state { uint8_t clk_idx; /* 6-bit 0..63 from VBIOS */ uint32_t sclk; }; int main(void) { uint8_t bios[2048]; memset(bios, 0xDD, sizeof(bios)); /* poison for OOB visibility */ struct _ClockInfoArray *arr = (struct _ClockInfoArray *)bios; arr->ucNumEntries = 1; arr->ucEntrySize = 16; ((union pplib_clock_info *)&arr->clockInfo[0])->sumo.usEngineClockLow = 0x1000; /* Crafted VCE states with clk_idx near the 6-bit max (63). */ struct vce_state vce[RADEON_MAX_VCE_LEVELS] = { { .clk_idx = 63 }, { .clk_idx = 63 }, { .clk_idx = 63 }, { .clk_idx = 63 }, }; printf("DF-1402 trinity VCE clk_idx OOB-read demonstration\n"); printf("clockInfoArray.ucNumEntries = %u, ucEntrySize = %u\n", arr->ucNumEntries, arr->ucEntrySize); int oob_count = 0; for (int i = 0; i < RADEON_MAX_VCE_LEVELS; i++) { /* ---- exact kernel logic (trinity_dpm.c:1811-1814) ---- * clockInfo is a UCHAR[] (byte array), so [idx*ucEntrySize] is a * BYTE offset, then cast to union pplib_clock_info *. */ uint8_t clock_array_index = vce[i].clk_idx; /* line 1811: NO CHECK */ union pplib_clock_info *clock_info = (union pplib_clock_info *) &arr->clockInfo[clock_array_index * arr->ucEntrySize]; /* line 1812-1813: OOB */ uint32_t sclk = clock_info->sumo.usEngineClockLow; /* line 1814 */ sclk |= (uint32_t)clock_info->sumo.ucEngineClockHigh << 16; vce[i].sclk = sclk; int oob = (clock_array_index >= arr->ucNumEntries); if (oob) oob_count++; printf(" level %d: clk_idx=%u -> byte offset [%u*%u=%u] %s -> sclk=0x%x\n", i, clock_array_index, clock_array_index, arr->ucEntrySize, clock_array_index * arr->ucEntrySize, oob ? "OOB" : "in-bounds", sclk); } printf("guard present? : NO (clock path at :1789 checks, this VCE loop does not)\n"); if (oob_count > 0) { printf("\nOOB READ CONFIRMED: clk_idx=63 vs ucNumEntries=1 reads ~%u bytes OOB per level " "(idx*ucEntrySize).\n", 63 * arr->ucEntrySize); printf("READ-ONLY primitive -> no escalation; impact = wrong VCE engine clock / OOB info " "read (DoS via bogus clock programming on crafted VBIOS).\n"); return 0; } fprintf(stderr, "ERROR: OOB not observed\n"); return 1; } |