Bug 907957 - Detect if a segfault signal handler is useless. If it is, disable on-demand decompression. r=nfroyd, a=akeybl
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 30 Aug 2013 11:14:17 +0900
changeset 153974 ed9260da09cb6334f13e72a7311c6aa033150d91
parent 153973 f7b964eedcc8edb04b78f0dd48e619d7472aa117
child 153975 5da9972833a630be65a32bd509d6d2d3dacc7b50
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroyd, akeybl
bugs907957
milestone25.0a2
Bug 907957 - Detect if a segfault signal handler is useless. If it is, disable on-demand decompression. r=nfroyd, a=akeybl
mozglue/linker/ElfLoader.cpp
mozglue/linker/ElfLoader.h
--- a/mozglue/linker/ElfLoader.cpp
+++ b/mozglue/linker/ElfLoader.cpp
@@ -147,16 +147,22 @@ MFBT_API void *
 MFBT_API void
 __dl_munmap(void *handle, void *addr, size_t length)
 {
   if (!handle)
     return;
   return reinterpret_cast<LibHandle *>(handle)->MappableMUnmap(addr, length);
 }
 
+MFBT_API bool
+IsSignalHandlingBroken()
+{
+  return ElfLoader::Singleton.isSignalHandlingBroken();
+}
+
 namespace {
 
 /**
  * Returns the part after the last '/' for the given path
  */
 const char *
 LeafName(const char *path)
 {
@@ -892,48 +898,97 @@ template <typename T>
 static bool
 Divert(T func, T new_func)
 {
   return false;
 }
 #endif
 
 SEGVHandler::SEGVHandler()
-: registeredHandler(false)
+: registeredHandler(false), signalHandlingBroken(false)
 {
+  /* Initialize oldStack.ss_flags to an invalid value when used to set
+   * an alternative stack, meaning we haven't got information about the
+   * original alternative stack and thus don't mean to restore it */
+  oldStack.ss_flags = SS_ONSTACK;
   if (!Divert(sigaction, __wrap_sigaction))
     return;
+
+  /* Get the current segfault signal handler. */
+  sys_sigaction(SIGSEGV, NULL, &this->action);
+
+  /* Some devices don't provide useful information to their SIGSEGV handlers,
+   * making it impossible for on-demand decompression to work. To check if
+   * we're on such a device, setup a temporary handler and deliberately
+   * trigger a segfault. The handler will set signalHandlingBroken if the
+   * provided information is bogus. */
+  struct sigaction action;
+  action.sa_sigaction = &SEGVHandler::test_handler;
+  sigemptyset(&action.sa_mask);
+  action.sa_flags = SA_SIGINFO | SA_NODEFER;
+  action.sa_restorer = NULL;
+  if (sys_sigaction(SIGSEGV, &action, NULL))
+    return;
+  stackPtr.Assign(MemoryRange::mmap(NULL, PageSize(), PROT_NONE,
+                                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+  if (stackPtr.get() == MAP_FAILED)
+    return;
+
+  *((volatile int*)stackPtr.get()) = 123;
+  stackPtr.Assign(MAP_FAILED, 0);
+  if (signalHandlingBroken) {
+    /* Restore the original segfault signal handler. */
+    sys_sigaction(SIGSEGV, &this->action, NULL);
+    return;
+  }
+
   /* Setup an alternative stack if the already existing one is not big
    * enough, or if there is none. */
-  if (sigaltstack(NULL, &oldStack) == -1 || !oldStack.ss_sp ||
-      oldStack.ss_size < stackSize) {
-    stackPtr.Assign(MemoryRange::mmap(NULL, stackSize, PROT_READ | PROT_WRITE,
-                                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
-    stack_t stack;
-    stack.ss_sp = stackPtr;
-    stack.ss_size = stackSize;
-    stack.ss_flags = 0;
-    sigaltstack(&stack, NULL);
+  if (sigaltstack(NULL, &oldStack) == 0) {
+    if (oldStack.ss_flags == SS_ONSTACK)
+      oldStack.ss_flags = 0;
+    if (!oldStack.ss_sp || oldStack.ss_size < stackSize) {
+      stackPtr.Assign(MemoryRange::mmap(NULL, stackSize, PROT_READ | PROT_WRITE,
+                                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+      if (stackPtr.get() == MAP_FAILED)
+        return;
+      stack_t stack;
+      stack.ss_sp = stackPtr;
+      stack.ss_size = stackSize;
+      stack.ss_flags = 0;
+      if (sigaltstack(&stack, NULL) != 0)
+        return;
+    }
   }
   /* Register our own handler, and store the already registered one in
    * SEGVHandler's struct sigaction member */
-  struct sigaction action;
   action.sa_sigaction = &SEGVHandler::handler;
-  sigemptyset(&action.sa_mask);
   action.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
-  action.sa_restorer = NULL;
-  registeredHandler = !sys_sigaction(SIGSEGV, &action, &this->action);
+  registeredHandler = !sys_sigaction(SIGSEGV, &action, NULL);
 }
 
 SEGVHandler::~SEGVHandler()
 {
   /* Restore alternative stack for signals */
-  sigaltstack(&oldStack, NULL);
+  if (oldStack.ss_flags != SS_ONSTACK)
+    sigaltstack(&oldStack, NULL);
   /* Restore original signal handler */
-  sys_sigaction(SIGSEGV, &this->action, NULL);
+  if (registeredHandler)
+    sys_sigaction(SIGSEGV, &this->action, NULL);
+}
+
+/* Test handler for a deliberately triggered SIGSEGV that determines whether
+ * useful information is provided to signal handlers, particularly whether
+ * si_addr is filled in properly. */
+void SEGVHandler::test_handler(int signum, siginfo_t *info, void *context)
+{
+  SEGVHandler &that = ElfLoader::Singleton;
+  if (signum != SIGSEGV || info == NULL || info->si_addr != that.stackPtr.get())
+    that.signalHandlingBroken = true;
+  mprotect(that.stackPtr, that.stackPtr.GetLength(), PROT_READ | PROT_WRITE);
 }
 
 /* TODO: "properly" handle signal masks and flags */
 void SEGVHandler::handler(int signum, siginfo_t *info, void *context)
 {
   //ASSERT(signum == SIGSEGV);
   DEBUG_LOG("Caught segmentation fault @%p", info->si_addr);
 
--- a/mozglue/linker/ElfLoader.h
+++ b/mozglue/linker/ElfLoader.h
@@ -49,16 +49,19 @@ MFBT_API size_t
 __dl_get_mappable_length(void *handle);
 
 MFBT_API void *
 __dl_mmap(void *handle, void *addr, size_t length, off_t offset);
 
 MFBT_API void
 __dl_munmap(void *handle, void *addr, size_t length);
 
+MFBT_API bool
+IsSignalHandlingBroken();
+
 }
 
 /**
  * Specialize RefCounted template for LibHandle. We may get references to
  * LibHandles during the execution of their destructor, so we need
  * RefCounted<LibHandle>::Release to support some reentrancy. See further
  * below.
  */
@@ -290,16 +293,20 @@ private:
  */
 class SEGVHandler
 {
 public:
   bool hasRegisteredHandler() {
     return registeredHandler;
   }
 
+  bool isSignalHandlingBroken() {
+    return signalHandlingBroken;
+  }
+
 protected:
   SEGVHandler();
   ~SEGVHandler();
 
 private:
   static int __wrap_sigaction(int signum, const struct sigaction *act,
                               struct sigaction *oldact);
 
@@ -309,16 +316,21 @@ private:
   struct sigaction action;
   
   /**
    * ElfLoader SIGSEGV handler.
    */
   static void handler(int signum, siginfo_t *info, void *context);
 
   /**
+   * Temporary test handler.
+   */
+  static void test_handler(int signum, siginfo_t *info, void *context);
+
+  /**
    * Size of the alternative stack. The printf family requires more than 8KB
    * of stack, and our signal handler may print a few things.
    */
   static const size_t stackSize = 12 * 1024;
 
   /**
    * Alternative stack information used before initialization.
    */
@@ -326,16 +338,17 @@ private:
 
   /**
    * Pointer to an alternative stack for signals. Only set if oldStack is
    * not set or not big enough.
    */
   MappedPtr stackPtr;
 
   bool registeredHandler;
+  bool signalHandlingBroken;
 };
 
 /**
  * Elf Loader class in charge of loading and bookkeeping libraries.
  */
 class ElfLoader: public SEGVHandler
 {
 public: