Commit 901ea6c3 authored by Gabriele Svelto's avatar Gabriele Svelto
Browse files

Bug 1678152 - Catch all stack overflows on Linux r=jld

parent 49d568d9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ if CONFIG["MOZ_CRASHREPORTER"]:
            "google-breakpad/src/common",
            "google-breakpad/src/common/linux",
            "google-breakpad/src/processor",
            "pthread_create_interposer",
        ]

        if CONFIG["MOZ_OXIDIZED_BREAKPAD"]:
+12 −0
Original line number Diff line number Diff line
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
NoVisibilityFlags()

UNIFIED_SOURCES += [
    "pthread_create_interposer.cpp",
]

FINAL_LIBRARY = "mozglue"
+119 −0
Original line number Diff line number Diff line
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <algorithm>

#include <dlfcn.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/mman.h>

#include "mozilla/Assertions.h"
#include "mozilla/DebugOnly.h"

using mozilla::DebugOnly;

struct PthreadCreateParams {
  void* (*start_routine)(void*);
  void* arg;
};

const size_t kSigStackSize = std::max(size_t(16384), size_t(SIGSTKSZ));

// Install the alternate signal stack, returns a pointer to the memory area we
// mapped to store the stack only if it was installed successfully, otherwise
// returns NULL.
static void* install_sig_alt_stack() {
  void* alt_stack_mem = mmap(nullptr, kSigStackSize, PROT_READ | PROT_WRITE,
                             MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (alt_stack_mem) {
    stack_t alt_stack = {
        .ss_sp = alt_stack_mem,
        .ss_flags = 0,
        .ss_size = kSigStackSize,
    };

    int rv = sigaltstack(&alt_stack, nullptr);
    if (rv == 0) {
      return alt_stack_mem;
    }

    rv = munmap(alt_stack_mem, kSigStackSize);
    MOZ_ASSERT(rv == 0);
  }

  return nullptr;
}

// Uninstall the alternate signal handler and unmaps it. Does nothing if
// alt_stack_mem is NULL.
static void uninstall_sig_alt_stack(void* alt_stack_mem) {
  if (alt_stack_mem) {
    stack_t disable_alt_stack = {};
    disable_alt_stack.ss_flags = SS_DISABLE;
    DebugOnly<int> rv = sigaltstack(&disable_alt_stack, nullptr);
    MOZ_ASSERT(rv == 0);
    rv = munmap(alt_stack_mem, kSigStackSize);
    MOZ_ASSERT(rv == 0);
  }
}

// This replaces the routine passed to pthread_create() when a thread is
// started, it handles the alternate signal stack and calls the thread's
// actual routine.
void* set_alt_signal_stack_and_start(PthreadCreateParams* params) {
  void* (*start_routine)(void*) = params->start_routine;
  void* arg = params->arg;
  free(params);

  void* thread_rv = nullptr;
  void* alt_stack_mem = install_sig_alt_stack();
  pthread_cleanup_push(uninstall_sig_alt_stack, alt_stack_mem);
  thread_rv = start_routine(arg);
  pthread_cleanup_pop(1);

  return thread_rv;
}

using pthread_create_func_t = int (*)(pthread_t*, const pthread_attr_t*,
                                      void* (*)(void*), void*);

extern "C" {
// This interposer replaces libpthread's pthread_create() so that we can
// inject an alternate signal stack in every new thread.
__attribute__((visibility("default"))) int pthread_create(
    pthread_t* thread, const pthread_attr_t* attr,
    void* (*start_routine)(void*), void* arg) {
  // static const pthread_create_func_t real_pthread_create =
  static const pthread_create_func_t real_pthread_create =
      (pthread_create_func_t)dlsym(RTLD_NEXT, "pthread_create");

  if (real_pthread_create == nullptr) {
    MOZ_CRASH(
        "pthread_create() interposition failed but the interposer function is "
        "still being called, this won't work!");
  }

  if (real_pthread_create == pthread_create) {
    MOZ_CRASH(
        "We could not obtain the real pthread_create(). Calling the symbol we "
        "got would make us enter an infinte loop so stop here instead.");
  }

  PthreadCreateParams* params =
      (PthreadCreateParams*)malloc(sizeof(PthreadCreateParams));
  params->start_routine = start_routine;
  params->arg = arg;

  int result = real_pthread_create(
      thread, attr, (void* (*)(void*))set_alt_signal_stack_and_start, params);

  if (result != 0) {
    free(params);
  }

  return result;
}
}
+1 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ var CrashTestUtils = {
  CRASH_PHC_BOUNDS_VIOLATION: 23,
  CRASH_HEAP_CORRUPTION: 24,
  CRASH_EXC_GUARD: 25,
  CRASH_STACK_OVERFLOW: 26,

  // Constants for dumpHasStream()
  // From google_breakpad/common/minidump_format.h
+29 −0
Original line number Diff line number Diff line
@@ -97,6 +97,7 @@ const int16_t CRASH_PHC_DOUBLE_FREE = 22;
const int16_t CRASH_PHC_BOUNDS_VIOLATION = 23;
const int16_t CRASH_HEAP_CORRUPTION = 24;
const int16_t CRASH_EXC_GUARD = 25;
const int16_t CRASH_STACK_OVERFLOW = 26;

#if XP_WIN && HAVE_64BIT_BUILD && defined(_M_X64) && !defined(__MINGW32__)

@@ -153,6 +154,23 @@ uint8_t* GetPHCAllocation(size_t aSize) {
}
#endif

#ifndef XP_WIN
static void* overflow_stack(void* aUnused) {
  // We use a dummy variable and a bit of magic to pretend we care about what's
  // on the stack so the compiler doesn't optimize the loop and allocations away
  void* rv = nullptr;

  for (size_t i = 0; i < 1024 * 1024 * 1024; i++) {
    void* ptr = alloca(sizeof(void*));
    if (ptr != nullptr) {
      rv = ptr;
    }
  }

  return rv;
}
#endif  // XP_WIN

extern "C" NS_EXPORT void Crash(int16_t how) {
  switch (how) {
    case CRASH_INVALID_POINTER_DEREF: {
@@ -265,6 +283,17 @@ extern "C" NS_EXPORT void Crash(int16_t how) {
      }
    }
#endif  // XP_MACOSX
#ifndef XP_WIN
    case CRASH_STACK_OVERFLOW: {
      pthread_t thread_id;
      int rv = pthread_create(&thread_id, nullptr, overflow_stack, nullptr);
      if (!rv) {
        pthread_join(thread_id, nullptr);
      }

      break;  // This should be unreachable
    }
#endif  // XP_WIN
    default:
      break;
  }
Loading