8065895: Synchronous signals during error reporting may terminate or hang VM process
Reviewed-by: dholmes, gziemski
Contributed-by: Thomas Stuefe <thomas.stuefe@gmail.com>
--- a/hotspot/src/os/aix/vm/vmError_aix.cpp Wed Feb 04 16:09:49 2015 +0100
+++ b/hotspot/src/os/aix/vm/vmError_aix.cpp Wed Feb 04 18:47:42 2015 -0500
@@ -80,7 +80,6 @@
}
int VMError::get_resetted_sigflags(int sig) {
- // Handle all program errors.
for (int i = 0; i < NUM_SIGNALS; i++) {
if (SIGNALS[i] == sig) {
return resettedSigflags[i];
@@ -90,7 +89,6 @@
}
address VMError::get_resetted_sighandler(int sig) {
- // Handle all program errors.
for (int i = 0; i < NUM_SIGNALS; i++) {
if (SIGNALS[i] == sig) {
return resettedSighandler[i];
@@ -100,12 +98,19 @@
}
static void crash_handler(int sig, siginfo_t* info, void* ucVoid) {
+
// Unmask current signal.
sigset_t newset;
sigemptyset(&newset);
sigaddset(&newset, sig);
+ // and all other synchronous signals too.
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ sigaddset(&newset, SIGNALS[i]);
+ }
+ sigthreadmask(SIG_UNBLOCK, &newset, NULL);
- Unimplemented();
+ VMError err(NULL, sig, NULL, info, ucVoid);
+ err.report_and_die();
}
void VMError::reset_signal_handlers() {
--- a/hotspot/src/os/bsd/vm/vmError_bsd.cpp Wed Feb 04 16:09:49 2015 +0100
+++ b/hotspot/src/os/bsd/vm/vmError_bsd.cpp Wed Feb 04 18:47:42 2015 -0500
@@ -63,9 +63,15 @@
} while (yes);
}
+// handle all synchronous program error signals which may happen during error
+// reporting. They must be unblocked, caught, handled.
+
+static const int SIGNALS[] = { SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP }; // add more if needed
+static const int NUM_SIGNALS = sizeof(SIGNALS) / sizeof(int);
+
// Space for our "saved" signal flags and handlers
-static int resettedSigflags[2];
-static address resettedSighandler[2];
+static int resettedSigflags[NUM_SIGNALS];
+static address resettedSighandler[NUM_SIGNALS];
static void save_signal(int idx, int sig)
{
@@ -78,19 +84,19 @@
}
int VMError::get_resetted_sigflags(int sig) {
- if(SIGSEGV == sig) {
- return resettedSigflags[0];
- } else if(SIGBUS == sig) {
- return resettedSigflags[1];
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ if (SIGNALS[i] == sig) {
+ return resettedSigflags[i];
+ }
}
return -1;
}
address VMError::get_resetted_sighandler(int sig) {
- if(SIGSEGV == sig) {
- return resettedSighandler[0];
- } else if(SIGBUS == sig) {
- return resettedSighandler[1];
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ if (SIGNALS[i] == sig) {
+ return resettedSighandler[i];
+ }
}
return NULL;
}
@@ -100,16 +106,25 @@
sigset_t newset;
sigemptyset(&newset);
sigaddset(&newset, sig);
- sigprocmask(SIG_UNBLOCK, &newset, NULL);
+ // also unmask other synchronous signals
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ sigaddset(&newset, SIGNALS[i]);
+ }
+ pthread_sigmask(SIG_UNBLOCK, &newset, NULL);
VMError err(NULL, sig, NULL, info, ucVoid);
err.report_and_die();
}
void VMError::reset_signal_handlers() {
- // Save sigflags for resetted signals
- save_signal(0, SIGSEGV);
- save_signal(1, SIGBUS);
- os::signal(SIGSEGV, CAST_FROM_FN_PTR(void *, crash_handler));
- os::signal(SIGBUS, CAST_FROM_FN_PTR(void *, crash_handler));
+ // install signal handlers for all synchronous program error signals
+ sigset_t newset;
+ sigemptyset(&newset);
+
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ save_signal(i, SIGNALS[i]);
+ os::signal(SIGNALS[i], CAST_FROM_FN_PTR(void *, crash_handler));
+ sigaddset(&newset, SIGNALS[i]);
+ }
+ pthread_sigmask(SIG_UNBLOCK, &newset, NULL);
}
--- a/hotspot/src/os/linux/vm/vmError_linux.cpp Wed Feb 04 16:09:49 2015 +0100
+++ b/hotspot/src/os/linux/vm/vmError_linux.cpp Wed Feb 04 18:47:42 2015 -0500
@@ -63,9 +63,15 @@
} while (yes);
}
+// handle all synchronous program error signals which may happen during error
+// reporting. They must be unblocked, caught, handled.
+
+static const int SIGNALS[] = { SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP }; // add more if needed
+static const int NUM_SIGNALS = sizeof(SIGNALS) / sizeof(int);
+
// Space for our "saved" signal flags and handlers
-static int resettedSigflags[2];
-static address resettedSighandler[2];
+static int resettedSigflags[NUM_SIGNALS];
+static address resettedSighandler[NUM_SIGNALS];
static void save_signal(int idx, int sig)
{
@@ -78,19 +84,19 @@
}
int VMError::get_resetted_sigflags(int sig) {
- if(SIGSEGV == sig) {
- return resettedSigflags[0];
- } else if(SIGBUS == sig) {
- return resettedSigflags[1];
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ if (SIGNALS[i] == sig) {
+ return resettedSigflags[i];
+ }
}
return -1;
}
address VMError::get_resetted_sighandler(int sig) {
- if(SIGSEGV == sig) {
- return resettedSighandler[0];
- } else if(SIGBUS == sig) {
- return resettedSighandler[1];
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ if (SIGNALS[i] == sig) {
+ return resettedSighandler[i];
+ }
}
return NULL;
}
@@ -100,16 +106,26 @@
sigset_t newset;
sigemptyset(&newset);
sigaddset(&newset, sig);
- sigprocmask(SIG_UNBLOCK, &newset, NULL);
+ // also unmask other synchronous signals
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ sigaddset(&newset, SIGNALS[i]);
+ }
+ pthread_sigmask(SIG_UNBLOCK, &newset, NULL);
VMError err(NULL, sig, NULL, info, ucVoid);
err.report_and_die();
}
void VMError::reset_signal_handlers() {
- // Save sigflags for resetted signals
- save_signal(0, SIGSEGV);
- save_signal(1, SIGBUS);
- os::signal(SIGSEGV, CAST_FROM_FN_PTR(void *, crash_handler));
- os::signal(SIGBUS, CAST_FROM_FN_PTR(void *, crash_handler));
+ // install signal handlers for all synchronous program error signals
+ sigset_t newset;
+ sigemptyset(&newset);
+
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ save_signal(i, SIGNALS[i]);
+ os::signal(SIGNALS[i], CAST_FROM_FN_PTR(void *, crash_handler));
+ sigaddset(&newset, SIGNALS[i]);
+ }
+ pthread_sigmask(SIG_UNBLOCK, &newset, NULL);
+
}
--- a/hotspot/src/os/solaris/vm/vmError_solaris.cpp Wed Feb 04 16:09:49 2015 +0100
+++ b/hotspot/src/os/solaris/vm/vmError_solaris.cpp Wed Feb 04 18:47:42 2015 -0500
@@ -30,6 +30,7 @@
#include <sys/types.h>
#include <sys/wait.h>
+#include <thread.h>
#include <signal.h>
void VMError::show_message_box(char *buf, int buflen) {
@@ -59,9 +60,15 @@
} while (yes);
}
+// handle all synchronous program error signals which may happen during error
+// reporting. They must be unblocked, caught, handled.
+
+static const int SIGNALS[] = { SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP }; // add more if needed
+static const int NUM_SIGNALS = sizeof(SIGNALS) / sizeof(int);
+
// Space for our "saved" signal flags and handlers
-static int resettedSigflags[2];
-static address resettedSighandler[2];
+static int resettedSigflags[NUM_SIGNALS];
+static address resettedSighandler[NUM_SIGNALS];
static void save_signal(int idx, int sig)
{
@@ -74,19 +81,19 @@
}
int VMError::get_resetted_sigflags(int sig) {
- if(SIGSEGV == sig) {
- return resettedSigflags[0];
- } else if(SIGBUS == sig) {
- return resettedSigflags[1];
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ if (SIGNALS[i] == sig) {
+ return resettedSigflags[i];
+ }
}
return -1;
}
address VMError::get_resetted_sighandler(int sig) {
- if(SIGSEGV == sig) {
- return resettedSighandler[0];
- } else if(SIGBUS == sig) {
- return resettedSighandler[1];
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ if (SIGNALS[i] == sig) {
+ return resettedSighandler[i];
+ }
}
return NULL;
}
@@ -96,16 +103,25 @@
sigset_t newset;
sigemptyset(&newset);
sigaddset(&newset, sig);
- sigprocmask(SIG_UNBLOCK, &newset, NULL);
+ // also unmask other synchronous signals
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ sigaddset(&newset, SIGNALS[i]);
+ }
+ thr_sigsetmask(SIG_UNBLOCK, &newset, NULL);
VMError err(NULL, sig, NULL, info, ucVoid);
err.report_and_die();
}
void VMError::reset_signal_handlers() {
- // Save sigflags for resetted signals
- save_signal(0, SIGSEGV);
- save_signal(1, SIGBUS);
- os::signal(SIGSEGV, CAST_FROM_FN_PTR(void *, crash_handler));
- os::signal(SIGBUS, CAST_FROM_FN_PTR(void *, crash_handler));
+ // install signal handlers for all synchronous program error signals
+ sigset_t newset;
+ sigemptyset(&newset);
+
+ for (int i = 0; i < NUM_SIGNALS; i++) {
+ save_signal(i, SIGNALS[i]);
+ os::signal(SIGNALS[i], CAST_FROM_FN_PTR(void *, crash_handler));
+ sigaddset(&newset, SIGNALS[i]);
+ }
+ thr_sigsetmask(SIG_UNBLOCK, &newset, NULL);
}
--- a/hotspot/src/share/vm/runtime/globals.hpp Wed Feb 04 16:09:49 2015 +0100
+++ b/hotspot/src/share/vm/runtime/globals.hpp Wed Feb 04 18:47:42 2015 -0500
@@ -905,6 +905,10 @@
"determines which error to provoke. See test_error_handler() " \
"in debug.cpp.") \
\
+ notproduct(uintx, TestCrashInErrorHandler, 0, \
+ "If > 0, provokes an error inside VM error handler (a secondary " \
+ "crash). see test_error_handler() in debug.cpp.") \
+ \
develop(bool, Verbose, false, \
"Print additional debugging information from other modes") \
\
--- a/hotspot/src/share/vm/utilities/debug.cpp Wed Feb 04 16:09:49 2015 +0100
+++ b/hotspot/src/share/vm/utilities/debug.cpp Wed Feb 04 18:47:42 2015 -0500
@@ -316,13 +316,47 @@
#ifndef PRODUCT
#include <signal.h>
+typedef void (*voidfun_t)();
+// Crash with an authentic sigfpe
+static void crash_with_sigfpe() {
+ // generate a native synchronous SIGFPE where possible;
+ // if that did not cause a signal (e.g. on ppc), just
+ // raise the signal.
+ volatile int x = 0;
+ volatile int y = 1/x;
+#ifndef _WIN32
+ raise(SIGFPE);
+#endif
+} // end: crash_with_sigfpe
+
+// crash with sigsegv at non-null address.
+static void crash_with_segfault() {
+
+ char* const crash_addr = (char*) get_segfault_address();
+ *crash_addr = 'X';
+
+} // end: crash_with_segfault
+
+// returns an address which is guaranteed to generate a SIGSEGV on read,
+// for test purposes, which is not NULL and contains bits in every word
+void* get_segfault_address() {
+ return (void*)
+#ifdef _LP64
+ 0xABC0000000000ABCULL;
+#else
+ 0x00000ABC;
+#endif
+}
+
void test_error_handler() {
- uintx test_num = ErrorHandlerTest;
- if (test_num == 0) return;
+ controlled_crash(ErrorHandlerTest);
+}
+
+void controlled_crash(int how) {
+ if (how == 0) return;
// If asserts are disabled, use the corresponding guarantee instead.
- size_t n = test_num;
- NOT_DEBUG(if (n <= 2) n += 2);
+ NOT_DEBUG(if (how <= 2) how += 2);
const char* const str = "hello";
const size_t num = (size_t)os::vm_page_size();
@@ -333,7 +367,7 @@
const void (*funcPtr)(void) = (const void(*)()) 0xF; // bad function pointer
// Keep this in sync with test/runtime/6888954/vmerrors.sh.
- switch (n) {
+ switch (how) {
case 1: vmassert(str == NULL, "expected null");
case 2: vmassert(num == 1023 && *str == 'X',
err_msg("num=" SIZE_FORMAT " str=\"%s\"", num, str));
@@ -358,8 +392,10 @@
// There's no guarantee the bad function pointer will crash us
// so "break" out to the ShouldNotReachHere().
case 13: (*funcPtr)(); break;
+ case 14: crash_with_segfault(); break;
+ case 15: crash_with_sigfpe(); break;
- default: tty->print_cr("ERROR: %d: unexpected test_num value.", n);
+ default: tty->print_cr("ERROR: %d: unexpected test_num value.", how);
}
ShouldNotReachHere();
}
--- a/hotspot/src/share/vm/utilities/debug.hpp Wed Feb 04 16:09:49 2015 +0100
+++ b/hotspot/src/share/vm/utilities/debug.hpp Wed Feb 04 18:47:42 2015 -0500
@@ -248,6 +248,24 @@
/* Test vmassert(), fatal(), guarantee(), etc. */
NOT_PRODUCT(void test_error_handler();)
+// crash in a controlled way:
+// how can be one of:
+// 1,2 - asserts
+// 3,4 - guarantee
+// 5-7 - fatal
+// 8 - vm_exit_out_of_memory
+// 9 - ShouldNotCallThis
+// 10 - ShouldNotReachHere
+// 11 - Unimplemented
+// 12,13 - (not guaranteed) crashes
+// 14 - SIGSEGV
+// 15 - SIGFPE
+NOT_PRODUCT(void controlled_crash(int how);)
+
+// returns an address which is guaranteed to generate a SIGSEGV on read,
+// for test purposes, which is not NULL and contains bits in every word
+NOT_PRODUCT(void* get_segfault_address();)
+
void pd_ps(frame f);
void pd_obfuscate_location(char *buf, size_t buflen);
--- a/hotspot/src/share/vm/utilities/vmError.cpp Wed Feb 04 16:09:49 2015 +0100
+++ b/hotspot/src/share/vm/utilities/vmError.cpp Wed Feb 04 18:47:42 2015 -0500
@@ -353,6 +353,26 @@
"Runtime Environment to continue.");
}
+#ifndef PRODUCT
+ // Error handler self tests
+
+ // test secondary error handling. Test it twice, to test that resetting
+ // error handler after a secondary crash works.
+ STEP(13, "(test secondary crash 1)")
+ if (_verbose && TestCrashInErrorHandler != 0) {
+ st->print_cr("Will crash now (TestCrashInErrorHandler=%d)...",
+ TestCrashInErrorHandler);
+ controlled_crash(TestCrashInErrorHandler);
+ }
+
+ STEP(14, "(test secondary crash 2)")
+ if (_verbose && TestCrashInErrorHandler != 0) {
+ st->print_cr("Will crash now (TestCrashInErrorHandler=%d)...",
+ TestCrashInErrorHandler);
+ controlled_crash(TestCrashInErrorHandler);
+ }
+#endif // PRODUCT
+
STEP(15, "(printing type of error)")
switch(_id) {
@@ -786,6 +806,15 @@
st->cr();
}
+#ifndef PRODUCT
+ // print a defined marker to show that error handling finished correctly.
+ STEP(290, "(printing end marker)" )
+
+ if (_verbose) {
+ st->print_cr("END.");
+ }
+#endif
+
END
# undef BEGIN