From 662b6da59dd0218ea5496c085cb010d59f2b4219 Mon Sep 17 00:00:00 2001 From: "ted.mielczarek" Date: Fri, 27 Aug 2010 13:18:49 +0000 Subject: Allow Linux dumper to work on PTRACE-hardened kernels A=Chris Coulson R=nealsid at http://breakpad.appspot.com/166001/show git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@673 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/client/linux/handler/exception_handler.cc | 60 +++++++++++++++++++++++++++ src/client/linux/handler/exception_handler.h | 9 ++++ 2 files changed, 69 insertions(+) (limited to 'src/client/linux') diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc index f3a7aa85..87cfa3a2 100644 --- a/src/client/linux/handler/exception_handler.cc +++ b/src/client/linux/handler/exception_handler.cc @@ -89,6 +89,11 @@ #include "common/linux/memory.h" #include "client/linux/minidump_writer/minidump_writer.h" #include "common/linux/guid_creator.h" +#include "common/linux/eintr_wrapper.h" + +#ifndef PR_SET_PTRACER +#define PR_SET_PTRACER 0x59616d61 +#endif // A wrapper for the tgkill syscall: send a signal to a specific thread. static int tgkill(pid_t tgid, pid_t tid, int sig) { @@ -301,6 +306,11 @@ struct ThreadArgument { // static int ExceptionHandler::ThreadEntry(void *arg) { const ThreadArgument *thread_arg = reinterpret_cast(arg); + + // Block here until the crashing process unblocks us when + // we're allowed to use ptrace + thread_arg->handler->WaitForContinueSignal(); + return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, thread_arg->context_size) == false; } @@ -355,14 +365,35 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) { thread_arg.context = context; thread_arg.context_size = sizeof(*context); + // We need to explicitly enable ptrace of parent processes on some + // kernels, but we need to know the PID of the cloned process before we + // can do this. Create a pipe here which we can use to block the + // cloned process after creating it, until we have explicitly enabled ptrace + if(sys_pipe(fdes) == -1) { + // Creating the pipe failed. We'll log an error but carry on anyway, + // as we'll probably still get a useful crash report. All that will happen + // is the write() and read() calls will fail with EBADF + static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \ + sys_pipe failed:"; + sys_write(2, no_pipe_msg, sizeof(no_pipe_msg) - 1); + sys_write(2, strerror(errno), strlen(strerror(errno))); + sys_write(2, "\n", 1); + } + const pid_t child = sys_clone( ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, &thread_arg, NULL, NULL, NULL); int r, status; + // Allow the child to ptrace us + prctl(PR_SET_PTRACER, child, 0, 0, 0); + SendContinueSignalToChild(); do { r = sys_waitpid(child, &status, __WALL); } while (r == -1 && errno == EINTR); + sys_close(fdes[0]); + sys_close(fdes[1]); + if (r == -1) { static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:"; sys_write(2, msg, sizeof(msg) - 1); @@ -379,6 +410,35 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) { return success; } +// This function runs in a compromised context: see the top of the file. +void ExceptionHandler::SendContinueSignalToChild() { + static const char okToContinueMessage = 'a'; + int r; + r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char))); + if(r == -1) { + static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \ + sys_write failed:"; + sys_write(2, msg, sizeof(msg) - 1); + sys_write(2, strerror(errno), strlen(strerror(errno))); + sys_write(2, "\n", 1); + } +} + +// This function runs in a compromised context: see the top of the file. +// Runs on the cloned process. +void ExceptionHandler::WaitForContinueSignal() { + int r; + char receivedMessage; + r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char))); + if(r == -1) { + static const char msg[] = "ExceptionHandler::WaitForContinueSignal \ + sys_read failed:"; + sys_write(2, msg, sizeof(msg) - 1); + sys_write(2, strerror(errno), strlen(strerror(errno))); + sys_write(2, "\n", 1); + } +} + // This function runs in a compromised context: see the top of the file. // Runs on the cloned process. bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, diff --git a/src/client/linux/handler/exception_handler.h b/src/client/linux/handler/exception_handler.h index 2a497037..497b1cc0 100644 --- a/src/client/linux/handler/exception_handler.h +++ b/src/client/linux/handler/exception_handler.h @@ -184,6 +184,8 @@ class ExceptionHandler { void UninstallHandlers(); void PreresolveSymbols(); bool GenerateDump(CrashContext *context); + void SendContinueSignalToChild(); + void WaitForContinueSignal(); void UpdateNextID(); static void SignalHandler(int sig, siginfo_t* info, void* uc); @@ -223,6 +225,13 @@ class ExceptionHandler { // A vector of the old signal handlers. std::vector > old_handlers_; + + // We need to explicitly enable ptrace of parent processes on some + // kernels, but we need to know the PID of the cloned process before we + // can do this. We create a pipe which we can use to block the + // cloned process after creating it, until we have explicitly enabled + // ptrace. This is used to store the file descriptors for the pipe + int fdes[2]; }; } // namespace google_breakpad -- cgit v1.2.1