NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: misc/56820: Many FPE related tests fail on softfloat machines



The following reply was made to PR misc/56820; it has been noted by GNATS.

From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
To: rokuyama.rk%gmail.com@localhost
Cc: gnats-bugs%NetBSD.org@localhost, martin%NetBSD.org@localhost
Subject: Re: misc/56820: Many FPE related tests fail on softfloat machines
Date: Mon, 19 Feb 2024 17:24:36 +0000

 The relevant fragment of the kernel's hardware SIGFPE delivery path
 that we need to emulate is this:
 
 	mutex_enter(&proc_lock);
 	mutex_enter(p->p_lock);
 ...
 	action = SIGACTION_PS(ps, signo).sa_handler;
 ...
 	const bool masked = sigismember(mask, signo);
 	const bool ignored = action == SIG_IGN;
 	if (masked || ignored) {
 		mutex_enter(&ps->sa_mutex);
 		sigdelset(mask, signo);
 		sigdelset(&p->p_sigctx.ps_sigcatch, signo);
 		sigdelset(&p->p_sigctx.ps_sigignore, signo);
 		sigdelset(&SIGACTION_PS(ps, signo).sa_mask, signo);
 		SIGACTION_PS(ps, signo).sa_handler = SIG_DFL;
 		mutex_exit(&ps->sa_mutex);
 	}
 
 	kpsignal2(p, ksi);
 	mutex_exit(p->p_lock);
 	mutex_exit(&proc_lock);
 
 Unfortunately, I don't think there's any non-racy way to do that in
 userland without a new syscall.
 
 That said, for a single-threaded program, perhaps the following
 userland logic will work better to emulate the kernel trap logic --
 with a caveat noted in the XXX comment about where it should loop:
 
 	/*
 	 * Deliver the signal, and loop in case the signal handler
 	 * returns -- executing the same instruction should have the
 	 * same effect.
 	 *
 	 * XXX What if the signal handler changes the set of masked
 	 * exceptions in an attempt to restart the operation?  For
 	 * example, this could record a fine-grained stack trace of
 	 * where an invalid-operation or divide-by-zero first happened,
 	 * and then pick up where it left off with the exception
 	 * masked.  So really this loop should be around the
 	 * floating-point operation, not around the signal delivery.
 	 */
 	for (;;) {
 		struct sigaction sa;
 		sigset_t mask, omask;
 
 		/*
 		 * Block all signals while we figure out how to deliver
 		 * an uncatchable SIGFPE, and obtain the current signal
 		 * mask.
 		 */
 		sigfillset(&mask);
 		sigprocmask(SIG_BLOCK, &mask, &omask);
 
 		/*
 		 * Find the current signal disposition of SIGFPE.
 		 */
 		sigaction(SIGFPE, NULL, &sa);
 
 		/*
 		 * If SIGFPE is masked or ignored, unmask it and reset
 		 * it to the default disposition to deliver the signal.
 		 */
 		if (sigismember(&omask, SIGFPE) ||
 		    ((sa.sa_flags & SA_SIGINFO) == 0 &&
 			sa.sa_handler == SIG_IGN)) {
 			/*
 			 * Prepare to unmask SIGFPE.  This will take
 			 * effect when we use sigprocmask(SIG_SETMASK,
 			 * ...) below, once the signal has been queued,
 			 * so that it happens atomically with respect
 			 * to other signal delivery.
 			 */
 			sigdelset(&omask, SIGFPE);
 
 			/*
 			 * Reset SIGFPE to the default disposition,
 			 * which is to terminate the process.
 			 */
 			memset(&sa, 0, sizeof(sa));
 			sa.sa_handler = SIG_DFL;
 			sigemptyset(&sa.sa_mask);
 			sa.sa_flags = 0;
 			sigaction(SIGFPE, &sa, NULL);
 		}
 
 		/*
 		 * Queue the signal for delivery.  It won't trigger the
 		 * signal handler yet, because it's still masked, but
 		 * as soon as we unmask it either the process will
 		 * terminate or the signal handler will be called.
 		 */
 		sigqueueinfo(getpid(), &info);
 
 		/*
 		 * Restore the old signal mask, except with SIGFPE
 		 * unmasked even if it was masked before.
 		 *
 		 * At this point, either the process will terminate (if
 		 * SIGFPE had or now has the default disposition) or
 		 * the signal handler will be called (if SIGFPE had a
 		 * non-default, non-ignored disposition).
 		 */
 		sigprocmask(SIG_SETMASK, &omask, NULL);
 	}
 


Home | Main Index | Thread Index | Old Index