DF-3583 / trigger_df3583.c
/* * DF-3583 trigger logic — extraction of the kernel loop in isp_intr(): * * } else if (etype == RQSTYPE_RIO1) { * isp_rio1_t *rio = (isp_rio1_t *) qe; // uint8_t qe[QENTRY_LEN] * isp_get_rio1(isp, (isp_rio1_t *) hp, rio); * for (i = 0; i < rio->req_header.rqs_seqno; i++) { * isp_fastpost_complete(isp, rio->req_handles[i]); <-- OOB * } * * isp_rio1_t is exactly QENTRY_LEN (64) bytes: 4-byte header + * uint32_t req_handles[15]. rqs_seqno is an 8-bit, card/DMA-controlled byte * (0..255). Nothing clamps it to 15, so a card-supplied seqno of N > 15 makes * the loop read (N-15)*4 bytes past the 64-byte stack buffer 'qe'. * * Harness: place qe[64] at the end of an RW page followed by PROT_NONE and * run the identical loop with seqno = 0xff -> faults at qe+64 (i == 16), * i.e. up to 960 bytes of kernel stack can be walked. */ #include <stdio.h> #include <stdint.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; uint8_t *map, *qe; struct sigaction sa; volatile uint32_t sink = 0; int i; printf("sizeof(isp_rio1_t) = %zu (header 4 + req_handles[%zu])\n", sizeof(isp_rio1_t), sizeof(((isp_rio1_t *)0)->req_handles) / sizeof(uint32_t)); printf("rqs_seqno is 8 bits => card may claim up to 255 handles\n"); printf("max OOB stack read = (255-15)*4 = %d bytes past qe[64]\n\n", (255 - 15) * 4); map = mmap(NULL, 2 * pagesz, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); if (map == MAP_FAILED) { perror("mmap"); return (1); } if (mprotect(map, pagesz, PROT_READ|PROT_WRITE) != 0) { perror("mprotect"); return (1); } /* the kernel stack frame's qe[QENTRY_LEN], flush against a guard page */ qe = map + pagesz - QENTRY_LEN; memset(qe, 0, QENTRY_LEN); memset(&sa, 0, sizeof (sa)); sa.sa_sigaction = handler; sa.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); sigaction(SIGBUS, &sa, NULL); { isp_rio1_t *rio = (isp_rio1_t *) qe; rio->req_header.rqs_seqno = 0xff; /* card-controlled */ if (sigsetjmp(jb, 1) != 0) { printf("FAULT (%s) at %p => %zd bytes past the end of qe[64] " "(loop reached i == %d, array holds 15)\n", faultbuf, fault_at, (ssize_t)((uint8_t *)fault_at - (qe + QENTRY_LEN)), (int)(((uint8_t *)fault_at - qe) / 4) - 1 + 1); printf("kernel impact: read of up to 960 bytes of kernel stack,\n" " values consumed as completion handles (isp_fastpost_complete)\n"); return (0); } /* the kernel loop, verbatim */ for (i = 0; i < rio->req_header.rqs_seqno; i++) { sink += rio->req_handles[i]; } } printf("ERROR: no fault occurred (sink=%u)\n", sink); return (1); } |