qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/9] linux-user: Detect and report host crashes
@ 2023-10-03 19:20 Richard Henderson
  2023-10-03 19:20 ` [PATCH v5 1/9] linux-user: Split out die_with_signal Richard Henderson
                   ` (9 more replies)
  0 siblings, 10 replies; 12+ messages in thread
From: Richard Henderson @ 2023-10-03 19:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent, deller

More signal cleanups.  Mostly tested by temporarily adding an
abort, divide by zero, undefined instruction, null dereference,
within the implementation of a guest syscall to induce an error.

Changes for v5:
  * Remap guest abort, which means we need to try less hard on
    the host side to handle assertion failures.
  * Drop the backtrace.  Since backtrace_symbols only looks at the
    dynamic symbol set, we don't much that's useful -- we still
    need to use the debugger.


r~


Helge Deller (1):
  linux-user: Detect and report host crashes

Richard Henderson (8):
  linux-user: Split out die_with_signal
  linux-user: Exit not abort in die_with_backtrace
  linux-user: Only register handlers for core_dump_signal by default
  linux-user: Map unsupported signals to an out-of-bounds value
  linux-user: Simplify signal_init
  linux-user: Split out host_sig{segv,bus}_handler
  linux-user: Detect and report host SIGILL, SIGFPE, SIGTRAP
  linux-user: Remap guest SIGABRT

 linux-user/signal.c | 452 +++++++++++++++++++++++++++++---------------
 1 file changed, 296 insertions(+), 156 deletions(-)

-- 
2.34.1



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v5 1/9] linux-user: Split out die_with_signal
  2023-10-03 19:20 [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
@ 2023-10-03 19:20 ` Richard Henderson
  2023-10-03 19:20 ` [PATCH v5 2/9] linux-user: Exit not abort in die_with_backtrace Richard Henderson
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Richard Henderson @ 2023-10-03 19:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent, deller, Philippe Mathieu-Daudé

Because we trap so many signals for use by the guest,
we have to take extra steps to exit properly.

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/signal.c | 52 ++++++++++++++++++++++++---------------------
 1 file changed, 28 insertions(+), 24 deletions(-)

diff --git a/linux-user/signal.c b/linux-user/signal.c
index a67ab47d30..b7a2c47837 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -689,13 +689,39 @@ 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 */
+    abort();
+}
+
 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);
     trace_user_dump_core_and_abort(env, target_sig, host_sig);
@@ -719,29 +745,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
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v5 2/9] linux-user: Exit not abort in die_with_backtrace
  2023-10-03 19:20 [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
  2023-10-03 19:20 ` [PATCH v5 1/9] linux-user: Split out die_with_signal Richard Henderson
@ 2023-10-03 19:20 ` Richard Henderson
  2023-10-03 19:20 ` [PATCH v5 3/9] linux-user: Detect and report host crashes Richard Henderson
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Richard Henderson @ 2023-10-03 19:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent, deller, Philippe Mathieu-Daudé

This line is supposed to be unreachable, but if we're going to
have it at all, SIGABRT via abort() is subject to the same signal
peril that created this function in the first place.

We can _exit immediately without peril.

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/signal.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/linux-user/signal.c b/linux-user/signal.c
index b7a2c47837..84a56b76cc 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -713,7 +713,7 @@ void die_with_signal(int host_sig)
     sigsuspend(&act.sa_mask);
 
     /* unreachable */
-    abort();
+    _exit(EXIT_FAILURE);
 }
 
 static G_NORETURN
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v5 3/9] linux-user: Detect and report host crashes
  2023-10-03 19:20 [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
  2023-10-03 19:20 ` [PATCH v5 1/9] linux-user: Split out die_with_signal Richard Henderson
  2023-10-03 19:20 ` [PATCH v5 2/9] linux-user: Exit not abort in die_with_backtrace Richard Henderson
@ 2023-10-03 19:20 ` Richard Henderson
  2023-10-03 19:20 ` [PATCH v5 4/9] linux-user: Only register handlers for core_dump_signal by default Richard Henderson
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Richard Henderson @ 2023-10-03 19:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent, deller

From: Helge Deller <deller@gmx.de>

If there is an internal program error in the qemu source code which
raises SIGSEGV or SIGBUS, we currently assume the signal belongs to
the guest.  With an artificial error introduced, we will now print

   QEMU internal SIGSEGV {code=MAPERR, addr=(nil)}

Signed-off-by: Helge Deller <deller@gmx.de>
Message-Id: <20230812164314.352131-1-deller@gmx.de>
[rth: Use in_code_gen_buffer and die_with_signal; drop backtrace]
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/signal.c | 69 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 63 insertions(+), 6 deletions(-)

diff --git a/linux-user/signal.c b/linux-user/signal.c
index 84a56b76cc..9fadc51347 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -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];
 
@@ -779,6 +780,50 @@ 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;
+    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_signal_handler(int host_sig, siginfo_t *info, void *puc)
 {
     CPUState *cpu = thread_cpu;
@@ -814,16 +859,28 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
         is_write = host_signal_write(info, uc);
         access_type = adjust_signal_pc(&pc, is_write);
 
+        /* If this was a write to a TB protected page, restart. */
+        if (is_write
+            && host_sig == SIGSEGV
+            && info->si_code == SEGV_ACCERR
+            && h2g_valid(host_addr)
+            && handle_sigsegv_accerr_write(cpu, sigmask, 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);
+        }
+
         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.
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v5 4/9] linux-user: Only register handlers for core_dump_signal by default
  2023-10-03 19:20 [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
                   ` (2 preceding siblings ...)
  2023-10-03 19:20 ` [PATCH v5 3/9] linux-user: Detect and report host crashes Richard Henderson
@ 2023-10-03 19:20 ` Richard Henderson
  2023-10-03 19:20 ` [PATCH v5 5/9] linux-user: Map unsupported signals to an out-of-bounds value Richard Henderson
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Richard Henderson @ 2023-10-03 19:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent, deller

The set of fatal signals is really immaterial.  If one arrives,
and is unhandled, then the qemu process dies and the parent gets
the correct signal.

It is only for those signals which we would like to perform a
guest core dump instead of a host core dump that we need to catch.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/signal.c | 43 ++++++++++++++-----------------------------
 1 file changed, 14 insertions(+), 29 deletions(-)

diff --git a/linux-user/signal.c b/linux-user/signal.c
index 9fadc51347..aab05f8eec 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -488,26 +488,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)
 {
@@ -602,8 +582,9 @@ void signal_init(void)
            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))
+        if (core_dump_signal(i)) {
             sigaction(host_sig, &act, NULL);
+        }
     }
 }
 
@@ -997,7 +978,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;
 
@@ -1057,22 +1037,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);
         }
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v5 5/9] linux-user: Map unsupported signals to an out-of-bounds value
  2023-10-03 19:20 [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
                   ` (3 preceding siblings ...)
  2023-10-03 19:20 ` [PATCH v5 4/9] linux-user: Only register handlers for core_dump_signal by default Richard Henderson
@ 2023-10-03 19:20 ` Richard Henderson
  2023-10-03 19:20 ` [PATCH v5 6/9] linux-user: Simplify signal_init Richard Henderson
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Richard Henderson @ 2023-10-03 19:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent, deller

Do not return a valid signal number in one domain
when given an invalid signal number in the other domain.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/signal.c | 72 ++++++++++++++++++++++++---------------------
 1 file changed, 38 insertions(+), 34 deletions(-)

diff --git a/linux-user/signal.c b/linux-user/signal.c
index aab05f8eec..653fd2f9fd 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -44,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.
  */
@@ -57,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];
@@ -65,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];
 }
 
@@ -507,48 +511,48 @@ 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.
      */
-    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;
+    for (hsig = SIGRTMIN; 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++;
-            }
+    /* 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)
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v5 6/9] linux-user: Simplify signal_init
  2023-10-03 19:20 [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
                   ` (4 preceding siblings ...)
  2023-10-03 19:20 ` [PATCH v5 5/9] linux-user: Map unsupported signals to an out-of-bounds value Richard Henderson
@ 2023-10-03 19:20 ` Richard Henderson
  2023-10-03 19:20 ` [PATCH v5 7/9] linux-user: Split out host_sig{segv,bus}_handler Richard Henderson
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Richard Henderson @ 2023-10-03 19:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent, deller

Install the host signal handler at the same time we are
probing the target signals for SIG_IGN/SIG_DFL.  Ignore
unmapped target signals.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/signal.c | 42 ++++++++++++++++++++++--------------------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/linux-user/signal.c b/linux-user/signal.c
index 653fd2f9fd..09840b0eb0 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -558,10 +558,7 @@ static void signal_table_init(void)
 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();
@@ -572,23 +569,28 @@ 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;
-        }
-        /* 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 (core_dump_signal(i)) {
-            sigaction(host_sig, &act, NULL);
+
+    /*
+     * 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) {
+            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;
     }
 }
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v5 7/9] linux-user: Split out host_sig{segv,bus}_handler
  2023-10-03 19:20 [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
                   ` (5 preceding siblings ...)
  2023-10-03 19:20 ` [PATCH v5 6/9] linux-user: Simplify signal_init Richard Henderson
@ 2023-10-03 19:20 ` Richard Henderson
  2023-10-03 19:20 ` [PATCH v5 8/9] linux-user: Detect and report host SIGILL, SIGFPE, SIGTRAP Richard Henderson
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Richard Henderson @ 2023-10-03 19:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent, deller

Make host_signal_handler slightly easier to read.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/signal.c | 145 ++++++++++++++++++++++++++------------------
 1 file changed, 85 insertions(+), 60 deletions(-)

diff --git a/linux-user/signal.c b/linux-user/signal.c
index 09840b0eb0..706b8ac7a7 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -811,6 +811,80 @@ void die_from_signal(siginfo_t *info)
     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;
@@ -822,73 +896,23 @@ 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.
      */
-    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 this was a write to a TB protected page, restart. */
-        if (is_write
-            && host_sig == SIGSEGV
-            && info->si_code == SEGV_ACCERR
-            && h2g_valid(host_addr)
-            && handle_sigsegv_accerr_write(cpu, sigmask, pc, guest_addr)) {
+    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;
         }
-
-        /*
-         * 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);
-        }
-
-        if (host_sig == SIGSEGV) {
-            bool maperr = true;
-
-            if (info->si_code == SEGV_ACCERR && h2g_valid(host_addr)) {
-                /*
-                 * 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);
-            }
-        }
-
-        sync_sig = true;
     }
 
     /* get target signal number */
@@ -929,6 +953,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);
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v5 8/9] linux-user: Detect and report host SIGILL, SIGFPE, SIGTRAP
  2023-10-03 19:20 [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
                   ` (6 preceding siblings ...)
  2023-10-03 19:20 ` [PATCH v5 7/9] linux-user: Split out host_sig{segv,bus}_handler Richard Henderson
@ 2023-10-03 19:20 ` Richard Henderson
  2023-10-03 19:20 ` [PATCH v5 9/9] linux-user: Remap guest SIGABRT Richard Henderson
  2023-10-13  3:52 ` [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
  9 siblings, 0 replies; 12+ messages in thread
From: Richard Henderson @ 2023-10-03 19:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent, deller

These signals, when not spoofed via kill(), are always bugs.
Use die_from_signal to report this sensibly.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/signal.c | 44 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/linux-user/signal.c b/linux-user/signal.c
index 706b8ac7a7..b67077f320 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -796,6 +796,43 @@ void die_from_signal(siginfo_t *info)
             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;
@@ -900,7 +937,8 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
 
     /*
      * 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 (info->si_code > 0) {
         switch (host_sig) {
@@ -912,6 +950,10 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
             host_sigbus_handler(cpu, info, uc);
             sync_sig = true;
             break;
+        case SIGILL:
+        case SIGFPE:
+        case SIGTRAP:
+            die_from_signal(info);
         }
     }
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v5 9/9] linux-user: Remap guest SIGABRT
  2023-10-03 19:20 [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
                   ` (7 preceding siblings ...)
  2023-10-03 19:20 ` [PATCH v5 8/9] linux-user: Detect and report host SIGILL, SIGFPE, SIGTRAP Richard Henderson
@ 2023-10-03 19:20 ` Richard Henderson
  2023-10-13  3:52 ` [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
  9 siblings, 0 replies; 12+ messages in thread
From: Richard Henderson @ 2023-10-03 19:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent, deller

Distinguish host SIGABRT from guest SIGABRT by mapping
the guest signal onto one of the host RT signals.

This prevents a cycle by which a host assertion failure
is caught and handled by host_signal_handler, queued for
the guest, and then we attempt to continue past the
host abort.  What happens next depends on the host libc,
but is neither good nor helpful.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/signal.c | 35 ++++++++++++++++++++++++++++-------
 1 file changed, 28 insertions(+), 7 deletions(-)

diff --git a/linux-user/signal.c b/linux-user/signal.c
index b67077f320..b7f4ce3cb9 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -522,8 +522,16 @@ static void signal_table_init(void)
      * 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.
      */
-    for (hsig = SIGRTMIN; hsig <= SIGRTMAX; hsig++) {
+
+    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;
@@ -582,13 +590,21 @@ void signal_init(void)
         int hsig = target_to_host_signal(tsig);
         abi_ptr thand = TARGET_SIG_IGN;
 
-        if (hsig < _NSIG) {
-            struct sigaction *iact = core_dump_signal(tsig) ? &act : NULL;
+        if (hsig >= _NSIG) {
+            continue;
+        }
 
+        /* 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;
-            }
+        }
+
+        if (oact.sa_sigaction != (void *)SIG_IGN) {
+            thand = TARGET_SIG_DFL;
         }
         sigact_table[tsig - 1]._sa_handler = thand;
     }
@@ -711,7 +727,12 @@ void dump_core_and_abort(CPUArchState *env, int target_sig)
     TaskState *ts = (TaskState *)cpu->opaque;
     int host_sig, core_dumped = 0;
 
-    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);
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v5 0/9] linux-user: Detect and report host crashes
  2023-10-03 19:20 [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
                   ` (8 preceding siblings ...)
  2023-10-03 19:20 ` [PATCH v5 9/9] linux-user: Remap guest SIGABRT Richard Henderson
@ 2023-10-13  3:52 ` Richard Henderson
  2023-10-13 12:53   ` Helge Deller
  9 siblings, 1 reply; 12+ messages in thread
From: Richard Henderson @ 2023-10-13  3:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent, deller

Ping.

On 10/3/23 12:20, Richard Henderson wrote:
> More signal cleanups.  Mostly tested by temporarily adding an
> abort, divide by zero, undefined instruction, null dereference,
> within the implementation of a guest syscall to induce an error.
> 
> Changes for v5:
>    * Remap guest abort, which means we need to try less hard on
>      the host side to handle assertion failures.
>    * Drop the backtrace.  Since backtrace_symbols only looks at the
>      dynamic symbol set, we don't much that's useful -- we still
>      need to use the debugger.
> 
> 
> r~
> 
> 
> Helge Deller (1):
>    linux-user: Detect and report host crashes
> 
> Richard Henderson (8):
>    linux-user: Split out die_with_signal
>    linux-user: Exit not abort in die_with_backtrace
>    linux-user: Only register handlers for core_dump_signal by default
>    linux-user: Map unsupported signals to an out-of-bounds value
>    linux-user: Simplify signal_init
>    linux-user: Split out host_sig{segv,bus}_handler
>    linux-user: Detect and report host SIGILL, SIGFPE, SIGTRAP
>    linux-user: Remap guest SIGABRT
> 
>   linux-user/signal.c | 452 +++++++++++++++++++++++++++++---------------
>   1 file changed, 296 insertions(+), 156 deletions(-)
> 



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v5 0/9] linux-user: Detect and report host crashes
  2023-10-13  3:52 ` [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
@ 2023-10-13 12:53   ` Helge Deller
  0 siblings, 0 replies; 12+ messages in thread
From: Helge Deller @ 2023-10-13 12:53 UTC (permalink / raw)
  To: Richard Henderson, qemu-devel; +Cc: laurent

On 10/13/23 05:52, Richard Henderson wrote:
> On 10/3/23 12:20, Richard Henderson wrote:
>> More signal cleanups.  Mostly tested by temporarily adding an
>> abort, divide by zero, undefined instruction, null dereference,
>> within the implementation of a guest syscall to induce an error.
>>
>> Changes for v5:
>>    * Remap guest abort, which means we need to try less hard on
>>      the host side to handle assertion failures.
>>    * Drop the backtrace.  Since backtrace_symbols only looks at the
>>      dynamic symbol set, we don't much that's useful -- we still
>>      need to use the debugger.

I've attached this series on git head and did some basic tests.
My various chroots still work, and after adding some temporary code to trigger
a segfault in qemu code it works as expected and reports the fault.

So:
Acked-by: Helge Deller <deller@gmx.de>

Thank you!
Helge


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2023-10-13 12:54 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-10-03 19:20 [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
2023-10-03 19:20 ` [PATCH v5 1/9] linux-user: Split out die_with_signal Richard Henderson
2023-10-03 19:20 ` [PATCH v5 2/9] linux-user: Exit not abort in die_with_backtrace Richard Henderson
2023-10-03 19:20 ` [PATCH v5 3/9] linux-user: Detect and report host crashes Richard Henderson
2023-10-03 19:20 ` [PATCH v5 4/9] linux-user: Only register handlers for core_dump_signal by default Richard Henderson
2023-10-03 19:20 ` [PATCH v5 5/9] linux-user: Map unsupported signals to an out-of-bounds value Richard Henderson
2023-10-03 19:20 ` [PATCH v5 6/9] linux-user: Simplify signal_init Richard Henderson
2023-10-03 19:20 ` [PATCH v5 7/9] linux-user: Split out host_sig{segv,bus}_handler Richard Henderson
2023-10-03 19:20 ` [PATCH v5 8/9] linux-user: Detect and report host SIGILL, SIGFPE, SIGTRAP Richard Henderson
2023-10-03 19:20 ` [PATCH v5 9/9] linux-user: Remap guest SIGABRT Richard Henderson
2023-10-13  3:52 ` [PATCH v5 0/9] linux-user: Detect and report host crashes Richard Henderson
2023-10-13 12:53   ` Helge Deller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).