DF-4967 / gart_math.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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | /* * gart_math.c - arithmetic validation for the two radeon_gart.c findings. * * Not a kernel PoC: this reproduces, in userland with the exact constant * types used by the kernel code, the arithmetic of: * * F1 (offset truncation): * sys/dev/drm/radeon/radeon_device.c:1124-1137 radeon_check_arguments() * - accepts radeon_gart_size (MB) that is a power of two and >= 32, * with NO upper bound; gtt_size = (u64)radeon_gart_size << 20 * sys/dev/drm/radeon/radeon_ttm.c:940-943 * - GTT heap = ttm_bo_init_mm(TTM_PL_TT, gtt_size >> PAGE_SHIFT) * sys/dev/drm/radeon/radeon_ttm.c:656,659 * - offset = bo_mem->start << PAGE_SHIFT (unsigned long, 64-bit) * passed to radeon_gart_bind(rdev, unsigned offset, ...) (32-bit!) * sys/dev/drm/radeon/radeon_gart.c:299 * - t = offset / RADEON_GPU_PAGE_SIZE (truncated index) * * F2 (table_size wrap - DF-2012 radeon twin): * sys/dev/drm/radeon/r600.c:1115, rs600.c:546, r100.c:647, r300.c:136 * - table_size = num_gpu_pages * 8 (or *4) computed in 32-bit unsigned * sys/dev/drm/radeon/radeon_gart.c:345-350 * - num_cpu_pages/num_gpu_pages are 'unsigned' (u32) fields assigned * from u64 gtt_size with no overflow check * * Build: cc -O2 -o gart_math gart_math.c * Run: ./gart_math * Success criterion: every CHECK(...) line prints PASS. */ #include <stdio.h> #include <stdint.h> #define PAGE_SHIFT 12 #define RADEON_GPU_PAGE_SIZE 4096u static int failures; static int is_pot(int v) { return v > 0 && (v & (v - 1)) == 0; } /* radeon_check_arguments (radeon_device.c:1117-1134): power of two, >= 32, NO * upper bound. Returns accepted/rejected exactly like the kernel. */ static int check_gart_size_accepted(int radeon_gart_size /* MB */) { if (radeon_gart_size < 32) return 0; if (!is_pot(radeon_gart_size)) return 0; return 1; } static void check(const char *what, int cond) { printf("%-78s %s\n", what, cond ? "PASS" : "FAIL"); if (!cond) failures++; } int main(void) { printf("== F1: gart_size=8192MB (8 GiB GTT) -> 32-bit offset truncation ==\n"); /* Tunable accepted by radeon_check_arguments? */ check("radeon_gart_size=8192 passes kernel validation (pot, >=32, no upper bound)", check_gart_size_accepted(8192)); { /* radeon_gart_init (radeon_gart.c:345-346): u64 -> unsigned */ uint64_t gtt_size = (uint64_t)8192 << 20; /* 8 GiB */ unsigned num_cpu_pages = gtt_size / 4096; /* 2^21 */ unsigned num_gpu_pages = gtt_size / RADEON_GPU_PAGE_SIZE; unsigned table_size = num_gpu_pages * 8; /* 16 MiB, no wrap */ check("num_cpu_pages == 2^21 (heap spans > 4GiB of aperture offsets)", num_cpu_pages == 1u << 21); check("table_size == 16 MiB (table itself fine - the bug is elsewhere)", table_size == 16u * 1024u * 1024u); /* radeon_ttm_backend_bind: offset = (unsigned long)(start << PAGE_SHIFT) */ unsigned long start_low = 0; /* first page */ unsigned long start_high = 1ul << 20; /* page 2^20 = byte offset 4 GiB */ /* ... then radeon_gart_bind(struct radeon_device *, unsigned offset, ...) * truncates to 32 bit, and t = offset / RADEON_GPU_PAGE_SIZE: */ unsigned off_low = (unsigned)(start_low << PAGE_SHIFT); unsigned off_high = (unsigned)(start_high << PAGE_SHIFT); unsigned t_low = off_low / RADEON_GPU_PAGE_SIZE; unsigned t_high = off_high / RADEON_GPU_PAGE_SIZE; check("byte offset of start_high is 4 GiB (fits only u64)", (start_high << PAGE_SHIFT) == 1ull << 32); check("truncated 32-bit offset of start_high wraps to 0", off_high == 0); check("GART entry index computed for both BOs is IDENTICAL (aliasing)", t_low == t_high && t_low == 0); /* both BOs' bind() write pages[p]/pages_entry[t]/set_page(t) at p==t==0: * the high BO's PTE overwrites the low BO's PTE -> the GPU translates * the LOW BO's aperture addresses to the HIGH BO's physical page. */ (void)num_cpu_pages; } printf("\n== F2: gart_size=2097152MB (2 TiB) -> table_size wrap (DF-2012 twin) ==\n"); check("radeon_gart_size=2097152 passes kernel validation (pot, >=32, no upper bound)", check_gart_size_accepted(2097152)); { uint64_t gtt_size = (uint64_t)2097152 << 20; /* 2 TiB */ unsigned num_gpu_pages = gtt_size / RADEON_GPU_PAGE_SIZE; unsigned table_size = num_gpu_pages * 8; /* 32-bit mult */ check("num_gpu_pages == 536870912 (0x20000100-would-be; here 2^29)", num_gpu_pages == 536870912u); check("table_size wraps to 0 (2^29 * 8 == 2^32 mod 2^32)", table_size == 0); } { /* r100/r300/rs400/rv370 (x4 tables): wrap at gtt >= 4 TiB */ uint64_t gtt_size = (uint64_t)4194304 << 20; /* 4 TiB */ unsigned num_gpu_pages = gtt_size / RADEON_GPU_PAGE_SIZE; unsigned table_size = num_gpu_pages * 4; check("x4-asic: num_gpu_pages == 2^30, table_size wraps to 0", num_gpu_pages == 1u << 30 && table_size == 0); } { /* gtt >= 16 TiB: num_gpu_pages itself truncates to 0 in the u32 field */ uint64_t gtt_size = (uint64_t)(1 << 24) << 20; /* 16 TiB */ unsigned num_gpu_pages = gtt_size / RADEON_GPU_PAGE_SIZE; check("num_gpu_pages u32 field truncates to 0 at gtt >= 16 TiB", num_gpu_pages == 0); } printf("\n%s (%d failure%s)\n", failures ? "SOME CHECKS FAILED" : "ALL CHECKS PASSED", failures, failures == 1 ? "" : "s"); return failures ? 1 : 0; } |