DragonFlyBSD Kernel Audit
DF-0813 / dirstress_h2.c
← back to finding ↓ download raw
/*
 * DF-0813 targeted: stress a single hammer2 directory to force indirect-block
 * topology churn (chain parentage changes) concurrent with sync flush.
 *
 * The race we want to hit: hammer2_flush.c retry loop (397-406) where
 * chain->parent becomes NULL during flush_core's unlock window, then the loop
 * body derefs info.parent at :403(drop)/:405(ref) without a NULL guard.
 *
 * Strategy: one shared directory; writers create/delete hundreds of files in it
 * (forcing block-table growth/shrink => indirect block topology changes =>
 * chain->parent mutations); syncers hammer sync() to drive flush concurrently.
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <signal.h>

static volatile sig_atomic_t g_run=1;
static void h(int s){(void)s;g_run=0;}
static double now(void){struct timespec ts;clock_gettime(CLOCK_MONOTONIC,&ts);return ts.tv_sec+ts.tv_nsec/1e9;}

static void writer(const char *dir,int id){
    char p[300]; unsigned long round=0;
    while(g_run){
        int n=200+id*13%200;
        for(int i=0;i<n&&g_run;i++){
            snprintf(p,sizeof(p),"%s/f%d_%lu_%d",dir,id,round,i);
            int fd=open(p,O_WRONLY|O_CREAT|O_TRUNC,0644);
            if(fd>=0){ write(fd,p,64); close(fd); }
        }
        /* delete half to shrink the dir (topology merge) */
        for(int i=0;i<n&&g_run;i+=2){
            snprintf(p,sizeof(p),"%s/f%d_%lu_%d",dir,id,round,i);
            unlink(p);
        }
        round++;
    }
    fprintf(stderr,"[w%d] %lu rounds\n",id,round);
}

static void syncer(const char *dir){
    unsigned long n=0;
    while(g_run){
        sync();
        int fd=open(dir,O_RDONLY);
        if(fd>=0){fsync(fd);close(fd);}
        n++;
    }
    fprintf(stderr,"[sync] %lu\n",n);
}

int main(int argc,char**argv){
    const char *dir=getenv("H2DIR"); if(!dir) dir="/mnt/h2";
    int secs=60, nw=8, ns=3;
    if(argc>1) dir=argv[1];
    if(argc>2) secs=atoi(argv[2]);
    char *e; if((e=getenv("NW")))nw=atoi(e); if((e=getenv("NS")))ns=atoi(e);

    if(access(dir,W_OK)<0){perror(dir);return 2;}
    signal(SIGTERM,h);signal(SIGINT,h);
    /* clean slate */
    fprintf(stderr,"DF-0813 dir-stress dir=%s nw=%d ns=%d secs=%d\n",dir,nw,ns,secs);
    double t0=now();
    pid_t kids[64]; int nk=0;
    for(int i=0;i<nw&&nk<64;i++){pid_t p=fork();if(!p){writer(dir,i);_exit(0);}if(p>0)kids[nk++]=p;}
    for(int i=0;i<ns&&nk<64;i++){pid_t p=fork();if(!p){syncer(dir);_exit(0);}if(p>0)kids[nk++]=p;}
    while(g_run&&(now()-t0)<secs) sleep(1);
    g_run=0;
    for(int i=0;i<nk;i++) kill(kids[i],SIGTERM);
    for(int i=0;i<nk;i++){int st;waitpid(kids[i],&st,0);}
    fprintf(stderr,"DF-0813 dir-stress done.\n");
    return 0;
}