DF-1300 / 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 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 140 141 142 143 144 145 146 | /* * DF-1300 harness โ dc_add_all_planes_for_stream stack overflow (dead API) * * Reproduces the vulnerable write pattern of * sys/dev/drm/amd/display/dc/core/dc_resource.c:1557-1574 * in userspace. * * The kernel function: * bool dc_add_all_planes_for_stream(..., int plane_count, ...) * { * struct dc_validation_set set; // on the stack * ... * set.plane_count = plane_count; // raw caller, NO clamp * for (i = 0; i < plane_count; i++) * set.plane_states[i] = plane_states[i]; // NO check vs MAX_SURFACES * ... * } * * `struct dc_validation_set` (dc.h:597-601) is: * struct dc_stream_state *stream; // offset 0 * struct dc_plane_state *plane_states[MAX_SURFACES=3]; // offset 8, 3 ptrs * uint8_t plane_count; // offset 32 (immediately after) * * So plane_states[3] aliases plane_count + padding; plane_states[4+] aliases * the saved frame pointer / return address on the stack. A plane_count > 3 * is a classic stack buffer overflow. * * The function is a DEAD public API: zero in-tree callers (only the * definition at dc_resource.c:1557 and the prototype at dc_stream.h:218). * So the bug is a LATENT HAZARD: a future DC consumer that wires it without * clamping plane_count to MAX_SURFACES gets a stack overflow. * * Build: cc -O2 -Wall -o harness harness.c * Run: ./harness * * Proof strategy: replicate the struct layout with canary fields after * plane_states[], call the function with plane_count=8, and show that the * writes past plane_states[2] corrupt the adjacent canary (which stands in * for plane_count + saved registers in the real frame). */ #include <stdio.h> #include <stdint.h> #include <string.h> #define MAX_SURFACES 3 /* dc.h:43 */ struct dc_stream_state { int dummy; }; struct dc_plane_state { int dummy; }; /* Faithful replica of dc_validation_set (dc.h:597-601), plus trailing canary * fields that stand in for the stack frame past the struct (plane_count, * padding, saved RBP, return address). */ struct dc_validation_set_with_canary { struct dc_stream_state *stream; /* offset 0 */ struct dc_plane_state *plane_states[MAX_SURFACES]; /* offset 8 (3 ptrs) */ uint8_t plane_count; /* offset 32 */ uint8_t pad[7]; /* padding */ /* --- canary: represents stack frame past the struct --- */ uint64_t saved_rbp_canary; uint64_t saved_rip_canary; } __attribute__((packed)); /* pack to make offsets deterministic */ #define CANARY_RBP 0xDEADBEEFCAFEBABEULL #define CANARY_RIP 0x1122334455667788ULL /* Faithful replica of dc_add_all_planes_for_stream (dc_resource.c:1564-1571), * operating on a caller-provided frame so we can inspect the overflow. * * We index `plane_states` through a flat `void **` cast so the compiler * cannot see the [MAX_SURFACES] array bound and (incorrectly, at -O2) elide * the OOB iterations as UB. This faithfully represents the LOGICAL bug: there * is NO `if (plane_count > MAX_SURFACES) return false;` check. NOTE: in the * real kernel, gcc -O2 with -faggressive-loop-optimizations MIGHT silently * clamp the loop to MAX_SURFACES iterations because the array bound is * visible โ but that is fragile (depends on gcc version/flags) and the * correct fix is the explicit check, not relying on optimizer UB-elision. */ static int vulnerable_add_planes(struct dc_validation_set_with_canary *set, struct dc_plane_state * const *plane_states, int plane_count) { int i; void **flat = (void **)set->plane_states; /* flat view, no [3] bound */ set->plane_count = (uint8_t)plane_count; /* :1568 raw, NO clamp */ for (i = 0; i < plane_count; i++) /* :1570 NO check vs MAX_SURFACES */ flat[i] = plane_states[i]; /* :1571 the overflow */ return 0; } int main(void) { printf("DF-1300 dc_add_all_planes_for_stream stack-overflow harness (dead API)\n"); printf("MAX_SURFACES=%d sizeof(plane_states)=%zu (3 ptrs)\n", MAX_SURFACES, MAX_SURFACES * sizeof(struct dc_plane_state *)); struct dc_validation_set_with_canary set; memset(&set, 0, sizeof(set)); set.saved_rbp_canary = CANARY_RBP; set.saved_rip_canary = CANARY_RIP; /* Attacker-supplied plane array with plane_count = 8 (> MAX_SURFACES). * Each entry is a distinct recognizable pointer. */ struct dc_plane_state planes[8]; struct dc_plane_state *plane_ptrs[8]; for (int i = 0; i < 8; i++) { planes[i].dummy = 0xA0 + i; plane_ptrs[i] = &planes[i]; } int plane_count = 8; printf("calling vulnerable_add_planes(plane_count=%d) -- MAX_SURFACES=%d\n", plane_count, MAX_SURFACES); int oob_writes = (plane_count > MAX_SURFACES) ? (plane_count - MAX_SURFACES) : 0; printf("=> %d OOB writes past plane_states[MAX_SURFACES-1] expected " "(each writes a pointer into the frame past plane_states[])\n", oob_writes); printf("BEFORE: plane_count=%u saved_rbp_canary=0x%016llx saved_rip_canary=0x%016llx\n", set.plane_count, (unsigned long long)set.saved_rbp_canary, (unsigned long long)set.saved_rip_canary); vulnerable_add_planes(&set, plane_ptrs, plane_count); int overflows = 0; printf("AFTER:\n"); for (int i = 0; i < MAX_SURFACES; i++) { printf(" plane_states[%d] = %p (in-bounds)\n", i, (void *)set.plane_states[i]); } /* plane_states[3] aliases plane_count + pad; plane_states[4+] aliases canaries. */ printf(" plane_count (overwritten by [3]) = %u\n", set.plane_count); printf(" saved_rbp_canary = 0x%016llx %s\n", (unsigned long long)set.saved_rbp_canary, (set.saved_rbp_canary != CANARY_RBP) ? "*** CORRUPTED ***" : "(intact)"); printf(" saved_rip_canary = 0x%016llx %s\n", (unsigned long long)set.saved_rip_canary, (set.saved_rip_canary != CANARY_RIP) ? "*** CORRUPTED ***" : "(intact)"); if (set.saved_rbp_canary != CANARY_RBP || set.saved_rip_canary != CANARY_RIP) overflows = 1; if (overflows) { printf("RESULT: stack buffer overflow CONFIRMED at dc_resource.c:1570-1571 " "(plane_count>MAX_SURFACES overwrites frame past plane_states[])\n"); printf("IN-KERNEL IMPACT: latent (dead API, 0 in-tree callers) โ stack overflow / " "potential control-flow hijack if a future DC consumer wires it without clamping\n"); return 0; } printf("UNEXPECTED: no overflow detected\n"); return 1; } |