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