diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -22,7 +22,7 @@ #include "tsd.h" #ifdef GWP_ASAN_HOOKS -# include "gwp_asan/guarded_pool_allocator.h" +#include "gwp_asan/guarded_pool_allocator.h" // GWP-ASan is declared here in order to avoid indirect call overhead. It's also // instantiated outside of the Allocator class, as the allocator is only // zero-initialised. GWP-ASan requires constant initialisation, and the Scudo @@ -414,19 +414,16 @@ return NewPtr; } - // TODO(kostyak): while this locks the Primary & Secondary, it still allows - // pointers to be fetched from the TSD. We ultimately want to - // lock the registry as well. For now, it's good enough. void disable() { initThreadMaybe(); - Primary.disable(); + TSDRegistry.disable(); Secondary.disable(); } void enable() { initThreadMaybe(); Secondary.enable(); - Primary.enable(); + TSDRegistry.enable(); } // The function returns the amount of bytes required to store the statistics, diff --git a/compiler-rt/lib/scudo/standalone/flags.cpp b/compiler-rt/lib/scudo/standalone/flags.cpp --- a/compiler-rt/lib/scudo/standalone/flags.cpp +++ b/compiler-rt/lib/scudo/standalone/flags.cpp @@ -40,7 +40,7 @@ #ifdef GWP_ASAN_HOOKS #define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description) \ - Parser->registerFlag("GWP_ASAN_"#Name, Description, FlagType::FT_##Type, \ + Parser->registerFlag("GWP_ASAN_" #Name, Description, FlagType::FT_##Type, \ reinterpret_cast(&F->GWP_ASAN_##Name)); #include "gwp_asan/options.inc" #undef GWP_ASAN_OPTION diff --git a/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp b/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp --- a/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp @@ -284,6 +284,21 @@ free(P); } +// We expect heap operations within a disable/enable scope to deadlock. +TEST(ScudoWrappersCTest, MallocDisableDeadlock) { + EXPECT_DEATH( + { + void *P = malloc(Size); + EXPECT_NE(P, nullptr); + free(P); + malloc_disable(); + alarm(1); + P = malloc(Size); + malloc_enable(); + }, + ""); +} + #if !SCUDO_FUCHSIA TEST(ScudoWrappersCTest, MallocInfo) { char Buffer[64]; diff --git a/compiler-rt/lib/scudo/standalone/tsd_exclusive.h b/compiler-rt/lib/scudo/standalone/tsd_exclusive.h --- a/compiler-rt/lib/scudo/standalone/tsd_exclusive.h +++ b/compiler-rt/lib/scudo/standalone/tsd_exclusive.h @@ -48,7 +48,7 @@ } ALWAYS_INLINE TSD *getTSDAndLock(bool *UnlockRequired) { - if (LIKELY(State == ThreadState::Initialized)) { + if (LIKELY(State == ThreadState::Initialized && !Disabled)) { *UnlockRequired = false; return &ThreadTSD; } @@ -58,6 +58,18 @@ return FallbackTSD; } + // To disable the exclusive TSD registry, we effectively lock the fallback TSD + // and force all threads to attempt to use it instead of their local one. + void disable() { + FallbackTSD->lock(); + Disabled = true; + } + + void enable() { + Disabled = false; + FallbackTSD->unlock(); + } + private: void initOnceMaybe(Allocator *Instance) { ScopedLock L(Mutex); @@ -81,6 +93,7 @@ pthread_key_t PThreadKey; bool Initialized; + bool Disabled; TSD *FallbackTSD; HybridMutex Mutex; static THREADLOCAL ThreadState State; diff --git a/compiler-rt/lib/scudo/standalone/tsd_shared.h b/compiler-rt/lib/scudo/standalone/tsd_shared.h --- a/compiler-rt/lib/scudo/standalone/tsd_shared.h +++ b/compiler-rt/lib/scudo/standalone/tsd_shared.h @@ -72,6 +72,16 @@ return getTSDAndLockSlow(TSD); } + void disable() { + for (u32 I = 0; I < NumberOfTSDs; I++) + TSDs[I].lock(); + } + + void enable() { + for (u32 I = 0; I < NumberOfTSDs; I++) + TSDs[I].unlock(); + } + private: ALWAYS_INLINE void setCurrentTSD(TSD *CurrentTSD) { #if _BIONIC