aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authordigit@chromium.org <digit@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-10-09 18:03:25 +0000
committerdigit@chromium.org <digit@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-10-09 18:03:25 +0000
commitf72b9c6ff4b6c48d4a2239c7da31e5dbf220c440 (patch)
tree4ceb0e9c515073e1ab5cc914491f786f5d4ada44 /src/client
parentFix the Android unit tests. (diff)
downloadbreakpad-f72b9c6ff4b6c48d4a2239c7da31e5dbf220c440.tar.xz
Make Linux signal handler more robust.
Breakpad can be used on processes where a mistaken library saves then restores one of our signal handlers with 'signal' instead of 'sigaction'. This loses the SA_SIGINFO flag associated with the Breakpad handler, and in some cases (e.g. Android/ARM kernels), the values of the 'info' and 'uc' parameters that ExceptionHandler::SignalHandler() receives will be completely bogus, leading to a crash when the function is executed (and of course, no minidump generation). To work-around this, have SignalHandler() check the state of the flag. If it is incorrectly unset, re-register with 'sigaction' and the correct flag, then return. The signal will be re-thrown, and this time the function will be called with the correct values. Review URL: https://breakpad.appspot.com/481002 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1067 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/client')
-rw-r--r--src/client/linux/handler/exception_handler.cc29
-rw-r--r--src/client/linux/handler/exception_handler_unittest.cc31
2 files changed, 60 insertions, 0 deletions
diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc
index aad2a0e0..9a56c14b 100644
--- a/src/client/linux/handler/exception_handler.cc
+++ b/src/client/linux/handler/exception_handler.cc
@@ -292,6 +292,35 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
// All the exception signals are blocked at this point.
pthread_mutex_lock(&handler_stack_mutex_);
+ // Sometimes, Breakpad runs inside a process where some other buggy code
+ // saves and restores signal handlers temporarily with 'signal'
+ // instead of 'sigaction'. This loses the SA_SIGINFO flag associated
+ // with this function. As a consequence, the values of 'info' and 'uc'
+ // become totally bogus, generally inducing a crash.
+ //
+ // The following code tries to detect this case. When it does, it
+ // resets the signal handlers with sigaction + SA_SIGINFO and returns.
+ // This forces the signal to be thrown again, but this time the kernel
+ // will call the function with the right arguments.
+ struct sigaction cur_handler;
+ if (sigaction(sig, NULL, &cur_handler) == 0 &&
+ (cur_handler.sa_flags & SA_SIGINFO) == 0) {
+ // Reset signal handler with the right flags.
+ sigemptyset(&cur_handler.sa_mask);
+ sigaddset(&cur_handler.sa_mask, sig);
+
+ cur_handler.sa_sigaction = SignalHandler;
+ cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO;
+
+ if (sigaction(sig, &cur_handler, NULL) == -1) {
+ // When resetting the handler fails, try to reset the
+ // default one to avoid an infinite loop here.
+ signal(sig, SIG_DFL);
+ }
+ pthread_mutex_unlock(&handler_stack_mutex_);
+ return;
+ }
+
bool handled = false;
for (int i = handler_stack_->size() - 1; !handled && i >= 0; --i) {
handled = (*handler_stack_)[i]->HandleSignal(sig, info, uc);
diff --git a/src/client/linux/handler/exception_handler_unittest.cc b/src/client/linux/handler/exception_handler_unittest.cc
index 890061e6..bb463510 100644
--- a/src/client/linux/handler/exception_handler_unittest.cc
+++ b/src/client/linux/handler/exception_handler_unittest.cc
@@ -317,6 +317,37 @@ TEST(ExceptionHandlerTest, RedeliveryToDefaultHandler) {
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
}
+// Check that saving and restoring the signal handler with 'signal'
+// instead of 'sigaction' doesn't make the Breakpad signal handler
+// crash. See comments in ExceptionHandler::SignalHandler for full
+// details.
+TEST(ExceptionHandlerTest, RedeliveryOnBadSignalHandlerFlag) {
+ AutoTempDir temp_dir;
+ const pid_t child = fork();
+ if (child == 0) {
+ // Install the RaiseSIGKILL handler for SIGSEGV.
+ ASSERT_TRUE(InstallRaiseSIGKILL());
+
+ // Create a new exception handler, this installs a new SIGSEGV
+ // handler, after saving the old one.
+ ExceptionHandler handler(
+ MinidumpDescriptor(temp_dir.path()), NULL,
+ DoneCallbackReturnFalse, NULL, true, -1);
+
+ // Install the default SIGSEGV handler, saving the current one.
+ // Then re-install the current one with 'signal', this loses the
+ // SA_SIGINFO flag associated with the Breakpad handler.
+ sighandler_t old_handler = signal(SIGSEGV, SIG_DFL);
+ ASSERT_NE(old_handler, SIG_ERR);
+ ASSERT_NE(signal(SIGSEGV, old_handler), SIG_ERR);
+
+ // Crash with the exception handler in scope.
+ *reinterpret_cast<volatile int*>(NULL) = 0;
+ }
+ // SIGKILL means Breakpad's signal handler didn't crash.
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
+}
+
TEST(ExceptionHandlerTest, StackedHandlersDeliveredToTop) {
AutoTempDir temp_dir;