Allow Linux dumper to work on PTRACE-hardened kernels (Ubuntu 10.10). r=nealsid a=blocking
☠☠ backed out by 97beb099e49a ☠ ☠
authorChris Coulson <chrisccoulson@ubuntu.com>
Fri, 27 Aug 2010 07:01:00 -0400
changeset 51792 e996bccb391dbcff31272bbcaf3202b18da3a399
parent 51791 eea1c03b2d2731885f33fce882549b54ddb4efd1
child 51793 d2800a98321ed4564f080f60f0518a30ed665e49
child 51796 97beb099e49af88cdb8b8a63211f85a4e7692a48
push id15429
push usertmielczarek@mozilla.com
push dateTue, 31 Aug 2010 19:14:08 +0000
treeherdermozilla-central@3dd650be4ddc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnealsid, blocking
milestone2.0b6pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Allow Linux dumper to work on PTRACE-hardened kernels (Ubuntu 10.10). r=nealsid a=blocking
toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc
toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc
@@ -84,16 +84,21 @@
 #include <algorithm>
 #include <vector>
 
 #include "common/linux/linux_libc_support.h"
 #include "common/linux/linux_syscall_support.h"
 #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) {
   return syscall(__NR_tgkill, tgid, tid, sig);
   return 0;
 }
 
 namespace google_breakpad {
@@ -284,16 +289,21 @@ struct ThreadArgument {
   size_t context_size;
 };
 
 // This is the entry function for the cloned process. We are in a compromised
 // context here: see the top of the file.
 // static
 int ExceptionHandler::ThreadEntry(void *arg) {
   const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(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;
 }
 
 // This function runs in a compromised context: see the top of the file.
 // Runs on the crashing thread.
 bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
   if (filter_ && !filter_(callback_context_))
@@ -338,24 +348,45 @@ bool ExceptionHandler::GenerateDump(Cras
   my_memset(stack - 16, 0, 16);
 
   ThreadArgument thread_arg;
   thread_arg.handler = this;
   thread_arg.pid = getpid();
   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);
     sys_write(2, strerror(errno), strlen(strerror(errno)));
     sys_write(2, "\n", 1);
   }
 
   bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
@@ -363,16 +394,45 @@ bool ExceptionHandler::GenerateDump(Cras
   if (callback_)
     success = callback_(dump_path_c_, next_minidump_id_c_,
                         callback_context_, success);
 
   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,
                               size_t context_size) {
   return google_breakpad::WriteMinidump(
       next_minidump_path_c_, crashing_process, context, context_size);
 }
 
 // static
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h
@@ -207,16 +207,18 @@ class ExceptionHandler {
 
  private:
   void Init(const std::string &dump_path,
             const int server_fd);
   bool InstallHandlers();
   void UninstallHandlers();
   void PreresolveSymbols();
   bool GenerateDump(CrashContext *context);
+  void SendContinueSignalToChild();
+  void WaitForContinueSignal();
 
   void UpdateNextID();
   static void SignalHandler(int sig, siginfo_t* info, void* uc);
   bool HandleSignal(int sig, siginfo_t* info, void* uc);
   static int ThreadEntry(void* arg);
   bool DoDump(pid_t crashing_process, const void* context,
               size_t context_size);
 
@@ -246,13 +248,20 @@ class ExceptionHandler {
   // registered in this stack.
   static std::vector<ExceptionHandler*> *handler_stack_;
   // The index of the handler that should handle the next exception.
   static unsigned handler_stack_index_;
   static pthread_mutex_t handler_stack_mutex_;
 
   // A vector of the old signal handlers.
   std::vector<std::pair<int, struct sigaction *> > 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
 
 #endif  // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_