DF-3584 / trigger_df3584.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 | /* * DF-3584 trigger logic — the unchecked target index in isp_control(): * * case ISPCTL_RESET_DEV: * chan = va_arg(ap, int); * tgt = va_arg(ap, int); * if (IS_24XX(isp)) { * fcparam *fcp = FCPARAM(isp, chan); * hdlidx = fcp->isp_dev_map[tgt] - 1; <-- tgt unchecked * * reached from the CAM XPT_RESET_DEV glue (isp_freebsd.c:4519-4523): * * bus = cam_sim_bus(xpt_path_sim(ccb->ccb_h.path)); * tgt = ccb->ccb_h.target_id; * tgt |= (bus << 16); <-- bus bits left in tgt * error = isp_control(isp, ISPCTL_RESET_DEV, bus, tgt); * * For a device on channel 1 the read lands at * offsetof(fcparam, isp_dev_map) + 2*(65536 + target_id) * = 20536 + 131072 (+2*target_id) >= 151608 bytes into an allocation of * nchan*sizeof(fcparam) (21576 for 1 chan, 43152 for 2) — i.e. >= ~108 KB * past the end: a wild kernel read on every BDR issued to a channel>0 device. * * Harness: one RW page holding an fcparam-sized stand-in followed by * PROT_NONE; performs the identical isp_dev_map[tgt] load. */ #include <stdio.h> #include <stdint.h> #include <stddef.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <signal.h> #include <unistd.h> #include <setjmp.h> typedef void *XS_T; typedef uint64_t XS_DMA_ADDR_T; typedef struct { uint64_t t; } NANOTIME_T; typedef struct { uint16_t param[16]; } mbreg_t; #define MAILBOX_STORAGE 16 struct isposinfo { char pad[128]; }; #define ISP_KERNEL 1 #include <dev/disk/isp/isp_stds.h> #include <dev/disk/isp/ispmbox.h> #include <dev/disk/isp/ispvar.h> static sigjmp_buf jb; static char faultbuf[64]; static void *fault_at; static void handler(int sig, siginfo_t *si, void *uc) { snprintf(faultbuf, sizeof (faultbuf), "SIGSEGV/%s", sig == SIGBUS ? "BUS" : "SEGV"); fault_at = si->si_addr; siglongjmp(jb, 1); } int main(void) { size_t pagesz = 4096, allocsz = 2 * sizeof(fcparam); /* 2-channel 24xx */ uint8_t *map; fcparam *fcp; struct sigaction sa; int chan = 1, tgt, hdlidx; volatile int sink = 0; printf("sizeof(fcparam) = %zu; 2-channel allocation = %zu bytes\n", sizeof(fcparam), allocsz); printf("offsetof(fcparam, isp_dev_map) = %zu (entries: %zu)\n", offsetof(fcparam, isp_dev_map), sizeof(((fcparam *)0)->isp_dev_map) / sizeof(uint16_t)); map = mmap(NULL, 1 << 20, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); if (map == MAP_FAILED) { perror("mmap"); return (1); } /* allocation stand-in: 43152 bytes spread over RW pages at the start */ { size_t rw = (allocsz + pagesz - 1) / pagesz * pagesz; if (mprotect(map, rw, PROT_READ|PROT_WRITE) != 0) { perror("mprotect"); return (1); } } memset(map, 0, allocsz); fcp = (fcparam *)(void *)map; memset(&sa, 0, sizeof (sa)); sa.sa_sigaction = handler; sa.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); sigaction(SIGBUS, &sa, NULL); tgt = 0 | (chan << 16); /* exactly what XPT_RESET_DEV passes */ printf("\nXPT_RESET_DEV on chan 1, target 0 => tgt = 0x%x\n", tgt); printf("read address = base + %zu + 2*%u = base + %zu\n", offsetof(fcparam, isp_dev_map), tgt, offsetof(fcparam, isp_dev_map) + 2 * (size_t)tgt); printf("=> %zd bytes past the end of the %zu-byte allocation\n\n", (ssize_t)(offsetof(fcparam, isp_dev_map) + 2 * (size_t)tgt) - (ssize_t)allocsz, allocsz); if (sigsetjmp(jb, 1) != 0) { printf("FAULT (%s) at %p => %zd bytes past allocation end\n", faultbuf, fault_at, (ssize_t)((uint8_t *)fault_at - (map + allocsz))); printf("kernel impact: wild kernel read during BDR handling " "(panic if unmapped / garbage index otherwise)\n"); return (0); } /* the kernel read, verbatim (isp.c:4522) */ hdlidx = fcp->isp_dev_map[tgt] - 1; sink += hdlidx; printf("ERROR: no fault occurred (sink=%d)\n", sink); return (1); } |