linux-user/mips: fix abort on integer overflow

linux-user/sh4: Fix crashes on signal delivery
 linux-user/loongarch: Enable LSX/LASX in HWCAP
 linux-user: Fixes for zero_bss
 linux-user: Propagate failure in mmap_reserve_or_unmap back to target_munmap
 linux-user: Detect and report host crashes
 linux-user: Remap guest SIGABRT
 -----BEGIN PGP SIGNATURE-----
 
 iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAmUwapYdHHJpY2hhcmQu
 aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV92UAf/RSsFWwCBAqt1WKIK
 7/7F8AF7WW1Hhjy3bHLjNnzgsDeWYfdIVxMGfF9IYKrYMeEqFeBeQ+vcOe9LTAvW
 fEZkA//V+LosiYCwtVGBXyCbeXYxoONMp/taRv6lVHoqVU7aSlbXsYqwePcUtPWq
 r/V+Ru5vssqMueBdE9+E53JPewGPVw8xQE+xGgd1TZIeHWgegZHBzKWVap/3noey
 dKjTig3yxXXg1gQJLCRw+a6bl8oCl4vEluGsLzh5P8aV1imjvFPXkR2w1vQOC3ws
 8DmyMcPEcsY4D2WLeTAGMheURLRzs5141nT0fQCOB4yzO/I8zYFIG12xCxgWoQkg
 kW9XZw==
 =5oFh
 -----END PGP SIGNATURE-----

Merge tag 'pull-lu-20231018' of https://gitlab.com/rth7680/qemu into staging

linux-user/mips: fix abort on integer overflow
linux-user/sh4: Fix crashes on signal delivery
linux-user/loongarch: Enable LSX/LASX in HWCAP
linux-user: Fixes for zero_bss
linux-user: Propagate failure in mmap_reserve_or_unmap back to target_munmap
linux-user: Detect and report host crashes
linux-user: Remap guest SIGABRT

# -----BEGIN PGP SIGNATURE-----
#
# iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAmUwapYdHHJpY2hhcmQu
# aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV92UAf/RSsFWwCBAqt1WKIK
# 7/7F8AF7WW1Hhjy3bHLjNnzgsDeWYfdIVxMGfF9IYKrYMeEqFeBeQ+vcOe9LTAvW
# fEZkA//V+LosiYCwtVGBXyCbeXYxoONMp/taRv6lVHoqVU7aSlbXsYqwePcUtPWq
# r/V+Ru5vssqMueBdE9+E53JPewGPVw8xQE+xGgd1TZIeHWgegZHBzKWVap/3noey
# dKjTig3yxXXg1gQJLCRw+a6bl8oCl4vEluGsLzh5P8aV1imjvFPXkR2w1vQOC3ws
# 8DmyMcPEcsY4D2WLeTAGMheURLRzs5141nT0fQCOB4yzO/I8zYFIG12xCxgWoQkg
# kW9XZw==
# =5oFh
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 18 Oct 2023 16:30:30 PDT
# gpg:                using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F
# gpg:                issuer "richard.henderson@linaro.org"
# gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [full]
# Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A  05C0 64DF 38E8 AF7E 215F

* tag 'pull-lu-20231018' of https://gitlab.com/rth7680/qemu:
  linux-user: Remap guest SIGABRT
  linux-user: Detect and report host SIGILL, SIGFPE, SIGTRAP
  linux-user: Split out host_sig{segv,bus}_handler
  linux-user: Simplify signal_init
  linux-user: Map unsupported signals to an out-of-bounds value
  linux-user: Only register handlers for core_dump_signal by default
  linux-user: Detect and report host crashes
  linux-user: Exit not abort in die_with_backtrace
  linux-user: Split out die_with_signal
  linux-user: Propagate failure in mmap_reserve_or_unmap back to target_munmap
  linux-user/elfload: Enable LSX/LASX in HWCAP for LoongArch
  linux-user/sh4: Fix crashes on signal delivery
  linux-user/mips: fix abort on integer overflow
  linux-user: Fixes for zero_bss

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2023-10-19 10:20:57 -07:00
commit 0d239e513e
5 changed files with 379 additions and 183 deletions

View File

@ -1237,6 +1237,14 @@ static uint32_t get_elf_hwcap(void)
hwcaps |= HWCAP_LOONGARCH_LAM;
}
if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) {
hwcaps |= HWCAP_LOONGARCH_LSX;
}
if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) {
hwcaps |= HWCAP_LOONGARCH_LASX;
}
return hwcaps;
}
@ -2362,31 +2370,58 @@ static abi_ulong setup_arg_pages(struct linux_binprm *bprm,
* Map and zero the bss. We need to explicitly zero any fractional pages
* after the data section (i.e. bss). Return false on mapping failure.
*/
static bool zero_bss(abi_ulong start_bss, abi_ulong end_bss, int prot)
static bool zero_bss(abi_ulong start_bss, abi_ulong end_bss,
int prot, Error **errp)
{
abi_ulong align_bss;
/* We only expect writable bss; the code segment shouldn't need this. */
if (!(prot & PROT_WRITE)) {
error_setg(errp, "PT_LOAD with non-writable bss");
return false;
}
align_bss = TARGET_PAGE_ALIGN(start_bss);
end_bss = TARGET_PAGE_ALIGN(end_bss);
if (start_bss < align_bss) {
int flags = page_get_flags(start_bss);
if (!(flags & PAGE_VALID)) {
/* Map the start of the bss. */
if (!(flags & PAGE_BITS)) {
/*
* The whole address space of the executable was reserved
* at the start, therefore all pages will be VALID.
* But assuming there are no PROT_NONE PT_LOAD segments,
* a PROT_NONE page means no data all bss, and we can
* simply extend the new anon mapping back to the start
* of the page of bss.
*/
align_bss -= TARGET_PAGE_SIZE;
} else if (flags & PAGE_WRITE) {
/* The page is already mapped writable. */
memset(g2h_untagged(start_bss), 0, align_bss - start_bss);
} else {
/* Read-only zeros? */
g_assert_not_reached();
/*
* The start of the bss shares a page with something.
* The only thing that we expect is the data section,
* which would already be marked writable.
* Overlapping the RX code segment seems malformed.
*/
if (!(flags & PAGE_WRITE)) {
error_setg(errp, "PT_LOAD with bss overlapping "
"non-writable page");
return false;
}
/* The page is already mapped and writable. */
memset(g2h_untagged(start_bss), 0, align_bss - start_bss);
}
}
return align_bss >= end_bss ||
target_mmap(align_bss, end_bss - align_bss, prot,
MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) != -1;
if (align_bss < end_bss &&
target_mmap(align_bss, end_bss - align_bss, prot,
MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) == -1) {
error_setg_errno(errp, errno, "Error mapping bss");
return false;
}
return true;
}
#if defined(TARGET_ARM)
@ -3410,8 +3445,8 @@ static void load_elf_image(const char *image_name, int image_fd,
/* If the load segment requests extra zeros (e.g. bss), map it. */
if (vaddr_ef < vaddr_em &&
!zero_bss(vaddr_ef, vaddr_em, elf_prot)) {
goto exit_mmap;
!zero_bss(vaddr_ef, vaddr_em, elf_prot, &err)) {
goto exit_errmsg;
}
/* Find the full program boundaries. */

View File

@ -180,7 +180,9 @@ done_syscall:
}
force_sig_fault(TARGET_SIGFPE, si_code, env->active_tc.PC);
break;
case EXCP_OVERFLOW:
force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTOVF, env->active_tc.PC);
break;
/* The code below was inspired by the MIPS Linux kernel trap
* handling code in arch/mips/kernel/traps.c.
*/

View File

@ -778,7 +778,7 @@ fail:
return -1;
}
static void mmap_reserve_or_unmap(abi_ulong start, abi_ulong len)
static int mmap_reserve_or_unmap(abi_ulong start, abi_ulong len)
{
abi_ulong real_start;
abi_ulong real_last;
@ -807,7 +807,7 @@ static void mmap_reserve_or_unmap(abi_ulong start, abi_ulong len)
prot |= page_get_flags(a + 1);
}
if (prot != 0) {
return;
return 0;
}
} else {
for (prot = 0, a = real_start; a < start; a += TARGET_PAGE_SIZE) {
@ -825,7 +825,7 @@ static void mmap_reserve_or_unmap(abi_ulong start, abi_ulong len)
}
if (real_last < real_start) {
return;
return 0;
}
}
@ -836,32 +836,36 @@ static void mmap_reserve_or_unmap(abi_ulong start, abi_ulong len)
void *ptr = mmap(host_start, real_len, PROT_NONE,
MAP_FIXED | MAP_ANONYMOUS
| MAP_PRIVATE | MAP_NORESERVE, -1, 0);
assert(ptr == host_start);
} else {
int ret = munmap(host_start, real_len);
assert(ret == 0);
return ptr == host_start ? 0 : -1;
}
return munmap(host_start, real_len);
}
int target_munmap(abi_ulong start, abi_ulong len)
{
int ret;
trace_target_munmap(start, len);
if (start & ~TARGET_PAGE_MASK) {
return -TARGET_EINVAL;
errno = EINVAL;
return -1;
}
len = TARGET_PAGE_ALIGN(len);
if (len == 0 || !guest_range_valid_untagged(start, len)) {
return -TARGET_EINVAL;
errno = EINVAL;
return -1;
}
mmap_lock();
mmap_reserve_or_unmap(start, len);
page_set_flags(start, start + len - 1, 0);
shm_region_rm_complete(start, start + len - 1);
ret = mmap_reserve_or_unmap(start, len);
if (likely(ret == 0)) {
page_set_flags(start, start + len - 1, 0);
shm_region_rm_complete(start, start + len - 1);
}
mmap_unlock();
return 0;
return ret;
}
abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,

View File

@ -104,6 +104,14 @@ static void unwind_gusa(CPUSH4State *regs)
/* Reset the SP to the saved version in R1. */
regs->gregs[15] = regs->gregs[1];
} else if (regs->gregs[15] >= -128u && regs->pc == regs->gregs[0]) {
/* If we are on the last instruction of a gUSA region, we must reset
the SP, otherwise we would be pushing the signal context to
invalid memory. */
regs->gregs[15] = regs->gregs[1];
} else if (regs->flags & TB_FLAG_DELAY_SLOT) {
/* If we are in a delay slot, push the previous instruction. */
regs->pc -= 2;
}
}

View File

@ -32,6 +32,7 @@
#include "signal-common.h"
#include "host-signal.h"
#include "user/safe-syscall.h"
#include "tcg/tcg.h"
static struct target_sigaction sigact_table[TARGET_NSIG];
@ -43,9 +44,8 @@ abi_ulong default_sigreturn;
abi_ulong default_rt_sigreturn;
/*
* System includes define _NSIG as SIGRTMAX + 1,
* but qemu (like the kernel) defines TARGET_NSIG as TARGET_SIGRTMAX
* and the first signal is SIGHUP defined as 1
* System includes define _NSIG as SIGRTMAX + 1, but qemu (like the kernel)
* defines TARGET_NSIG as TARGET_SIGRTMAX and the first signal is 1.
* Signal number 0 is reserved for use as kill(pid, 0), to test whether
* a process exists without sending it a signal.
*/
@ -56,7 +56,6 @@ static uint8_t host_to_target_signal_table[_NSIG] = {
#define MAKE_SIG_ENTRY(sig) [sig] = TARGET_##sig,
MAKE_SIGNAL_LIST
#undef MAKE_SIG_ENTRY
/* next signals stay the same */
};
static uint8_t target_to_host_signal_table[TARGET_NSIG + 1];
@ -64,18 +63,24 @@ static uint8_t target_to_host_signal_table[TARGET_NSIG + 1];
/* valid sig is between 1 and _NSIG - 1 */
int host_to_target_signal(int sig)
{
if (sig < 1 || sig >= _NSIG) {
if (sig < 1) {
return sig;
}
if (sig >= _NSIG) {
return TARGET_NSIG + 1;
}
return host_to_target_signal_table[sig];
}
/* valid sig is between 1 and TARGET_NSIG */
int target_to_host_signal(int sig)
{
if (sig < 1 || sig > TARGET_NSIG) {
if (sig < 1) {
return sig;
}
if (sig > TARGET_NSIG) {
return _NSIG;
}
return target_to_host_signal_table[sig];
}
@ -487,26 +492,6 @@ void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo)
info->si_value.sival_ptr = (void *)(long)sival_ptr;
}
static int fatal_signal (int sig)
{
switch (sig) {
case TARGET_SIGCHLD:
case TARGET_SIGURG:
case TARGET_SIGWINCH:
/* Ignored by default. */
return 0;
case TARGET_SIGCONT:
case TARGET_SIGSTOP:
case TARGET_SIGTSTP:
case TARGET_SIGTTIN:
case TARGET_SIGTTOU:
/* Job control signals. */
return 0;
default:
return 1;
}
}
/* returns 1 if given signal should dump core if not handled */
static int core_dump_signal(int sig)
{
@ -526,57 +511,69 @@ static int core_dump_signal(int sig)
static void signal_table_init(void)
{
int host_sig, target_sig, count;
int hsig, tsig, count;
/*
* Signals are supported starting from TARGET_SIGRTMIN and going up
* until we run out of host realtime signals.
* glibc at least uses only the lower 2 rt signals and probably
* nobody's using the upper ones.
* it's why SIGRTMIN (34) is generally greater than __SIGRTMIN (32)
* To fix this properly we need to do manual signal delivery multiplexed
* over a single host signal.
* until we run out of host realtime signals. Glibc uses the lower 2
* RT signals and (hopefully) nobody uses the upper ones.
* This is why SIGRTMIN (34) is generally greater than __SIGRTMIN (32).
* To fix this properly we would need to do manual signal delivery
* multiplexed over a single host signal.
* Attempts for configure "missing" signals via sigaction will be
* silently ignored.
*
* Remap the target SIGABRT, so that we can distinguish host abort
* from guest abort. When the guest registers a signal handler or
* calls raise(SIGABRT), the host will raise SIG_RTn. If the guest
* arrives at dump_core_and_abort(), we will map back to host SIGABRT
* so that the parent (native or emulated) sees the correct signal.
* Finally, also map host to guest SIGABRT so that the emulated
* parent sees the correct mapping from wait status.
*/
for (host_sig = SIGRTMIN; host_sig <= SIGRTMAX; host_sig++) {
target_sig = host_sig - SIGRTMIN + TARGET_SIGRTMIN;
if (target_sig <= TARGET_NSIG) {
host_to_target_signal_table[host_sig] = target_sig;
hsig = SIGRTMIN;
host_to_target_signal_table[SIGABRT] = 0;
host_to_target_signal_table[hsig++] = TARGET_SIGABRT;
for (; hsig <= SIGRTMAX; hsig++) {
tsig = hsig - SIGRTMIN + TARGET_SIGRTMIN;
if (tsig <= TARGET_NSIG) {
host_to_target_signal_table[hsig] = tsig;
}
}
/* generate signal conversion tables */
for (target_sig = 1; target_sig <= TARGET_NSIG; target_sig++) {
target_to_host_signal_table[target_sig] = _NSIG; /* poison */
}
for (host_sig = 1; host_sig < _NSIG; host_sig++) {
if (host_to_target_signal_table[host_sig] == 0) {
host_to_target_signal_table[host_sig] = host_sig;
}
target_sig = host_to_target_signal_table[host_sig];
if (target_sig <= TARGET_NSIG) {
target_to_host_signal_table[target_sig] = host_sig;
/* Invert the mapping that has already been assigned. */
for (hsig = 1; hsig < _NSIG; hsig++) {
tsig = host_to_target_signal_table[hsig];
if (tsig) {
assert(target_to_host_signal_table[tsig] == 0);
target_to_host_signal_table[tsig] = hsig;
}
}
if (trace_event_get_state_backends(TRACE_SIGNAL_TABLE_INIT)) {
for (target_sig = 1, count = 0; target_sig <= TARGET_NSIG; target_sig++) {
if (target_to_host_signal_table[target_sig] == _NSIG) {
count++;
}
host_to_target_signal_table[SIGABRT] = TARGET_SIGABRT;
/* Map everything else out-of-bounds. */
for (hsig = 1; hsig < _NSIG; hsig++) {
if (host_to_target_signal_table[hsig] == 0) {
host_to_target_signal_table[hsig] = TARGET_NSIG + 1;
}
trace_signal_table_init(count);
}
for (count = 0, tsig = 1; tsig <= TARGET_NSIG; tsig++) {
if (target_to_host_signal_table[tsig] == 0) {
target_to_host_signal_table[tsig] = _NSIG;
count++;
}
}
trace_signal_table_init(count);
}
void signal_init(void)
{
TaskState *ts = (TaskState *)thread_cpu->opaque;
struct sigaction act;
struct sigaction oact;
int i;
int host_sig;
struct sigaction act, oact;
/* initialize signal conversion tables */
signal_table_init();
@ -587,22 +584,36 @@ void signal_init(void)
sigfillset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = host_signal_handler;
for(i = 1; i <= TARGET_NSIG; i++) {
host_sig = target_to_host_signal(i);
sigaction(host_sig, NULL, &oact);
if (oact.sa_sigaction == (void *)SIG_IGN) {
sigact_table[i - 1]._sa_handler = TARGET_SIG_IGN;
} else if (oact.sa_sigaction == (void *)SIG_DFL) {
sigact_table[i - 1]._sa_handler = TARGET_SIG_DFL;
/*
* A parent process may configure ignored signals, but all other
* signals are default. For any target signals that have no host
* mapping, set to ignore. For all core_dump_signal, install our
* host signal handler so that we may invoke dump_core_and_abort.
* This includes SIGSEGV and SIGBUS, which are also need our signal
* handler for paging and exceptions.
*/
for (int tsig = 1; tsig <= TARGET_NSIG; tsig++) {
int hsig = target_to_host_signal(tsig);
abi_ptr thand = TARGET_SIG_IGN;
if (hsig >= _NSIG) {
continue;
}
/* If there's already a handler installed then something has
gone horribly wrong, so don't even try to handle that case. */
/* Install some handlers for our own use. We need at least
SIGSEGV and SIGBUS, to detect exceptions. We can not just
trap all signals because it affects syscall interrupt
behavior. But do trap all default-fatal signals. */
if (fatal_signal (i))
sigaction(host_sig, &act, NULL);
/* As we force remap SIGABRT, cannot probe and install in one step. */
if (tsig == TARGET_SIGABRT) {
sigaction(SIGABRT, NULL, &oact);
sigaction(hsig, &act, NULL);
} else {
struct sigaction *iact = core_dump_signal(tsig) ? &act : NULL;
sigaction(hsig, iact, &oact);
}
if (oact.sa_sigaction != (void *)SIG_IGN) {
thand = TARGET_SIG_DFL;
}
sigact_table[tsig - 1]._sa_handler = thand;
}
}
@ -689,15 +700,46 @@ void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr,
}
/* abort execution with signal */
static G_NORETURN
void die_with_signal(int host_sig)
{
struct sigaction act = {
.sa_handler = SIG_DFL,
};
/*
* The proper exit code for dying from an uncaught signal is -<signal>.
* The kernel doesn't allow exit() or _exit() to pass a negative value.
* To get the proper exit code we need to actually die from an uncaught
* signal. Here the default signal handler is installed, we send
* the signal and we wait for it to arrive.
*/
sigfillset(&act.sa_mask);
sigaction(host_sig, &act, NULL);
kill(getpid(), host_sig);
/* Make sure the signal isn't masked (reusing the mask inside of act). */
sigdelset(&act.sa_mask, host_sig);
sigsuspend(&act.sa_mask);
/* unreachable */
_exit(EXIT_FAILURE);
}
static G_NORETURN
void dump_core_and_abort(CPUArchState *env, int target_sig)
{
CPUState *cpu = env_cpu(env);
TaskState *ts = (TaskState *)cpu->opaque;
int host_sig, core_dumped = 0;
struct sigaction act;
host_sig = target_to_host_signal(target_sig);
/* On exit, undo the remapping of SIGABRT. */
if (target_sig == TARGET_SIGABRT) {
host_sig = SIGABRT;
} else {
host_sig = target_to_host_signal(target_sig);
}
trace_user_dump_core_and_abort(env, target_sig, host_sig);
gdb_signalled(env, target_sig);
@ -719,29 +761,7 @@ void dump_core_and_abort(CPUArchState *env, int target_sig)
}
preexit_cleanup(env, 128 + target_sig);
/* The proper exit code for dying from an uncaught signal is
* -<signal>. The kernel doesn't allow exit() or _exit() to pass
* a negative value. To get the proper exit code we need to
* actually die from an uncaught signal. Here the default signal
* handler is installed, we send ourself a signal and we wait for
* it to arrive. */
sigfillset(&act.sa_mask);
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
sigaction(host_sig, &act, NULL);
/* For some reason raise(host_sig) doesn't send the signal when
* statically linked on x86-64. */
kill(getpid(), host_sig);
/* Make sure the signal isn't masked (just reuse the mask inside
of act) */
sigdelset(&act.sa_mask, host_sig);
sigsuspend(&act.sa_mask);
/* unreachable */
abort();
die_with_signal(host_sig);
}
/* queue a signal so that it will be send to the virtual CPU as soon
@ -775,6 +795,161 @@ static inline void rewind_if_in_safe_syscall(void *puc)
}
}
static G_NORETURN
void die_from_signal(siginfo_t *info)
{
char sigbuf[4], codebuf[12];
const char *sig, *code = NULL;
switch (info->si_signo) {
case SIGSEGV:
sig = "SEGV";
switch (info->si_code) {
case SEGV_MAPERR:
code = "MAPERR";
break;
case SEGV_ACCERR:
code = "ACCERR";
break;
}
break;
case SIGBUS:
sig = "BUS";
switch (info->si_code) {
case BUS_ADRALN:
code = "ADRALN";
break;
case BUS_ADRERR:
code = "ADRERR";
break;
}
break;
case SIGILL:
sig = "ILL";
switch (info->si_code) {
case ILL_ILLOPC:
code = "ILLOPC";
break;
case ILL_ILLOPN:
code = "ILLOPN";
break;
case ILL_ILLADR:
code = "ILLADR";
break;
case ILL_PRVOPC:
code = "PRVOPC";
break;
case ILL_PRVREG:
code = "PRVREG";
break;
case ILL_COPROC:
code = "COPROC";
break;
}
break;
case SIGFPE:
sig = "FPE";
switch (info->si_code) {
case FPE_INTDIV:
code = "INTDIV";
break;
case FPE_INTOVF:
code = "INTOVF";
break;
}
break;
case SIGTRAP:
sig = "TRAP";
break;
default:
snprintf(sigbuf, sizeof(sigbuf), "%d", info->si_signo);
sig = sigbuf;
break;
}
if (code == NULL) {
snprintf(codebuf, sizeof(sigbuf), "%d", info->si_code);
code = codebuf;
}
error_report("QEMU internal SIG%s {code=%s, addr=%p}",
sig, code, info->si_addr);
die_with_signal(info->si_signo);
}
static void host_sigsegv_handler(CPUState *cpu, siginfo_t *info,
host_sigcontext *uc)
{
uintptr_t host_addr = (uintptr_t)info->si_addr;
/*
* Convert forcefully to guest address space: addresses outside
* reserved_va are still valid to report via SEGV_MAPERR.
*/
bool is_valid = h2g_valid(host_addr);
abi_ptr guest_addr = h2g_nocheck(host_addr);
uintptr_t pc = host_signal_pc(uc);
bool is_write = host_signal_write(info, uc);
MMUAccessType access_type = adjust_signal_pc(&pc, is_write);
bool maperr;
/* If this was a write to a TB protected page, restart. */
if (is_write
&& is_valid
&& info->si_code == SEGV_ACCERR
&& handle_sigsegv_accerr_write(cpu, host_signal_mask(uc),
pc, guest_addr)) {
return;
}
/*
* If the access was not on behalf of the guest, within the executable
* mapping of the generated code buffer, then it is a host bug.
*/
if (access_type != MMU_INST_FETCH
&& !in_code_gen_buffer((void *)(pc - tcg_splitwx_diff))) {
die_from_signal(info);
}
maperr = true;
if (is_valid && info->si_code == SEGV_ACCERR) {
/*
* With reserved_va, the whole address space is PROT_NONE,
* which means that we may get ACCERR when we want MAPERR.
*/
if (page_get_flags(guest_addr) & PAGE_VALID) {
maperr = false;
} else {
info->si_code = SEGV_MAPERR;
}
}
sigprocmask(SIG_SETMASK, host_signal_mask(uc), NULL);
cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc);
}
static void host_sigbus_handler(CPUState *cpu, siginfo_t *info,
host_sigcontext *uc)
{
uintptr_t pc = host_signal_pc(uc);
bool is_write = host_signal_write(info, uc);
MMUAccessType access_type = adjust_signal_pc(&pc, is_write);
/*
* If the access was not on behalf of the guest, within the executable
* mapping of the generated code buffer, then it is a host bug.
*/
if (!in_code_gen_buffer((void *)(pc - tcg_splitwx_diff))) {
die_from_signal(info);
}
if (info->si_code == BUS_ADRALN) {
uintptr_t host_addr = (uintptr_t)info->si_addr;
abi_ptr guest_addr = h2g_nocheck(host_addr);
sigprocmask(SIG_SETMASK, host_signal_mask(uc), NULL);
cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc);
}
}
static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
{
CPUState *cpu = thread_cpu;
@ -786,61 +961,28 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
int guest_sig;
uintptr_t pc = 0;
bool sync_sig = false;
void *sigmask = host_signal_mask(uc);
void *sigmask;
/*
* Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special
* handling wrt signal blocking and unwinding.
* handling wrt signal blocking and unwinding. Non-spoofed SIGILL,
* SIGFPE, SIGTRAP are always host bugs.
*/
if ((host_sig == SIGSEGV || host_sig == SIGBUS) && info->si_code > 0) {
MMUAccessType access_type;
uintptr_t host_addr;
abi_ptr guest_addr;
bool is_write;
host_addr = (uintptr_t)info->si_addr;
/*
* Convert forcefully to guest address space: addresses outside
* reserved_va are still valid to report via SEGV_MAPERR.
*/
guest_addr = h2g_nocheck(host_addr);
pc = host_signal_pc(uc);
is_write = host_signal_write(info, uc);
access_type = adjust_signal_pc(&pc, is_write);
if (host_sig == SIGSEGV) {
bool maperr = true;
if (info->si_code == SEGV_ACCERR && h2g_valid(host_addr)) {
/* If this was a write to a TB protected page, restart. */
if (is_write &&
handle_sigsegv_accerr_write(cpu, sigmask, pc, guest_addr)) {
return;
}
/*
* With reserved_va, the whole address space is PROT_NONE,
* which means that we may get ACCERR when we want MAPERR.
*/
if (page_get_flags(guest_addr) & PAGE_VALID) {
maperr = false;
} else {
info->si_code = SEGV_MAPERR;
}
}
sigprocmask(SIG_SETMASK, sigmask, NULL);
cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc);
} else {
sigprocmask(SIG_SETMASK, sigmask, NULL);
if (info->si_code == BUS_ADRALN) {
cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc);
}
if (info->si_code > 0) {
switch (host_sig) {
case SIGSEGV:
/* Only returns on handle_sigsegv_accerr_write success. */
host_sigsegv_handler(cpu, info, uc);
return;
case SIGBUS:
host_sigbus_handler(cpu, info, uc);
sync_sig = true;
break;
case SIGILL:
case SIGFPE:
case SIGTRAP:
die_from_signal(info);
}
sync_sig = true;
}
/* get target signal number */
@ -881,6 +1023,7 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
* would write 0xff bytes off the end of the structure and trash
* data on the struct.
*/
sigmask = host_signal_mask(uc);
memset(sigmask, 0xff, SIGSET_T_SIZE);
sigdelset(sigmask, SIGSEGV);
sigdelset(sigmask, SIGBUS);
@ -936,7 +1079,6 @@ int do_sigaction(int sig, const struct target_sigaction *act,
struct target_sigaction *oact, abi_ulong ka_restorer)
{
struct target_sigaction *k;
struct sigaction act1;
int host_sig;
int ret = 0;
@ -996,22 +1138,27 @@ int do_sigaction(int sig, const struct target_sigaction *act,
return 0;
}
if (host_sig != SIGSEGV && host_sig != SIGBUS) {
struct sigaction act1;
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
ignore state to avoid getting unexpected interrupted
syscalls */
if (k->_sa_handler == TARGET_SIG_IGN) {
/*
* It is important to update the host kernel signal ignore
* state to avoid getting unexpected interrupted syscalls.
*/
act1.sa_sigaction = (void *)SIG_IGN;
} else if (k->_sa_handler == TARGET_SIG_DFL) {
if (fatal_signal (sig))
if (core_dump_signal(sig)) {
act1.sa_sigaction = host_signal_handler;
else
} else {
act1.sa_sigaction = (void *)SIG_DFL;
}
} else {
act1.sa_sigaction = host_signal_handler;
if (k->sa_flags & TARGET_SA_RESTART) {
act1.sa_flags |= SA_RESTART;
}
}
ret = sigaction(host_sig, &act1, NULL);
}