summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReid Kleckner <rnk@google.com>2018-07-30 23:32:33 +0000
committerReid Kleckner <rnk@google.com>2018-07-30 23:32:33 +0000
commit54b2ff7aa5ca9de0f46deabccb247d819e7da55a (patch)
tree4d7b9d1515777fa17dd3c2fc3f86562139deba67
parent3eadc5926b0dc8a0a9479ca7e1cebbb414ff82ea (diff)
downloadcompiler-rt-54b2ff7aa5ca9de0f46deabccb247d819e7da55a.tar.gz
[asan/win] Use SRW locks to fix a race in BlockingMutex
Summary: Before my change, BlockingMutex used Windows critial sections. Critical sections can only be initialized by calling InitializeCriticalSection, dynamically. The primary sanitizer allocator expects to be able to reinterpret zero initialized memory as a BlockingMutex and immediately lock it. RegionInfo contains a mutex, and it placement new is never called for it. These objects are accessed via: RegionInfo *GetRegionInfo(uptr class_id) const { DCHECK_LT(class_id, kNumClasses); RegionInfo *regions = reinterpret_cast<RegionInfo *>(SpaceEnd()); return &regions[class_id]; } The memory comes from the OS without any other initialization. For various reasons described in the comments, BlockingMutex::Lock would check if the object appeared to be zero-initialized, and it would lazily call the LinkerInitialized constructor to initialize the critical section. This pattern is obviously racy, and the code had a bunch of FIXMEs about it. The best fix here is to use slim reader writer locks, which can start out zero-initialized. They are available starting in Windows Vista. I think it's safe to go ahead and use them today. Reviewers: kcc, vitalybuka Subscribers: kubamracek, llvm-commits Differential Revision: https://reviews.llvm.org/D49893 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@338331 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/sanitizer_common/sanitizer_mutex.h7
-rw-r--r--lib/sanitizer_common/sanitizer_win.cc35
2 files changed, 8 insertions, 34 deletions
diff --git a/lib/sanitizer_common/sanitizer_mutex.h b/lib/sanitizer_common/sanitizer_mutex.h
index 4bb878ee1..9041f3ac8 100644
--- a/lib/sanitizer_common/sanitizer_mutex.h
+++ b/lib/sanitizer_common/sanitizer_mutex.h
@@ -73,13 +73,8 @@ class SpinMutex : public StaticSpinMutex {
class BlockingMutex {
public:
-#if SANITIZER_WINDOWS
- // Windows does not currently support LinkerInitialized
- explicit BlockingMutex(LinkerInitialized);
-#else
explicit constexpr BlockingMutex(LinkerInitialized)
- : opaque_storage_ {0, }, owner_(0) {}
-#endif
+ : opaque_storage_ {0, }, owner_{0} {}
BlockingMutex();
void Lock();
void Unlock();
diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc
index b8060b2e6..38e567d9a 100644
--- a/lib/sanitizer_common/sanitizer_win.cc
+++ b/lib/sanitizer_common/sanitizer_win.cc
@@ -767,43 +767,22 @@ void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; }
void internal_join_thread(void *th) { }
// ---------------------- BlockingMutex ---------------- {{{1
-const uptr LOCK_UNINITIALIZED = 0;
-const uptr LOCK_READY = (uptr)-1;
-
-BlockingMutex::BlockingMutex(LinkerInitialized li) {
- // FIXME: see comments in BlockingMutex::Lock() for the details.
- CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);
-
- CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
- InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
- owner_ = LOCK_READY;
-}
BlockingMutex::BlockingMutex() {
- CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
- InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
- owner_ = LOCK_READY;
+ CHECK(sizeof(SRWLOCK) <= sizeof(opaque_storage_));
+ internal_memset(this, 0, sizeof(*this));
}
void BlockingMutex::Lock() {
- if (owner_ == LOCK_UNINITIALIZED) {
- // FIXME: hm, global BlockingMutex objects are not initialized?!?
- // This might be a side effect of the clang+cl+link Frankenbuild...
- new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1));
-
- // FIXME: If it turns out the linker doesn't invoke our
- // constructors, we should probably manually Lock/Unlock all the global
- // locks while we're starting in one thread to avoid double-init races.
- }
- EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
- CHECK_EQ(owner_, LOCK_READY);
+ AcquireSRWLockExclusive((PSRWLOCK)opaque_storage_);
+ CHECK_EQ(owner_, 0);
owner_ = GetThreadSelf();
}
void BlockingMutex::Unlock() {
- CHECK_EQ(owner_, GetThreadSelf());
- owner_ = LOCK_READY;
- LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+ CheckLocked();
+ owner_ = 0;
+ ReleaseSRWLockExclusive((PSRWLOCK)opaque_storage_);
}
void BlockingMutex::CheckLocked() {