Skip to content
Snippets Groups Projects
Commit 29b838c0 authored by David Hildenbrand's avatar David Hildenbrand Committed by Michael S. Tsirkin
Browse files

util/oslib-posix: Forward SIGBUS to MCE handler under Linux


Temporarily modifying the SIGBUS handler is really nasty, as we might be
unlucky and receive an MCE SIGBUS while having our handler registered.
Unfortunately, there is no way around messing with SIGBUS when
MADV_POPULATE_WRITE is not applicable or not around.

Let's forward SIGBUS that don't belong to us to the already registered
handler and document the situation.

Reviewed-by: default avatarDaniel P. Berrangé <berrange@redhat.com>
Reviewed-by: default avatarMichal Privoznik <mprivozn@redhat.com>
Signed-off-by: default avatarDavid Hildenbrand <david@redhat.com>
Message-Id: <20211217134611.31172-8-david@redhat.com>
Reviewed-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent a960d664
No related branches found
No related tags found
No related merge requests found
......@@ -352,6 +352,10 @@ static void qemu_init_sigbus(void)
{
struct sigaction action;
/*
* ALERT: when modifying this, take care that SIGBUS forwarding in
* os_mem_prealloc() will continue working as expected.
*/
memset(&action, 0, sizeof(action));
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = sigbus_handler;
......
......@@ -35,6 +35,7 @@
#include "sysemu/sysemu.h"
#include "trace.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"
#include "qemu/thread.h"
#include <libgen.h>
......@@ -95,6 +96,7 @@ typedef struct MemsetThread MemsetThread;
/* used by sigbus_handler() */
static MemsetContext *sigbus_memset_context;
struct sigaction sigbus_oldact;
static QemuMutex sigbus_mutex;
static QemuMutex page_mutex;
......@@ -446,7 +448,11 @@ const char *qemu_get_exec_dir(void)
return exec_dir;
}
#ifdef CONFIG_LINUX
static void sigbus_handler(int signal, siginfo_t *siginfo, void *ctx)
#else /* CONFIG_LINUX */
static void sigbus_handler(int signal)
#endif /* CONFIG_LINUX */
{
int i;
......@@ -459,6 +465,26 @@ static void sigbus_handler(int signal)
}
}
}
#ifdef CONFIG_LINUX
/*
* We assume that the MCE SIGBUS handler could have been registered. We
* should never receive BUS_MCEERR_AO on any of our threads, but only on
* the main thread registered for PR_MCE_KILL_EARLY. Further, we should not
* receive BUS_MCEERR_AR triggered by action of other threads on one of
* our threads. So, no need to check for unrelated SIGBUS when seeing one
* for our threads.
*
* We will forward to the MCE handler, which will either handle the SIGBUS
* or reinstall the default SIGBUS handler and reraise the SIGBUS. The
* default SIGBUS handler will crash the process, so we don't care.
*/
if (sigbus_oldact.sa_flags & SA_SIGINFO) {
sigbus_oldact.sa_sigaction(signal, siginfo, ctx);
return;
}
#endif /* CONFIG_LINUX */
warn_report("os_mem_prealloc: unrelated SIGBUS detected and ignored");
}
static void *do_touch_pages(void *arg)
......@@ -628,10 +654,10 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
{
static gsize initialized;
int ret;
struct sigaction act, oldact;
size_t hpagesize = qemu_fd_getpagesize(fd);
size_t numpages = DIV_ROUND_UP(memory, hpagesize);
bool use_madv_populate_write;
struct sigaction act;
/*
* Sense on every invocation, as MADV_POPULATE_WRITE cannot be used for
......@@ -647,10 +673,15 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
qemu_mutex_lock(&sigbus_mutex);
memset(&act, 0, sizeof(act));
#ifdef CONFIG_LINUX
act.sa_sigaction = &sigbus_handler;
act.sa_flags = SA_SIGINFO;
#else /* CONFIG_LINUX */
act.sa_handler = &sigbus_handler;
act.sa_flags = 0;
#endif /* CONFIG_LINUX */
ret = sigaction(SIGBUS, &act, &oldact);
ret = sigaction(SIGBUS, &act, &sigbus_oldact);
if (ret) {
error_setg_errno(errp, errno,
"os_mem_prealloc: failed to install signal handler");
......@@ -667,7 +698,7 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
}
if (!use_madv_populate_write) {
ret = sigaction(SIGBUS, &oldact, NULL);
ret = sigaction(SIGBUS, &sigbus_oldact, NULL);
if (ret) {
/* Terminate QEMU since it can't recover from error */
perror("os_mem_prealloc: failed to reinstall signal handler");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment