DF-3666 / df3666.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-3666 PoC: faithful index-math reproduction of the undersized RLC * save/restore buffer object in sumo_rlc_init() * (sys/dev/drm/radeon/evergreen.c:4155 allocation vs 4197-4206 writes). * * For the ON/LN/TN (pre-TAHITI IGP: PALM/SUMO/SUMO2) path the function * allocates: * * evergreen.c:4155 radeon_bo_create(rdev, dws * 4, PAGE_SIZE, ...) * with dws = rdev->rlc.reg_list_size * = ARRAY_SIZE(sumo_rlc_save_restore_register_list) = 81 * -> a 324-byte BO * * but the packing loop writes the 2-regs-into-3-dwords format: * * evergreen.c:4197 for (i = 0; i < dws; i++) { * data = src_ptr[i] >> 2; * i++; * if (i < dws) data |= (src_ptr[i] >> 2) << 16; * j = (((i - 1) * 3) / 2); * dst_ptr[j] = cpu_to_le32(data); <-- dword 3k * } * evergreen.c:4205 j = ((i * 3) / 2); * dst_ptr[j] = END_MARKER; <-- highest idx * * which requires ceil(dws/2)*3 + 1 dwords = 124 dwords = 496 bytes. * * The overrun is 43 dwords / 172 bytes past the BO's declared size. In * practice it is contained by TTM page-granularity (bo->num_pages rounds * 324 bytes up to 1 page, and 496 < 4096), so it corrupts only slack * inside the object's own backing page -- which is why this is filed as * Low / hardening rather than an exploitable heap overflow. * * The RLC GPU microcode subsequently uses these save areas at runtime * (evergreen.c:4394 TN_RLC_SAVE_AND_RESTORE_BASE), so the GPU-side saves * also land in the undersized region. * * Build: cc -O2 -o df3666 df3666.c * Run: ./df3666 */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #define RLC_SAVE_RESTORE_LIST_END_MARKER 0x00000000 int main(void) { const int dws = 81; /* ARRAY_SIZE(sumo_rlc_save_restore_register_list), evergreen.c:123-206 */ const uint32_t alloc_bytes = (uint32_t)dws * 4; /* evergreen.c:4155 */ /* TTM: bo->num_pages = (size + PAGE_SIZE-1) >> PAGE_SHIFT */ const uint32_t backing_bytes = (alloc_bytes + 4095) & ~4095u; /* dst model of the mapped BO (page-sized backing, as TTM provides) */ uint32_t *dst = calloc(backing_bytes / 4, 4); uint32_t *src = malloc(alloc_bytes); uint32_t data, i, j; int max_idx = -1, oob_dwords = 0; memset(src, 0xAB, alloc_bytes); /* ---- verbatim loop of evergreen.c:4197-4204 ---- */ for (i = 0; i < (uint32_t)dws; i++) { data = src[i] >> 2; i++; if (i < (uint32_t)dws) data |= (src[i] >> 2) << 16; j = (((i - 1) * 3) / 2); dst[j] = data; if ((int)j > max_idx) max_idx = (int)j; } /* ---- verbatim epilogue of evergreen.c:4205-4206 ---- */ j = ((i * 3) / 2); dst[j] = RLC_SAVE_RESTORE_LIST_END_MARKER; if ((int)j > max_idx) max_idx = (int)j; /* every dword in [dws, max_idx] is past the 324-byte BO */ oob_dwords = max_idx - dws + 1; printf("== DF-3666: sumo_rlc_init save/restore BO sizing ==\n\n"); printf("reg_list entries (dws) : %d\n", dws); printf("BO allocated (evergreen.c:4155) : %u bytes (%u dwords)\n", alloc_bytes, alloc_bytes / 4); printf("loop exit i : %u\n", i); printf("end-marker index (evergreen.c:4205): %u\n", j); printf("highest dword written : %d (byte offset %d)\n", max_idx, max_idx * 4); printf("bytes actually required : %d\n", (max_idx + 1) * 4); printf("OOB write past BO size : %d dwords = %d bytes\n", oob_dwords, oob_dwords * 4); printf("TTM backing (page-rounded) : %u bytes -> overrun %s " "the object's own backing page\n", backing_bytes, ((max_idx + 1) * 4 <= (int)backing_bytes) ? "stays inside" : "ESCAPES"); if ((max_idx + 1) * 4 > (int)alloc_bytes) { printf("\nRESULT: BUG REPRODUCED - writes need %d bytes but the " "BO is %u bytes.\n", (max_idx + 1) * 4, alloc_bytes); printf("Contained only by TTM page rounding; a latent " "CWE-787 that must be fixed at the allocation site.\n"); return 0; } printf("\nRESULT: no overrun?!\n"); return 1; } |