From 69371858fda3c1793faf8bbf116ec4fe554605ac Mon Sep 17 00:00:00 2001
From: hedongbo <hedongbo@huawei.com>
Date: Fri, 21 Oct 2022 15:16:04 +0800
Subject: 8065895: Synchronous signals during error reporting may
 terminate or hang VM process

Bug url: https://bugs.openjdk.java.net/browse/JDK-8065895
---
 hotspot/src/os/aix/vm/vmError_aix.cpp         | 11 +++--
 hotspot/src/os/bsd/vm/vmError_bsd.cpp         | 47 +++++++++++-------
 hotspot/src/os/linux/vm/vmError_linux.cpp     | 48 ++++++++++++-------
 hotspot/src/os/solaris/vm/vmError_solaris.cpp | 48 ++++++++++++-------
 hotspot/src/share/vm/runtime/globals.hpp      |  4 ++
 hotspot/src/share/vm/utilities/debug.cpp      | 48 ++++++++++++++++---
 hotspot/src/share/vm/utilities/debug.hpp      | 18 +++++++
 hotspot/src/share/vm/utilities/vmError.cpp    | 29 +++++++++++
 8 files changed, 196 insertions(+), 57 deletions(-)

diff --git a/hotspot/src/os/aix/vm/vmError_aix.cpp b/hotspot/src/os/aix/vm/vmError_aix.cpp
index d99436ebc..34709134a 100644
--- a/hotspot/src/os/aix/vm/vmError_aix.cpp
+++ b/hotspot/src/os/aix/vm/vmError_aix.cpp
@@ -80,7 +80,6 @@ static void save_signal(int idx, int sig) {
 }
 
 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 @@ int VMError::get_resetted_sigflags(int sig) {
 }
 
 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 @@ address VMError::get_resetted_sighandler(int sig) {
 }
 
 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() {
diff --git a/hotspot/src/os/bsd/vm/vmError_bsd.cpp b/hotspot/src/os/bsd/vm/vmError_bsd.cpp
index 8ec6ca04c..f09e1163f 100644
--- a/hotspot/src/os/bsd/vm/vmError_bsd.cpp
+++ b/hotspot/src/os/bsd/vm/vmError_bsd.cpp
@@ -63,9 +63,15 @@ void VMError::show_message_box(char *buf, int buflen) {
   } 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 @@ static void save_signal(int idx, int sig)
 }
 
 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 @@ static void crash_handler(int sig, siginfo_t* info, void* ucVoid) {
   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);
 }
diff --git a/hotspot/src/os/linux/vm/vmError_linux.cpp b/hotspot/src/os/linux/vm/vmError_linux.cpp
index 378c9a6ab..fca239c7e 100644
--- a/hotspot/src/os/linux/vm/vmError_linux.cpp
+++ b/hotspot/src/os/linux/vm/vmError_linux.cpp
@@ -63,9 +63,15 @@ void VMError::show_message_box(char *buf, int buflen) {
   } 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 @@ static void save_signal(int idx, int sig)
 }
 
 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 @@ static void crash_handler(int sig, siginfo_t* info, void* ucVoid) {
   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);
+
 }
diff --git a/hotspot/src/os/solaris/vm/vmError_solaris.cpp b/hotspot/src/os/solaris/vm/vmError_solaris.cpp
index 6f3f5b06f..e24e5f6bf 100644
--- a/hotspot/src/os/solaris/vm/vmError_solaris.cpp
+++ b/hotspot/src/os/solaris/vm/vmError_solaris.cpp
@@ -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 @@ void VMError::show_message_box(char *buf, int buflen) {
   } 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 @@ static void save_signal(int idx, int sig)
 }
 
 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 @@ static void crash_handler(int sig, siginfo_t* info, void* ucVoid) {
   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);
 }
diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp
index eb13ee0d7..0dab18e1a 100644
--- a/hotspot/src/share/vm/runtime/globals.hpp
+++ b/hotspot/src/share/vm/runtime/globals.hpp
@@ -916,6 +916,10 @@ class CommandLineFlags {
           "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")        \
                                                                             \
diff --git a/hotspot/src/share/vm/utilities/debug.cpp b/hotspot/src/share/vm/utilities/debug.cpp
index 58a32a2b8..8cea16d32 100644
--- a/hotspot/src/share/vm/utilities/debug.cpp
+++ b/hotspot/src/share/vm/utilities/debug.cpp
@@ -337,13 +337,47 @@ bool is_error_reported() {
 #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();
@@ -354,7 +388,7 @@ void test_error_handler() {
   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: assert(str == NULL, "expected null");
     case  2: assert(num == 1023 && *str == 'X',
                     err_msg("num=" SIZE_FORMAT " str=\"%s\"", num, str));
@@ -379,8 +413,10 @@ void test_error_handler() {
     // 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();
 }
diff --git a/hotspot/src/share/vm/utilities/debug.hpp b/hotspot/src/share/vm/utilities/debug.hpp
index 3c3f8afe2..7a5e1523b 100644
--- a/hotspot/src/share/vm/utilities/debug.hpp
+++ b/hotspot/src/share/vm/utilities/debug.hpp
@@ -266,6 +266,24 @@ void set_error_reported();
 /* Test assert(), 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);
 
diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp
index aa0b63a80..9b40a3468 100644
--- a/hotspot/src/share/vm/utilities/vmError.cpp
+++ b/hotspot/src/share/vm/utilities/vmError.cpp
@@ -397,6 +397,26 @@ void VMError::report(outputStream* st) {
                    "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(static_cast<unsigned int>(_id)) {
@@ -829,6 +849,15 @@ void VMError::report(outputStream* st) {
        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
-- 
2.22.0

