DF-1174 / 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 | /* * DF-1174 — userspace harness for ata_raid_attach divide-by-zero. * * Reconstructs the rounddown() macro from sys/sys/param.h:400 and the * ata_raid_attach logic from sys/dev/disk/nata/ata-raid.c:155-160 to * demonstrate the kernel #DE (divide error) panic when a crafted RAID * metadata block supplies width==0 or interleave==0 for a RAID0/01/05 * array. * * Build: cc -O2 -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <signal.h> #include <setjmp.h> #include <string.h> /* sys/sys/param.h:400 */ #define rounddown(x, y) (((x)/(y))*(y)) #define AR_T_RAID0 0x01 #define AR_T_RAID1 0x02 #define AR_T_RAID01 0x03 #define AR_T_RAID5 0x05 struct ar_softc { int type; uint64_t total_sectors; unsigned int width; unsigned int interleave; }; /* ata_raid_attach excerpt — sys/dev/disk/nata/ata-raid.c:154-160. * BUG: rounddown(total_sectors, interleave*width) divides by interleave*width; * if either is 0 from crafted metadata -> kernel #DE panic. */ static uint64_t ata_raid_attach_rounddown(struct ar_softc *rdp) { rdp->total_sectors = rounddown(rdp->total_sectors, rdp->interleave * rdp->width); return rdp->total_sectors; } static sigjmp_buf jb; static volatile int got_sig; static void handler(int s) { got_sig = s; siglongjmp(jb, 1); } int main(void) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sigaction(SIGFPE, &sa, NULL); struct { const char *desc; unsigned interleave; unsigned width; } cases[] = { { "nVidia meta stripe_sectors=0, array_width=2", 0, 2 }, { "Intel meta stripe_sectors=64, array_width=0", 64, 0 }, { "ITE meta stripe_sectors=0, array_width=0", 0, 0 }, }; unsigned i; for (i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) { struct ar_softc rdp = { .type = AR_T_RAID0, .total_sectors = 1000000ULL, .interleave = cases[i].interleave, .width = cases[i].width, }; printf("[case %u] %s -> rounddown(total_sectors=%llu, %u*%u)\n", i + 1, cases[i].desc, (unsigned long long)rdp.total_sectors, rdp.interleave, rdp.width); fflush(stdout); got_sig = 0; if (sigsetjmp(jb, 1) == 0) { ata_raid_attach_rounddown(&rdp); printf(" result: total_sectors=%llu (no div0 -- " "not the bug case)\n", (unsigned long long)rdp.total_sectors); } else { printf(" BUG: integer divide-by-zero -> SIGFPE %d " "(kernel equivalent: #DE trap -> panic in " "ata_raid_attach at ata-raid.c:157-158)\n", got_sig); } } /* IOCATARAIDSTATUS divide-by-zero at ata-raid.c:1028 * status->progress = 100 * rdp->rebuild_lba / rdp->total_sectors; */ puts(""); puts("[case 4] IOCATARAIDSTATUS: total_sectors=0 (crafted metadata)"); uint64_t rebuild_lba = 12345ULL; uint64_t total_sectors_zero = 0ULL; volatile unsigned progress; got_sig = 0; if (sigsetjmp(jb, 1) == 0) { progress = 100 * rebuild_lba / total_sectors_zero; printf(" progress=%u (no div0)\n", progress); } else { printf(" BUG: integer divide-by-zero -> SIGFPE %d " "(kernel equivalent: #DE trap -> panic in " "ata_raid_status at ata-raid.c:1028)\n", got_sig); } /* ata_raid_create at ata-raid.c:1255 also divides by (type&...? 2:1) * -- not attacker-reachable so we skip it. */ puts(""); puts("DF-1174 harness: all div-by-zero cases demonstrated."); return 0; } |