⬢ DragonFlyBSD Kernel Audit
DF-2835 / vmsp_abi.c
← back to finding ↓ download raw
/*
 * DF-2835 PoC -- sys_vmspace_create violates its own ABI contract.
 *
 * Contract (sys/vm/vm_vmspace.c:134-139):
 *     "An id of NULL cannot be used. The type and data fields must
 *      currently be 0."
 *
 * Buggy kernel: the implementation (sys/vm/vm_vmspace.c:146-201) never
 * checks uap->id / uap->type / uap->data.  This program, running as an
 * unprivileged user, shows:
 *   [1] create(id=NULL, 0, NULL)  is accepted (rc=0) -- want -1/EINVAL
 *   [2] a second NULL create returns EEXIST, proving the NULL id became a
 *       live RB-tree key (sys/vm/vm_vmspace.c:190)
 *   [3] create(id, type=123, data=0xdead0000) is accepted, type/data are
 *       silently ignored -- want -1/EINVAL
 *   [4] a normal documented create still works (want rc=0)
 *
 * Exit status: 0 when the contract violation reproduces, 1 when the
 * contract is enforced (fixed kernel).
 *
 * Requires vm.vkernel_enable=1 (root sysctl), which is the documented
 * operating configuration on any vkernel host.
 */
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>

static int last_errno;

static long
vmspace_create(void *id, int type, void *data)
{
	long r;

	errno = 0;
	r = syscall(SYS_vmspace_create, id, type, data);
	last_errno = errno;
	return (r);
}

static long
vmspace_destroy(void *id)
{
	long r;

	errno = 0;
	r = syscall(SYS_vmspace_destroy, id);
	last_errno = errno;
	return (r);
}

int
main(void)
{
	int bug_null = 0, bug_typed = 0;
	long r;

	printf("uid=%d euid=%d SYS_vmspace_create=%d\n",
	    getuid(), geteuid(), SYS_vmspace_create);

	r = vmspace_create(NULL, 0, NULL);
	printf("[1] create(id=NULL,        type=0,   data=NULL) -> rc=%ld errno=%d (%s)  want -1/EINVAL\n",
	    r, last_errno, last_errno ? strerror(last_errno) : "ACCEPTED");
	if (r == 0)
		bug_null = 1;

	r = vmspace_create(NULL, 0, NULL);
	printf("[2] create(id=NULL, ...) again            -> rc=%ld errno=%d (%s)  EEXIST proves NULL is a live RB key\n",
	    r, last_errno, last_errno ? strerror(last_errno) : "ACCEPTED");
	if (bug_null && r == -1 && last_errno == EEXIST)
		printf("    => NULL id really resides in the per-proc vmspace RB tree\n");

	r = vmspace_create((void *)0x40000000UL, 123, (void *)0xdead0000UL);
	printf("[3] create(id=%p, type=123, data=%p) -> rc=%ld errno=%d (%s)  want -1/EINVAL\n",
	    (void *)0x40000000UL, (void *)0xdead0000UL, r, last_errno,
	    last_errno ? strerror(last_errno) : "ACCEPTED");
	if (r == 0)
		bug_typed = 1;

	r = vmspace_create((void *)0x40000001UL, 0, NULL);
	printf("[4] create(id=%p, type=0,   data=NULL) -> rc=%ld errno=%d (%s)  want 0 (legit use unchanged)\n",
	    (void *)0x40000001UL, r, last_errno,
	    last_errno ? strerror(last_errno) : "OK");

	/* cleanup: destroy whatever we created so later tests start clean */
	r = vmspace_destroy(NULL);
	printf("[c] destroy(id=NULL)       -> rc=%ld errno=%d\n", r, last_errno);
	r = vmspace_destroy((void *)0x40000000UL);
	printf("[c] destroy(id=%p)    -> rc=%ld errno=%d\n",
	    (void *)0x40000000UL, r, last_errno);
	r = vmspace_destroy((void *)0x40000001UL);
	printf("[c] destroy(id=%p)    -> rc=%ld errno=%d\n",
	    (void *)0x40000001UL, r, last_errno);

	printf("RESULT: null_id_accepted=%d typed_args_ignored=%d\n",
	    bug_null, bug_typed);
	if (bug_null || bug_typed) {
		printf("VERDICT: BUG REPRODUCED -- documented ABI contract not enforced\n");
		return (0);	/* bug present */
	}
	printf("VERDICT: contract enforced (EINVAL) -- fixed kernel\n");
	return (1);	/* bug absent */
}