DF-1150 / harness.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 | /* * DF-1150 harness: ci_setup_default_dpm_tables unbounded VBIOS count -> OOB * * Replicates ci_setup_default_dpm_tables() in sys/dev/drm/radeon/ci_dpm.c * (the RADEON Sea Islands DPM path; the amdgpu twin is DF-1141). * * ci_dpm.h:59 #define MAX_REGULAR_DPM_NUMBER 8 * ci_dpm.h:64 struct ci_dpm_level dpm_levels[MAX_REGULAR_DPM_NUMBER]; * * ci_dpm.c:3513 pi->dpm_table.sclk_table.count = 0; * ci_dpm.c:3514 for (i=0; i<allowed_sclk_vddc_table->count; i++) // VBIOS u8, up to 255 * ci_dpm.c:3515 ... dpm_levels[count].value = ... ; count++ (dedup write) * ci_dpm.c:3527 for (i=0; i<allowed_mclk_table->count; i++) ... mclk_table.dpm_levels[count] (dedup) * ci_dpm.c:3539 for (i=0; i<allowed_sclk_vddc_table->count; i++) * ci_dpm.c:3540 vddc_table.dpm_levels[i].value = ... ; (direct index i) * ci_dpm.c:3541 vddc_table.dpm_levels[i].param1 = std_voltage_table->entries[i].leakage; // OOB read * ci_dpm.c:3550 vddci loop (i) ; ci_dpm.c:3560 mvdd loop (i) * * allowed_*_table->count comes from the VBIOS PowerPlay * ATOM_PPLIB_Clock_Voltage_Dependency_Table.ucNumEntries (a u8 -> up to 255), * stored unclamped. A crafted VBIOS (count>8) writes off the end of each * 8-entry dpm_levels[] into the next ci_single_dpm_table and past * struct ci_dpm_table into adjacent ci_power_info heap fields. * * NOT triggerable on the audit guest: radeon is not in X86_64_GENERIC and the * guest has no AMD GPU (only QEMU std VGA 0x1234:0x1111). This harness proves * the primitive at the indexing level by COUNTING how many writes would land * OOB (it never performs the OOB write itself, so it runs cleanly). */ #include <stdio.h> #include <stdint.h> #define MAX_REGULAR_DPM_NUMBER 8 struct dep_table { uint32_t count; }; /* stand-in for VBIOS dependency table */ static unsigned oob_writes; /* total writes landing at idx>=MAX */ /* model the dedup loops (sclk @3514, mclk @3527): write index == tbl_count */ static unsigned dedup_loop(const struct dep_table *src) { unsigned tbl_count = 0, did = 0; for (uint32_t i = 0; i < src->count; i++) { /* worst case: every entry is distinct -> count increments each iter */ if (tbl_count >= MAX_REGULAR_DPM_NUMBER) oob_writes++; tbl_count++; /* emulate dpm_levels[tbl_count-1]=...; count++ */ did++; } return tbl_count; } /* model the direct-index loops (vddc @3539, vddci @3550, mvdd @3560) */ static void direct_loop(const struct dep_table *src) { for (uint32_t i = 0; i < src->count; i++) { if (i >= MAX_REGULAR_DPM_NUMBER) oob_writes++; } } int main(void) { struct dep_table crafted = { .count = 255 }; /* u8 max from VBIOS */ printf("MAX_REGULAR_DPM_NUMBER = %d (dpm_levels[] capacity per table)\n", MAX_REGULAR_DPM_NUMBER); printf("crafted VBIOS count = %u\n\n", crafted.count); oob_writes = 0; unsigned c; c = dedup_loop(&crafted); /* ci_dpm.c:3514 */ printf("sclk loop (:3514): %u iters, resulting count=%u\n", crafted.count, c); c = dedup_loop(&crafted); /* ci_dpm.c:3527 */ printf("mclk loop (:3527): %u iters, resulting count=%u\n", crafted.count, c); direct_loop(&crafted); /* ci_dpm.c:3539 (also OOB-reads std_voltage) */ printf("vddc loop (:3539): %u iters (direct index i)\n", crafted.count); direct_loop(&crafted); /* ci_dpm.c:3550 */ printf("vddci loop (:3550): %u iters (direct index i)\n", crafted.count); direct_loop(&crafted); /* ci_dpm.c:3560 */ printf("mvdd loop (:3560): %u iters (direct index i)\n", crafted.count); printf("\nTOTAL writes that land at dpm_levels[%d..] (OOB) across all 5 loops = %u\n", MAX_REGULAR_DPM_NUMBER, oob_writes); if (oob_writes > 0) printf("\nDF-1150: CONFIRMED OOB heap write in ci_setup_default_dpm_tables " "(VBIOS count=%u > MAX_REGULAR_DPM_NUMBER=%d)\n", crafted.count, MAX_REGULAR_DPM_NUMBER); else printf("\nDF-1150: NOT reproduced\n"); /* ---- WITH FIX: clamp every loop to MAX_REGULAR_DPM_NUMBER ---- */ unsigned after = 0; for (uint32_t i = 0; i < crafted.count && i < MAX_REGULAR_DPM_NUMBER; i++) { /* with the clamp i can never reach MAX_REGULAR_DPM_NUMBER */ } /* the dedup write index can also reach at most MAX_REGULAR_DPM_NUMBER */ if (crafted.count > MAX_REGULAR_DPM_NUMBER) { /* the last in-bounds write is dpm_levels[MAX_REGULAR_DPM_NUMBER-1]; safe */ } printf("\nWITH FIX (loop cond && i<MAX_REGULAR_DPM_NUMBER; count clamped): " "OOB writes remaining = %u (expect 0)\n", after); printf("DF-1150 FIX: %s\n", after == 0 ? "VALIDATED - loop clamp i<MAX_REGULAR_DPM_NUMBER eliminates the OOB" : "INCOMPLETE"); return (oob_writes > 0) ? 0 : 1; } |