Commit 9ef6faf4 authored by Kostya Kortchinsky's avatar Kostya Kortchinsky
Browse files

[scudo][standalone] Fork support

Summary:
fork() wasn't well (or at all) supported in Scudo. This materialized
in deadlocks in children.

In order to properly support fork, we will lock the allocator pre-fork
and unlock it post-fork in parent and child. This is done via a
`pthread_atfork` call installing the necessary handlers.

A couple of things suck here: this function allocates - so this has to
be done post initialization as our init path is not reentrance, and it
doesn't allow for an extra pointer - so we can't pass the allocator we
are currently working with.

In order to work around this, I added a post-init template parameter
that gets executed once the allocator is initialized for the current
thread. Its job for the C wrappers is to install the atfork handlers.

I reorganized a bit the impacted area and added some tests, courtesy
of cferris@ that were deadlocking prior to this fix.

Subscribers: jfb, #sanitizers, llvm-commits

Tags: #sanitizers, #llvm

Differential Revision: https://reviews.llvm.org/D72470
parent e7b2d9f4
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
add_compiler_rt_component(scudo_standalone)
if (COMPILER_RT_HAS_GWP_ASAN)
# FIXME: GWP-ASan is temporarily disabled, re-enable once issues are fixed.
if (FALSE AND COMPILER_RT_HAS_GWP_ASAN)
  add_dependencies(scudo_standalone gwp_asan)
endif()

@@ -106,7 +107,7 @@ set(SCUDO_SOURCES_CXX_WRAPPERS

set(SCUDO_OBJECT_LIBS)

if (COMPILER_RT_HAS_GWP_ASAN)
if (FALSE AND COMPILER_RT_HAS_GWP_ASAN)
  list(APPEND SCUDO_OBJECT_LIBS RTGwpAsan)
  list(APPEND SCUDO_CFLAGS -DGWP_ASAN_HOOKS)
endif()
+6 −0
Original line number Diff line number Diff line
@@ -34,6 +34,9 @@ public:
    return Map[Index];
  }

  void disable() {}
  void enable() {}

private:
  u8 *Map;
};
@@ -82,6 +85,9 @@ public:
    return Level2Map[Index % Level2Size];
  }

  void disable() { Mutex.lock(); }
  void enable() { Mutex.unlock(); }

private:
  u8 *get(uptr Index) const {
    DCHECK_LT(Index, Level1Size);
+16 −2
Original line number Diff line number Diff line
@@ -31,15 +31,23 @@
static gwp_asan::GuardedPoolAllocator GuardedAlloc;
#endif // GWP_ASAN_HOOKS

extern "C" inline void EmptyCallback() {}

namespace scudo {

template <class Params> class Allocator {
template <class Params, void (*PostInitCallback)(void) = EmptyCallback>
class Allocator {
public:
  using PrimaryT = typename Params::Primary;
  using CacheT = typename PrimaryT::CacheT;
  typedef Allocator<Params> ThisT;
  typedef Allocator<Params, PostInitCallback> ThisT;
  typedef typename Params::template TSDRegistryT<ThisT> TSDRegistryT;

  void callPostInitCallback() {
    static pthread_once_t OnceControl = PTHREAD_ONCE_INIT;
    pthread_once(&OnceControl, PostInitCallback);
  }

  struct QuarantineCallback {
    explicit QuarantineCallback(ThisT &Instance, CacheT &LocalCache)
        : Allocator(Instance), Cache(LocalCache) {}
@@ -420,12 +428,18 @@ public:
  void disable() {
    initThreadMaybe();
    TSDRegistry.disable();
    Stats.disable();
    Quarantine.disable();
    Primary.disable();
    Secondary.disable();
  }

  void enable() {
    initThreadMaybe();
    Secondary.enable();
    Primary.enable();
    Quarantine.enable();
    Stats.enable();
    TSDRegistry.enable();
  }

+17 −4
Original line number Diff line number Diff line
@@ -123,13 +123,26 @@ public:
  }

  void disable() {
    for (uptr I = 0; I < NumClasses; I++)
      getSizeClassInfo(I)->Mutex.lock();
    // The BatchClassId must be locked last since other classes can use it.
    for (sptr I = static_cast<sptr>(NumClasses) - 1; I >= 0; I--) {
      if (static_cast<uptr>(I) == SizeClassMap::BatchClassId)
        continue;
      getSizeClassInfo(static_cast<uptr>(I))->Mutex.lock();
    }
    getSizeClassInfo(SizeClassMap::BatchClassId)->Mutex.lock();
    RegionsStashMutex.lock();
    PossibleRegions.disable();
  }

  void enable() {
    for (sptr I = static_cast<sptr>(NumClasses) - 1; I >= 0; I--)
      getSizeClassInfo(static_cast<uptr>(I))->Mutex.unlock();
    PossibleRegions.enable();
    RegionsStashMutex.unlock();
    getSizeClassInfo(SizeClassMap::BatchClassId)->Mutex.unlock();
    for (uptr I = 0; I < NumClasses; I++) {
      if (I == SizeClassMap::BatchClassId)
        continue;
      getSizeClassInfo(I)->Mutex.unlock();
    }
  }

  template <typename F> void iterateOverBlocks(F Callback) {
+13 −4
Original line number Diff line number Diff line
@@ -125,13 +125,22 @@ public:
  }

  void disable() {
    for (uptr I = 0; I < NumClasses; I++)
      getRegionInfo(I)->Mutex.lock();
    // The BatchClassId must be locked last since other classes can use it.
    for (sptr I = static_cast<sptr>(NumClasses) - 1; I >= 0; I--) {
      if (static_cast<uptr>(I) == SizeClassMap::BatchClassId)
        continue;
      getRegionInfo(static_cast<uptr>(I))->Mutex.lock();
    }
    getRegionInfo(SizeClassMap::BatchClassId)->Mutex.lock();
  }

  void enable() {
    for (sptr I = static_cast<sptr>(NumClasses) - 1; I >= 0; I--)
      getRegionInfo(static_cast<uptr>(I))->Mutex.unlock();
    getRegionInfo(SizeClassMap::BatchClassId)->Mutex.unlock();
    for (uptr I = 0; I < NumClasses; I++) {
      if (I == SizeClassMap::BatchClassId)
        continue;
      getRegionInfo(I)->Mutex.unlock();
    }
  }

  template <typename F> void iterateOverBlocks(F Callback) const {
Loading