/*
 * corefixed.c — DF-5587 fix-semantics demo: the exc_nesting re-entrancy
 * guard from fix.diff applied to the same reproduction.  The recursive
 * fault is detected at depth 2, a one-line diagnostic is emitted, the
 * disposition reset to SIG_DFL, and the child dies of the TRUE faulting
 * signal (SIGSEGV) after exactly 2 handler entries — instead of 307,833
 * silent re-entries.
 */

#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/mman.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static volatile long *depthp;
#define depth (*depthp)
static volatile long tls_base;          /* "%gs base": 0, like cothread */

static void
fault_handler(int sig, siginfo_t *si, void *ctx)
{
	int save = errno;

	if (__sync_fetch_and_add(&depth, 1) != 0) {
		static char msg[] =
		    "vkernel: recursive fault in exc_segfault\n";
		write(2, msg, sizeof(msg) - 1);
		signal(SIGSEGV, SIG_DFL);
		signal(SIGBUS, SIG_DFL);
		signal(SIGTRAP, SIG_DFL);
		signal(SIGFPE, SIG_DFL);
		raise(sig);
		/* NOT REACHED */
	}
	{
		volatile char *p = (volatile char *)(uintptr_t)tls_base;
		*p;                       /* fault at "mycpu" -> re-enter */
	}
	errno = save;
}

int
main(void)
{
	struct sigaction sa;
	pid_t pid;
	int st;

	depthp = mmap(NULL, 4096, PROT_READ|PROT_WRITE,
		      MAP_SHARED|MAP_ANON, -1, 0);
	if (depthp == MAP_FAILED) { perror("mmap"); return 2; }
	*depthp = 0;
	pid = fork();
	if (pid == 0) {
		memset(&sa, 0, sizeof(sa));
		sa.sa_sigaction = fault_handler;
		sa.sa_flags = SA_SIGINFO | SA_NODEFER;
		sigemptyset(&sa.sa_mask);
		sigaction(SIGSEGV, &sa, NULL);
		{
			volatile char *p = (volatile char *)(uintptr_t)0x10;
			*p;
		}
		_exit(0);
	}
	waitpid(pid, &st, 0);
	printf("child: signaled=%d termsig=%d core=%d handler_entries=%ld\n",
	       WIFSIGNALED(st), WIFSIGNALED(st) ? WTERMSIG(st) : -1,
	       WCOREDUMP(st), depth);
	if (WIFSIGNALED(st) && WTERMSIG(st) == SIGSEGV && depth == 2) {
		printf("FIX_SEMANTICS_OK: honest SIGSEGV death after 2 "
		       "entries with diagnostic (vs 307833 silent)\n");
		return (0);
	}
	printf("UNEXPECTED\n");
	return (1);
}
