DF-3307 / trigger.c
/* * trigger.c -- DF-3307 reproducer (run as root: the hop-by-hop cmsg is * privilege-gated in ip6_setpktoption()). * * Sends one 5000-byte UDP6 datagram to ::1 with a 2048-byte * IPV6_HOPOPTS ancillary option (ip6h_len = 255 -> hbhlen 2048). * Total packet = 40 + 256 + 8 + 5000 = 5304 bytes > lo0 MTU (set to 1500) * -> ip6_output() fragments it, and its fragment path re-dereferences * the (freed by the pfil hook) hbh mbuf when rewriting the * next-header byte. * * Also binds a receiver on ::1:9999 first: on a fixed kernel the * fragments carry ip6f_nxt = IPPROTO_UDP(17), reassembly succeeds and * the datagram is delivered. (lo0 MTU is set to 1500 first.) On a vulnerable kernel the frag nxt is * read from freed memory (canary 0xAA) and the datagram never arrives. */ #include <sys/param.h> #include <sys/socket.h> #include <sys/time.h> #include <netinet/in.h> #include <err.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #define HBHLEN 256 #define PAYLEN 5000 static char cbuf[CMSG_SPACE(HBHLEN)]; static char payload[PAYLEN]; int main(void) { struct sockaddr_in6 to, bindaddr; struct msghdr msg; struct cmsghdr *cm; struct iovec iov; struct timeval tv; unsigned char *d; int tx, rx, n, i; memset(&to, 0, sizeof(to)); to.sin6_family = AF_INET6; to.sin6_port = htons(9999); to.sin6_addr = in6addr_loopback; rx = socket(AF_INET6, SOCK_DGRAM, 0); if (rx < 0) err(1, "rx socket"); bindaddr = to; if (bind(rx, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) err(1, "bind"); tx = socket(AF_INET6, SOCK_DGRAM, 0); if (tx < 0) err(1, "tx socket"); memset(cbuf, 0, sizeof(cbuf)); cm = (struct cmsghdr *)cbuf; cm->cmsg_level = IPPROTO_IPV6; cm->cmsg_type = IPV6_HOPOPTS; cm->cmsg_len = CMSG_LEN(HBHLEN); d = (unsigned char *)CMSG_DATA(cm); d[0] = 0; /* nxt, filled by ip6_output */ d[1] = 31; /* ip6h_len: (31+1)*8 = 256 */ d[2] = 1; /* PadN */ d[3] = HBHLEN - 4; /* pad length: covers the rest */ for (i = 4; i < HBHLEN; i++) d[i] = 0; memset(payload, 'A', sizeof(payload)); memset(&msg, 0, sizeof(msg)); iov.iov_base = payload; iov.iov_len = sizeof(payload); msg.msg_name = &to; msg.msg_namelen = sizeof(to); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cbuf; msg.msg_controllen = CMSG_LEN(HBHLEN); if (sendmsg(tx, &msg, 0) < 0) err(1, "sendmsg"); printf("sendmsg: %d bytes payload + %d hbh ok\n", PAYLEN, HBHLEN); tv.tv_sec = 2; tv.tv_usec = 0; if (setsockopt(rx, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) err(1, "SO_RCVTIMEO"); n = recv(rx, payload, sizeof(payload), 0); if (n < 0) printf("receiver: no datagram (fragments malformed): %s\n", strerror(errno)); else printf("receiver: got %d bytes -- datagram delivered\n", n); return (0); } |