DF-4337 / atom_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 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | /* * atom_harness_guest.c โ userland mirror of DragonFlyBSD * sys/dev/drm/radeon/atom.c logic, built and run ON THE AUDIT GUEST * (DragonFly 6.5-DEVELOPMENT, gcc 8.3) as logic-level PoC evidence for * DF-4337 / DF-4338 / DF-4339 / DF-4340. * * The kernel expressions are copied VERBATIM from atom.c (DRM_* mapped to * printf; atom_iio_len is relocated onto a page-guarded mapping so that an * out-of-range index traps instead of silently reading adjacent .data). * A SIGSEGV in a guarded case therefore proves the identical expression * reads out of bounds of the intended object. * * The kernel runtime itself is gated on radeon/ATOM hardware (guest has * none โ see env.txt); see VERDICT.md for why this mirror is the evidence. * * Build: cc -O1 -g -o atom_harness atom_harness_guest.c * Run: ./atom_harness <case> case in {data_header, iio, name, name_nul, shift} */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <stdbool.h> #include <sys/mman.h> #include <unistd.h> #define kprintf printf #define DRM_INFO(...) do { if (getenv("VERBOSE")) printf(__VA_ARGS__); } while (0) #define DRM_ERROR(...) do { printf("DRM_ERROR: " __VA_ARGS__); } while (0) typedef uint16_t u16; /* ---- atom-bits.h verbatim ---- */ static inline uint8_t get_u8(void *bios, int ptr) { return ((unsigned char *)bios)[ptr]; } static inline uint16_t get_u16(void *bios, int ptr) { return get_u8(bios, ptr) | (((uint16_t)get_u8(bios, ptr + 1)) << 8); } static inline uint32_t get_u32(void *bios, int ptr) { return get_u16(bios, ptr) | (((uint32_t)get_u16(bios, ptr + 2)) << 16); } #define CU8(ptr) get_u8(ctx->bios, (ptr)) #define CU16(ptr) get_u16(ctx->bios, (ptr)) #define CU32(ptr) get_u32(ctx->bios, (ptr)) #define CSTR(ptr) (((char *)(ctx->bios)) + (ptr)) struct atom_context { void *bios; uint32_t cmd_table, data_table; uint16_t *iio; uint16_t data_block; }; /* page-guarded allocations: object ends exactly at a large PROT_NONE region, * so any forward OOB read (atom offsets reach up to 128KB past the image) * traps instead of hitting an unrelated mapping */ static void *guarded_alloc(size_t sz) { long ps = 4096; size_t guard = (size_t)16 * 1024 * 1024; /* 16MB trap region */ size_t len = (size_t)ps + guard; char *base = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (base == MAP_FAILED) { perror("mmap"); exit(3); } if (mprotect(base + ps, guard, PROT_NONE) != 0) { perror("mprotect"); exit(3); } return base + ps - sz; /* object ends at the guard boundary */ } /* ===================================================================== * DF-4337 case: atom_parse_data_header (atom.c:1366-1385) โ verbatim. * ===================================================================== */ bool atom_parse_data_header(struct atom_context *ctx, int index, uint16_t * size, uint8_t * frev, uint8_t * crev, uint16_t * data_start) { int offset = index * 2 + 4; int idx = CU16(ctx->data_table + offset); u16 *mdt = (u16 *)((char *)ctx->bios + ctx->data_table + 4); if (!mdt[index]) return false; if (size) *size = CU16(idx); if (frev) *frev = CU8(idx + 2); if (crev) *crev = CU8(idx + 3); *data_start = idx; return true; } /* ===================================================================== * DF-4338 case: atom_index_iio (atom.c:1254-1268) โ verbatim logic. * NOTE: atom_iio_len is the same 10-int table, but placed on a guarded * mapping so the OOB index traps instead of reading adjacent globals. * ===================================================================== */ #define ATOM_IIO_START 1 #define ATOM_IIO_END 9 static int *atom_iio_len; /* -> guarded 10-int table */ static void atom_index_iio(struct atom_context *ctx, int base) { ctx->iio = calloc(256, 2); if (!ctx->iio) return; while (CU8(base) == ATOM_IIO_START) { ctx->iio[CU8(base + 1)] = base + 2; base += 2; while (CU8(base) != ATOM_IIO_END) base += atom_iio_len[CU8(base)]; base += 3; } } /* ===================================================================== * DF-4339 case: the atom_parse() name loop (atom.c:1315-1326) โ verbatim. * ===================================================================== */ static void atom_parse_name(struct atom_context *ctx, int msg_ptr) { char *str; char name[512]; int i; str = CSTR(msg_ptr); while (*str && ((*str == '\n') || (*str == '\r'))) str++; memset(name, 0, sizeof(name)); name[511] = 'A'; /* poison = uninit kernel stack byte */ for (i = 0; i < 511; i++) { name[i] = str[i]; if (name[i] < '.' || name[i] > 'z') { name[i] = 0; break; } } printf(" strlen(name) after loop = %zu (>511 == NUL never written; " "kernel %%s walks uninit stack)\n", strlen(name)); } /* ===================================================================== * DF-4340 case: UB shift counts โ verbatim expressions from * atom_op_shl (atom.c:937), WS OR_MASK (atom.c:252), IIO mask (atom.c:125). * ===================================================================== */ static void shift_ub_demo(void) { uint32_t dst = 0x0000FFFFu, shift = 40; uint32_t r40 = (dst << shift); /* UB: exponent >= 32 */ uint32_t r8 = (dst << 8); uint32_t temp = 0x12345678u; uint8_t width = 0; uint32_t masked = temp & ~((0xFFFFFFFFu >> (32 - width)) << 3); /* UB: >> 32 */ printf(" (0x%x << 40)=0x%08x vs (0x%x << 8)=0x%08x -> %s\n", dst, r40, dst, r8, r40 == r8 ? "PLATFORM-MASKED (shift%%32): UB relied upon" : "divergent"); printf(" IIO mask width=0: >>32 gives 0x%08x (UB relied upon)\n", masked); } int main(int argc, char **argv) { const char *cs = argc > 1 ? argv[1] : "data_header"; printf("case: %s\n", cs); if (!strcmp(cs, "data_header")) { /* 512-byte image (radeon_bios.c self-declared bios[2]*512 size), * master entry 0 points at 0x8000 โ far past the allocation. */ struct atom_context sctx, *ctx = &sctx; memset(&sctx, 0, sizeof(sctx)); ctx->bios = guarded_alloc(512); ctx->data_table = 0x20; ((uint8_t *)ctx->bios)[0x24] = 0x00; /* mdt[0] = 0x8000 */ ((uint8_t *)ctx->bios)[0x25] = 0x80; uint16_t size = 0, data_start = 0; bool ok = atom_parse_data_header(ctx, 0, &size, NULL, NULL, &data_start); printf(" SURVIVED: ok=%d size=0x%04x start=0x%04x (no trap)\n", ok, size, data_start); return 0; } if (!strcmp(cs, "iio")) { struct atom_context sctx, *ctx = &sctx; memset(&sctx, 0, sizeof(sctx)); ctx->bios = guarded_alloc(512); atom_iio_len = guarded_alloc(10 * sizeof(int)); { static const int lens[10] = {1,2,3,3,3,3,4,4,4,3}; memcpy(atom_iio_len, lens, sizeof(lens)); } uint8_t *b = ctx->bios; b[0x40] = ATOM_IIO_START; b[0x41] = 0x05; b[0x42] = 200; /* opcode 200 -> atom_iio_len[200] */ atom_index_iio(ctx, 0x40); printf(" SURVIVED: iio walk done (no trap)\n"); return 0; } if (!strcmp(cs, "name")) { /* msg string ends exactly at the (guarded) image end */ struct atom_context sctx, *ctx = &sctx; memset(&sctx, 0, sizeof(sctx)); size_t sz = 512; ctx->bios = guarded_alloc(sz); memset((uint8_t *)ctx->bios + sz - 300, 'B', 300); atom_parse_name(ctx, (int)(sz - 300)); printf(" SURVIVED (no trap)\n"); return 0; } if (!strcmp(cs, "name_nul")) { /* fully in-bounds 511 printable bytes => name[511] never set */ static uint8_t backing[1024]; memset(backing, 'C', sizeof(backing)); struct atom_context sctx, *ctx = &sctx; memset(&sctx, 0, sizeof(sctx)); ctx->bios = backing; atom_parse_name(ctx, 0); return 0; } if (!strcmp(cs, "shift")) { shift_ub_demo(); return 0; } fprintf(stderr, "unknown case %s\n", cs); return 2; } |