DF-1175 / 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 | /* * DF-1175 — userspace harness for Intel/nVidia RAID metadata parser OOB read. * * Reconstructs the checksum loops from: * - sys/dev/disk/nata/ata-raid.c:2132-2154 (Intel parser, kmalloc(1536)) * - sys/dev/disk/nata/ata-raid.c:3028-3048 (nVidia parser, kmalloc(512)) * * In both parsers `meta` is a small fixed-size kmalloc'd buffer, but the * checksum loop iterates a disk-controlled u32 field (`config_size`): * * Intel : count < meta->config_size/4 (4 bytes/iter) * nVidia : count < meta->config_size (4 bytes/iter) * * A large config_size drives the read past the kmalloc into unmapped kernel * memory -> panic. The magic-string gate atIntel:2145 / nVidia:3039 runs * BEFORE the loop, but the checksum verification at Intel:2156 / nVidia:3049 * runs AFTER, so a valid checksum is NOT needed to reach the bug -- only the * magic is. * * Build: cc -O2 -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <signal.h> #include <setjmp.h> #include <sys/types.h> #define INTEL_MAGIC "Intel Raid ISM Cfg Sig. " #define NV_MAGIC "NVIDIA " /* Mirror the on-disk struct layouts (verified by in-guest sizeof() = 512 * bytes each). We keep only the fields the parsers actually touch. */ struct intel_raid_conf { uint8_t intel_id[24]; uint8_t version[6]; uint8_t dummy_0[2]; uint32_t checksum; uint32_t config_size; uint32_t config_id; uint32_t generation; uint32_t dummy_1[2]; uint8_t total_disks; uint8_t total_volumes; uint8_t dummy_2[2]; uint32_t filler_0[39]; /* rest is irrelevant to the loop; size up to 512 */ uint8_t rest[512 - 24 - 6 - 2 - 4*7 - 2 - 39*4]; } __attribute__((packed)); struct nvidia_raid_conf { uint8_t nvidia_id[8]; uint32_t config_size; uint32_t checksum; uint16_t version; uint8_t disk_number; uint8_t dummy_0; uint32_t total_sectors; uint32_t sector_size; uint8_t serial[16]; uint8_t revision[4]; uint32_t dummy_1; uint8_t rest[512 - 8 - 4*5 - 2 - 2 - 16 - 4]; } __attribute__((packed)); static sigjmp_buf jb; static volatile int got_sig; static void handler(int s, siginfo_t *si, void *uc) { (void)si; (void)uc; got_sig = s; siglongjmp(jb, 1); } /* Reproduce the Intel checksum loop at ata-raid.c:2151-2154. * meta_bufsize is the kmalloc size (1536). */ static uint32_t intel_loop(struct intel_raid_conf *meta, size_t meta_bufsize) { uint32_t checksum = 0; uint32_t *ptr = (uint32_t *)meta; uint32_t count; /* ata-raid.c:2151-2154 */ for (count = 0; count < (meta->config_size / sizeof(uint32_t)); count++) checksum += *ptr++; (void)meta_bufsize; return checksum; } /* Reproduce the nVidia checksum loop at ata-raid.c:3046-3048. */ static uint32_t nvidia_loop(struct nvidia_raid_conf *meta, size_t meta_bufsize) { uint32_t checksum = 0; uint32_t *ptr = (uint32_t *)meta; uint32_t count; /* ata-raid.c:3046-3048 */ for (count = 0; count < meta->config_size; count++) checksum += *ptr++; (void)meta_bufsize; return checksum; } int main(void) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); sigaction(SIGBUS, &sa, NULL); /* ----- Case 1: Intel parser, oversized config_size ----- */ puts("=== Case 1: Intel parser (ata-raid.c:2132-2154) ==="); puts(" meta = kmalloc(1536); magic OK; config_size = 0x10000 (65536)"); puts(" loop reads 65536 bytes from a 1536-byte buffer -> 64000-byte OOB read"); /* Allocate exactly what the kernel does: 1536 bytes, zeroed. */ char *intel_buf = calloc(1, 1536); struct intel_raid_conf *meta_i = (struct intel_raid_conf *)intel_buf; memcpy(meta_i->intel_id, INTEL_MAGIC, strlen(INTEL_MAGIC)); /* magic check (ata-raid.c:2145) would PASS */ if (strncmp((char *)meta_i->intel_id, INTEL_MAGIC, strlen(INTEL_MAGIC)) != 0) { puts(" magic mismatch -- harness setup error"); } else { puts(" magic check: PASS (matches INTEL_MAGIC)"); } meta_i->config_size = 0x10000; /* disk-controlled u32 = 65536 */ uint32_t iter_count_i = meta_i->config_size / sizeof(uint32_t); uint32_t bytes_read_i = iter_count_i * sizeof(uint32_t); printf(" loop will run %u iterations, reading %u bytes " "(buffer = 1536 bytes -> %u-byte OOB read)\n", iter_count_i, bytes_read_i, bytes_read_i - 1536); fflush(stdout); got_sig = 0; if (sigsetjmp(jb, 1) == 0) { intel_loop(meta_i, 1536); /* In userspace with a single 1536-byte calloc we usually crash * before completing; if not, the loop has read garbage which * itself is the info-leak equivalent in the kernel. */ puts(" (loop completed without SIGSEGV -- in the kernel this " "would have consumed kmalloc trailing bytes = info leak)"); } else { printf(" BUG: OOB read faulted (signal %d) -- kernel " "equivalent: page fault past kmalloc(1536) in " "ata_raid_intel_read_meta -> panic\n", got_sig); } free(intel_buf); /* ----- Case 2: nVidia parser, oversized config_size ----- */ puts(""); puts("=== Case 2: nVidia parser (ata-raid.c:3028-3048) ==="); puts(" meta = kmalloc(sizeof(nvidia_raid_conf)=512); magic OK; config_size = 0x400 (1024)"); puts(" loop reads 1024 u32 words = 4096 bytes from a 512-byte buffer -> 3584-byte OOB read"); char *nv_buf = calloc(1, 512); struct nvidia_raid_conf *meta_n = (struct nvidia_raid_conf *)nv_buf; memcpy(meta_n->nvidia_id, NV_MAGIC, strlen(NV_MAGIC)); if (strncmp((char *)meta_n->nvidia_id, NV_MAGIC, strlen(NV_MAGIC)) != 0) { puts(" magic mismatch -- harness setup error"); } else { puts(" magic check: PASS (matches NV_MAGIC)"); } meta_n->config_size = 0x400; /* 1024 iterations = 4096 bytes */ uint32_t bytes_read_n = meta_n->config_size * sizeof(uint32_t); printf(" loop will run %u iterations, reading %u bytes " "(buffer = 512 bytes -> %u-byte OOB read)\n", meta_n->config_size, bytes_read_n, bytes_read_n - 512); fflush(stdout); got_sig = 0; if (sigsetjmp(jb, 1) == 0) { nvidia_loop(meta_n, 512); puts(" (loop completed without SIGSEGV -- kernel equivalent: " "info leak of slab trailing bytes)"); } else { printf(" BUG: OOB read faulted (signal %d) -- kernel " "equivalent: page fault past kmalloc(512) in " "ata_raid_nvidia_read_meta -> panic\n", got_sig); } free(nv_buf); puts(""); puts("DF-1175 harness: both OOB-read cases demonstrated."); puts(" Impact ceiling: info leak (small over-read) + DoS panic (large over-read)."); return 0; } |