bsd-user/signal.c: implement do_sigaction

Implement the meat of the sigaction(2) system call with do_sigaction and
helper routiner block_signals (which is also used to implemement signal
masking so it's global).

Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Kyle Evans <kevans@freebsd.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
master
Warner Losh 2022-01-08 23:59:42 -07:00
parent c885ae0e4e
commit 394cf69427
2 changed files with 104 additions and 0 deletions

View File

@ -9,7 +9,29 @@
#ifndef SIGNAL_COMMON_H
#define SIGNAL_COMMON_H
/**
* block_signals: block all signals while handling this guest syscall
*
* Block all signals, and arrange that the signal mask is returned to
* its correct value for the guest before we resume execution of guest code.
* If this function returns non-zero, then the caller should immediately
* return -TARGET_ERESTARTSYS to the main loop, which will take the pending
* signal and restart execution of the syscall.
* If block_signals() returns zero, then the caller can continue with
* emulation of the system call knowing that no signals can be taken
* (and therefore that no race conditions will result).
* This should only be called once, because if it is called a second time
* it will always return non-zero. (Think of it like a mutex that can't
* be recursively locked.)
* Signals will be unblocked again by process_pending_signals().
*
* Return value: non-zero if there was a pending signal, zero if not.
*/
int block_signals(void); /* Returns non zero if signal pending */
long do_rt_sigreturn(CPUArchState *env);
int do_sigaction(int sig, const struct target_sigaction *act,
struct target_sigaction *oact);
abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp);
long do_sigreturn(CPUArchState *env, abi_ulong addr);
void force_sig_fault(int sig, int code, abi_ulong addr);

View File

@ -309,6 +309,25 @@ static void tswap_siginfo(target_siginfo_t *tinfo, const target_siginfo_t *info)
}
}
int block_signals(void)
{
TaskState *ts = (TaskState *)thread_cpu->opaque;
sigset_t set;
/*
* It's OK to block everything including SIGSEGV, because we won't run any
* further guest code before unblocking signals in
* process_pending_signals(). We depend on the FreeBSD behaivor here where
* this will only affect this thread's signal mask. We don't use
* pthread_sigmask which might seem more correct because that routine also
* does odd things with SIGCANCEL to implement pthread_cancel().
*/
sigfillset(&set);
sigprocmask(SIG_SETMASK, &set, 0);
return qatomic_xchg(&ts->signal_pending, 1);
}
/* Returns 1 if given signal should dump core if not handled. */
static int core_dump_signal(int sig)
{
@ -554,6 +573,69 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
cpu_exit(thread_cpu);
}
/* do_sigaction() return host values and errnos */
int do_sigaction(int sig, const struct target_sigaction *act,
struct target_sigaction *oact)
{
struct target_sigaction *k;
struct sigaction act1;
int host_sig;
int ret = 0;
if (sig < 1 || sig > TARGET_NSIG) {
return -TARGET_EINVAL;
}
if ((sig == TARGET_SIGKILL || sig == TARGET_SIGSTOP) &&
act != NULL && act->_sa_handler != TARGET_SIG_DFL) {
return -TARGET_EINVAL;
}
if (block_signals()) {
return -TARGET_ERESTART;
}
k = &sigact_table[sig - 1];
if (oact) {
oact->_sa_handler = tswapal(k->_sa_handler);
oact->sa_flags = tswap32(k->sa_flags);
oact->sa_mask = k->sa_mask;
}
if (act) {
k->_sa_handler = tswapal(act->_sa_handler);
k->sa_flags = tswap32(act->sa_flags);
k->sa_mask = act->sa_mask;
/* Update the host signal state. */
host_sig = target_to_host_signal(sig);
if (host_sig != SIGSEGV && host_sig != SIGBUS) {
memset(&act1, 0, sizeof(struct sigaction));
sigfillset(&act1.sa_mask);
act1.sa_flags = SA_SIGINFO;
if (k->sa_flags & TARGET_SA_RESTART) {
act1.sa_flags |= SA_RESTART;
}
/*
* Note: It is important to update the host kernel signal mask to
* avoid getting unexpected interrupted system calls.
*/
if (k->_sa_handler == TARGET_SIG_IGN) {
act1.sa_sigaction = (void *)SIG_IGN;
} else if (k->_sa_handler == TARGET_SIG_DFL) {
if (fatal_signal(sig)) {
act1.sa_sigaction = host_signal_handler;
} else {
act1.sa_sigaction = (void *)SIG_DFL;
}
} else {
act1.sa_sigaction = host_signal_handler;
}
ret = sigaction(host_sig, &act1, NULL);
}
}
return ret;
}
static inline abi_ulong get_sigframe(struct target_sigaction *ka,
CPUArchState *env, size_t frame_size)
{