DragonFlyBSD Kernel Audit
DF-2951 / stress_ucred_race.c
← back to finding ↓ download raw
/* DF-2951 stress: race sysctl_hostname's unlocked p->p_ucred loads
 * (kern_mib.c:221-229) against ucred replacement via setgroups churn
 * (cratom_proc, kern_prot.c:1166-1188). Readers hammer kern.hostname;
 * churners replace p_ucred as fast as possible. Run as jailed uid-0
 * (setgroups is allowed in jails; hostname read is ungated).
 */
#include <sys/sysctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

static volatile int stop_flag;
static unsigned long total_rd, total_sg;
static pthread_mutex_t cnt_mx = PTHREAD_MUTEX_INITIALIZER;

static void *
reader(void *arg)
{
	char buf[300];
	size_t l;
	unsigned long n = 0, errs = 0;

	while (!stop_flag) {
		l = sizeof(buf);
		if (sysctlbyname("kern.hostname", buf, &l, 0, 0) < 0)
			errs++;
		n++;
	}
	pthread_mutex_lock(&cnt_mx);
	total_rd += n + (errs << 40);
	pthread_mutex_unlock(&cnt_mx);
	return (NULL);
}

static void *
churner(void *arg)
{
	gid_t ga[2] = {0, 1001};
	gid_t gb[1] = {0};
	unsigned long n = 0, errs = 0;
	int i = 0;

	while (!stop_flag) {
		if (setgroups((i & 1) ? 1 : 2, (i & 1) ? gb : ga) < 0)
			errs++;
		n++;
		i++;
	}
	pthread_mutex_lock(&cnt_mx);
	total_sg += n + (errs << 40);
	pthread_mutex_unlock(&cnt_mx);
	return (NULL);
}

int
main(int argc, char **argv)
{
	pthread_t th[8];
	int secs = (argc > 1) ? atoi(argv[1]) : 120;
	int i;

	for (i = 0; i < 4; i++)
		pthread_create(&th[i], NULL, reader, NULL);
	for (i = 4; i < 8; i++)
		pthread_create(&th[i], NULL, churner, NULL);
	sleep(secs);
	stop_flag = 1;
	for (i = 0; i < 8; i++)
		pthread_join(th[i], NULL);
	printf("reads: %lu (errs hi-40: %lu) setgroups: %lu (errs hi-40: %lu)\n",
	    total_rd & 0xffffffffffUL, total_rd >> 40,
	    total_sg & 0xffffffffffUL, total_sg >> 40);
	return (0);
}