Bug 1625884 - add `clang-10` to `linux64` `clang-tools`. r=nalexander,dmajor

Patches that are applied on top on `clang-10` repo are based on the original
patches from our trunk and have been rebased on top of `clang-10`.
Please see as an example: `find_symbolizer_linux.patch` copied to `find_symbolizer_linux_clang_10.patch`.

Differential Revision: https://phabricator.services.mozilla.com/D70063

--HG--
rename : build/build-clang/clang-linux64.json => build/build-clang/clang-10-linux64.json
rename : build/build-clang/find_symbolizer_linux.patch => build/build-clang/find_symbolizer_linux_clang_10.patch
rename : build/build-clang/llvmorg-11-init-4265-g2dcbdba8540.patch => build/build-clang/llvmorg-11-init-4265-g2dcbdba8540_clang_10.patch
rename : build/build-clang/rG7e18aeba5062.patch => build/build-clang/rG7e18aeba5062_clang_10.patch
rename : build/build-clang/rename_gcov_flush.patch => build/build-clang/rename_gcov_flush_clang_10.patch
rename : build/build-clang/tsan-hang-be41a98ac222.patch => build/build-clang/tsan-hang-be41a98ac222_clang_10.patch
rename : build/build-clang/unpoison-thread-stacks.patch => build/build-clang/unpoison-thread-stacks_clang_10.patch
extra : moz-landing-system : lando
This commit is contained in:
Andi-Bogdan Postelnicu 2020-04-09 05:27:50 +00:00
Родитель 20a905ded8
Коммит 06e7a3c818
9 изменённых файлов: 674 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,27 @@
{
"stages": "4",
"pgo" : true,
"build_libcxx": true,
"build_wasm": true,
"build_type": "Release",
"assertions": false,
"python_path": "/usr/bin/python2.7",
"gcc_dir": "{MOZ_FETCHES_DIR}/gcc",
"cc": "{MOZ_FETCHES_DIR}/gcc/bin/gcc",
"cxx": "{MOZ_FETCHES_DIR}/gcc/bin/g++",
"as": "{MOZ_FETCHES_DIR}/gcc/bin/gcc",
"wasi-sysroot": "{MOZ_FETCHES_DIR}/wasi-sysroot",
"patches": [
"static-llvm-symbolizer.patch",
"find_symbolizer_linux_clang_10.patch",
"rename_gcov_flush_clang_10.patch",
"critical_section_on_gcov_flush-rG02ce9d8ef5a8.patch",
"rG7e18aeba5062_clang_10.patch",
"llvmorg-11-init-4265-g2dcbdba8540_clang_10.patch",
"android-mangling-error.patch",
"unpoison-thread-stacks_clang_10.patch",
"downgrade-mangling-error.patch",
"tsan-hang-be41a98ac222_clang_10.patch",
"loosen-msvc-detection.patch"
]
}

Просмотреть файл

@ -0,0 +1,58 @@
We currently need this patch because ASan only searches PATH to find the
llvm-symbolizer binary to symbolize ASan traces. On testing machines, this
can be installed in PATH easily. However, for e.g. the ASan Nightly Project,
where we ship an ASan build, including llvm-symbolizer, to the user, we
cannot expect llvm-symbolizer to be on PATH. Instead, we should try to look
it up next to the binary. This patch implements the functionality for Linux
only until there is similar functionality provided upstream.
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_file.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_file.cpp
index 79930d79425..cfb4f90c0d5 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_file.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_file.cpp
@@ -20,6 +20,10 @@
#include "sanitizer_common.h"
#include "sanitizer_file.h"
+#if SANITIZER_LINUX
+#include "sanitizer_posix.h"
+#endif
+
namespace __sanitizer {
void CatastrophicErrorWrite(const char *buffer, uptr length) {
@@ -194,6 +198,34 @@ char *FindPathToBinary(const char *name) {
if (*end == '\0') break;
beg = end + 1;
}
+
+#if SANITIZER_LINUX
+ // If we cannot find the requested binary in PATH, we should try to locate
+ // it next to the binary, in case it is shipped with the build itself
+ // (e.g. llvm-symbolizer shipped with sanitizer build to symbolize on client.
+ if (internal_readlink("/proc/self/exe", buffer.data(), kMaxPathLength) < 0)
+ return nullptr;
+
+ uptr buf_len = internal_strlen(buffer.data());
+
+ /* Avoid using dirname() here */
+ while (buf_len > 0) {
+ if (buffer[buf_len - 1] == '/')
+ break;
+ buf_len--;
+ }
+
+ if (!buf_len)
+ return nullptr;
+
+ if (buf_len + name_len + 1 <= kMaxPathLength) {
+ internal_memcpy(&buffer[buf_len], name, name_len);
+ buffer[buf_len + name_len] = '\0';
+ if (FileExists(buffer.data()))
+ return internal_strdup(buffer.data());
+ }
+#endif
+
return nullptr;
}

Просмотреть файл

@ -0,0 +1,106 @@
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
index 8aea1e4ec05..a623f4fe589 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -1016,7 +1016,7 @@ TSAN_INTERCEPTOR(int, pthread_create,
TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret);
- int tid = ThreadTid(thr, pc, (uptr)th);
+ int tid = ThreadConsumeTid(thr, pc, (uptr)th);
ThreadIgnoreBegin(thr, pc);
int res = BLOCK_REAL(pthread_join)(th, ret);
ThreadIgnoreEnd(thr, pc);
@@ -1029,8 +1029,8 @@ TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
DEFINE_REAL_PTHREAD_FUNCTIONS
TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
- SCOPED_TSAN_INTERCEPTOR(pthread_detach, th);
- int tid = ThreadTid(thr, pc, (uptr)th);
+ SCOPED_INTERCEPTOR_RAW(pthread_detach, th);
+ int tid = ThreadConsumeTid(thr, pc, (uptr)th);
int res = REAL(pthread_detach)(th);
if (res == 0) {
ThreadDetach(thr, pc, tid);
@@ -1050,8 +1050,8 @@ TSAN_INTERCEPTOR(void, pthread_exit, void *retval) {
#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, pthread_tryjoin_np, void *th, void **ret) {
- SCOPED_TSAN_INTERCEPTOR(pthread_tryjoin_np, th, ret);
- int tid = ThreadTid(thr, pc, (uptr)th);
+ SCOPED_INTERCEPTOR_RAW(pthread_tryjoin_np, th, ret);
+ int tid = ThreadConsumeTid(thr, pc, (uptr)th);
ThreadIgnoreBegin(thr, pc);
int res = REAL(pthread_tryjoin_np)(th, ret);
ThreadIgnoreEnd(thr, pc);
@@ -1064,8 +1064,8 @@ TSAN_INTERCEPTOR(int, pthread_tryjoin_np, void *th, void **ret) {
TSAN_INTERCEPTOR(int, pthread_timedjoin_np, void *th, void **ret,
const struct timespec *abstime) {
- SCOPED_TSAN_INTERCEPTOR(pthread_timedjoin_np, th, ret, abstime);
- int tid = ThreadTid(thr, pc, (uptr)th);
+ SCOPED_INTERCEPTOR_RAW(pthread_timedjoin_np, th, ret, abstime);
+ int tid = ThreadConsumeTid(thr, pc, (uptr)th);
ThreadIgnoreBegin(thr, pc);
int res = BLOCK_REAL(pthread_timedjoin_np)(th, ret, abstime);
ThreadIgnoreEnd(thr, pc);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
index c38fc43a9f8..20f7a99157a 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
@@ -775,7 +775,7 @@ int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached);
void ThreadStart(ThreadState *thr, int tid, tid_t os_id,
ThreadType thread_type);
void ThreadFinish(ThreadState *thr);
-int ThreadTid(ThreadState *thr, uptr pc, uptr uid);
+int ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid);
void ThreadJoin(ThreadState *thr, uptr pc, int tid);
void ThreadDetach(ThreadState *thr, uptr pc, int tid);
void ThreadFinalize(ThreadState *thr);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
index 0ac1ee99c47..f7068f0d331 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
@@ -285,19 +285,34 @@ void ThreadFinish(ThreadState *thr) {
ctx->thread_registry->FinishThread(thr->tid);
}
-static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) {
- uptr uid = (uptr)arg;
- if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) {
+struct ConsumeThreadContext {
+ uptr uid;
+ ThreadContextBase* tctx;
+};
+
+static bool ConsumeThreadByUid(ThreadContextBase *tctx, void *arg) {
+ ConsumeThreadContext *findCtx = (ConsumeThreadContext*)arg;
+ if (tctx->user_id == findCtx->uid && tctx->status != ThreadStatusInvalid) {
+ if (findCtx->tctx) {
+ // Ensure that user_id is unique. If it's not the case we are screwed.
+ // Something went wrong before, but now there is no way to recover.
+ // Returning a wrong thread is not an option, it may lead to very hard
+ // to debug false positives (e.g. if we join a wrong thread).
+ Report("ThreadSanitizer: dup thread with used id 0x%zx\n", findCtx->uid);
+ Die();
+ }
+ findCtx->tctx = tctx;
tctx->user_id = 0;
- return true;
}
return false;
}
-int ThreadTid(ThreadState *thr, uptr pc, uptr uid) {
- int res = ctx->thread_registry->FindThread(FindThreadByUid, (void*)uid);
- DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res);
- return res;
+int ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid) {
+ ConsumeThreadContext findCtx = {uid, nullptr};
+ ctx->thread_registry->FindThread(ConsumeThreadByUid, &findCtx);
+ int tid = findCtx.tctx ? findCtx.tctx->tid : ThreadRegistry::kUnknownTid;
+ DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, tid);
+ return tid;
}
void ThreadJoin(ThreadState *thr, uptr pc, int tid) {

Просмотреть файл

@ -0,0 +1,249 @@
From 779a169144581438d9e24b8b46a86704f6335e35 Mon Sep 17 00:00:00 2001
From: Nikita Popov <nikita.ppv@gmail.com>
Date: Sat, 16 Nov 2019 16:22:18 +0100
Subject: [PATCH] [LVI] Restructure caching
Variant on D70103. The caching is switched to always use a BB to
cache entry map, which then contains per-value caches. A separate
set contains value handles with a deletion callback. This allows us
to properly invalidate overdefined values.
A possible alternative would be to always cache by value first and
have per-BB maps/sets in the each cache entry. In that case we could
use a ValueMap and would avoid the separate value handle set. I went
with the BB indexing at the top level to make it easier to integrate
D69914, but possibly that's not the right choice.
Differential Revision: https://reviews.llvm.org/D70376
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index bad2de9e5f5..33406a75d80 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -136,12 +136,10 @@ namespace {
/// A callback value handle updates the cache when values are erased.
class LazyValueInfoCache;
struct LVIValueHandle final : public CallbackVH {
- // Needs to access getValPtr(), which is protected.
- friend struct DenseMapInfo<LVIValueHandle>;
LazyValueInfoCache *Parent;
- LVIValueHandle(Value *V, LazyValueInfoCache *P)
+ LVIValueHandle(Value *V, LazyValueInfoCache *P = nullptr)
: CallbackVH(V), Parent(P) { }
void deleted() override;
@@ -155,89 +153,63 @@ namespace {
/// This is the cache kept by LazyValueInfo which
/// maintains information about queries across the clients' queries.
class LazyValueInfoCache {
- /// This is all of the cached block information for exactly one Value*.
- /// The entries are sorted by the BasicBlock* of the
- /// entries, allowing us to do a lookup with a binary search.
- /// Over-defined lattice values are recorded in OverDefinedCache to reduce
- /// memory overhead.
- struct ValueCacheEntryTy {
- ValueCacheEntryTy(Value *V, LazyValueInfoCache *P) : Handle(V, P) {}
- LVIValueHandle Handle;
- SmallDenseMap<PoisoningVH<BasicBlock>, ValueLatticeElement, 4> BlockVals;
+ /// This is all of the cached information for one basic block. It contains
+ /// the per-value lattice elements, as well as a separate set for
+ /// overdefined values to reduce memory usage.
+ struct BlockCacheEntryTy {
+ SmallDenseMap<AssertingVH<Value>, ValueLatticeElement, 4> LatticeElements;
+ SmallDenseSet<AssertingVH<Value>, 4> OverDefined;
};
- /// This tracks, on a per-block basis, the set of values that are
- /// over-defined at the end of that block.
- typedef DenseMap<PoisoningVH<BasicBlock>, SmallPtrSet<Value *, 4>>
- OverDefinedCacheTy;
- /// Keep track of all blocks that we have ever seen, so we
- /// don't spend time removing unused blocks from our caches.
- DenseSet<PoisoningVH<BasicBlock> > SeenBlocks;
-
- /// This is all of the cached information for all values,
- /// mapped from Value* to key information.
- DenseMap<Value *, std::unique_ptr<ValueCacheEntryTy>> ValueCache;
- OverDefinedCacheTy OverDefinedCache;
-
+ /// Cached information per basic block.
+ DenseMap<PoisoningVH<BasicBlock>, BlockCacheEntryTy> BlockCache;
+ /// Set of value handles used to erase values from the cache on deletion.
+ DenseSet<LVIValueHandle, DenseMapInfo<Value *>> ValueHandles;
public:
void insertResult(Value *Val, BasicBlock *BB,
const ValueLatticeElement &Result) {
- SeenBlocks.insert(BB);
-
+ auto &CacheEntry = BlockCache.try_emplace(BB).first->second;
// Insert over-defined values into their own cache to reduce memory
// overhead.
if (Result.isOverdefined())
- OverDefinedCache[BB].insert(Val);
- else {
- auto It = ValueCache.find_as(Val);
- if (It == ValueCache.end()) {
- ValueCache[Val] = std::make_unique<ValueCacheEntryTy>(Val, this);
- It = ValueCache.find_as(Val);
- assert(It != ValueCache.end() && "Val was just added to the map!");
- }
- It->second->BlockVals[BB] = Result;
- }
- }
-
- bool isOverdefined(Value *V, BasicBlock *BB) const {
- auto ODI = OverDefinedCache.find(BB);
-
- if (ODI == OverDefinedCache.end())
- return false;
+ CacheEntry.OverDefined.insert(Val);
+ else
+ CacheEntry.LatticeElements.insert({ Val, Result });
- return ODI->second.count(V);
+ auto HandleIt = ValueHandles.find_as(Val);
+ if (HandleIt == ValueHandles.end())
+ ValueHandles.insert({ Val, this });
}
bool hasCachedValueInfo(Value *V, BasicBlock *BB) const {
- if (isOverdefined(V, BB))
- return true;
-
- auto I = ValueCache.find_as(V);
- if (I == ValueCache.end())
+ auto It = BlockCache.find(BB);
+ if (It == BlockCache.end())
return false;
- return I->second->BlockVals.count(BB);
+ return It->second.OverDefined.count(V) ||
+ It->second.LatticeElements.count(V);
}
ValueLatticeElement getCachedValueInfo(Value *V, BasicBlock *BB) const {
- if (isOverdefined(V, BB))
+ auto It = BlockCache.find(BB);
+ if (It == BlockCache.end())
+ return ValueLatticeElement();
+
+ if (It->second.OverDefined.count(V))
return ValueLatticeElement::getOverdefined();
- auto I = ValueCache.find_as(V);
- if (I == ValueCache.end())
- return ValueLatticeElement();
- auto BBI = I->second->BlockVals.find(BB);
- if (BBI == I->second->BlockVals.end())
+ auto LatticeIt = It->second.LatticeElements.find(V);
+ if (LatticeIt == It->second.LatticeElements.end())
return ValueLatticeElement();
- return BBI->second;
+
+ return LatticeIt->second;
}
/// clear - Empty the cache.
void clear() {
- SeenBlocks.clear();
- ValueCache.clear();
- OverDefinedCache.clear();
+ BlockCache.clear();
+ ValueHandles.clear();
}
/// Inform the cache that a given value has been deleted.
@@ -251,23 +223,18 @@ namespace {
/// OldSucc might have (unless also overdefined in NewSucc). This just
/// flushes elements from the cache and does not add any.
void threadEdgeImpl(BasicBlock *OldSucc,BasicBlock *NewSucc);
-
- friend struct LVIValueHandle;
};
}
void LazyValueInfoCache::eraseValue(Value *V) {
- for (auto I = OverDefinedCache.begin(), E = OverDefinedCache.end(); I != E;) {
- // Copy and increment the iterator immediately so we can erase behind
- // ourselves.
- auto Iter = I++;
- SmallPtrSetImpl<Value *> &ValueSet = Iter->second;
- ValueSet.erase(V);
- if (ValueSet.empty())
- OverDefinedCache.erase(Iter);
+ for (auto &Pair : BlockCache) {
+ Pair.second.LatticeElements.erase(V);
+ Pair.second.OverDefined.erase(V);
}
- ValueCache.erase(V);
+ auto HandleIt = ValueHandles.find_as(V);
+ if (HandleIt != ValueHandles.end())
+ ValueHandles.erase(HandleIt);
}
void LVIValueHandle::deleted() {
@@ -277,18 +244,7 @@ void LVIValueHandle::deleted() {
}
void LazyValueInfoCache::eraseBlock(BasicBlock *BB) {
- // Shortcut if we have never seen this block.
- DenseSet<PoisoningVH<BasicBlock> >::iterator I = SeenBlocks.find(BB);
- if (I == SeenBlocks.end())
- return;
- SeenBlocks.erase(I);
-
- auto ODI = OverDefinedCache.find(BB);
- if (ODI != OverDefinedCache.end())
- OverDefinedCache.erase(ODI);
-
- for (auto &I : ValueCache)
- I.second->BlockVals.erase(BB);
+ BlockCache.erase(BB);
}
void LazyValueInfoCache::threadEdgeImpl(BasicBlock *OldSucc,
@@ -306,10 +262,11 @@ void LazyValueInfoCache::threadEdgeImpl(BasicBlock *OldSucc,
std::vector<BasicBlock*> worklist;
worklist.push_back(OldSucc);
- auto I = OverDefinedCache.find(OldSucc);
- if (I == OverDefinedCache.end())
+ auto I = BlockCache.find(OldSucc);
+ if (I == BlockCache.end() || I->second.OverDefined.empty())
return; // Nothing to process here.
- SmallVector<Value *, 4> ValsToClear(I->second.begin(), I->second.end());
+ SmallVector<Value *, 4> ValsToClear(I->second.OverDefined.begin(),
+ I->second.OverDefined.end());
// Use a worklist to perform a depth-first search of OldSucc's successors.
// NOTE: We do not need a visited list since any blocks we have already
@@ -323,10 +280,10 @@ void LazyValueInfoCache::threadEdgeImpl(BasicBlock *OldSucc,
if (ToUpdate == NewSucc) continue;
// If a value was marked overdefined in OldSucc, and is here too...
- auto OI = OverDefinedCache.find(ToUpdate);
- if (OI == OverDefinedCache.end())
+ auto OI = BlockCache.find(ToUpdate);
+ if (OI == BlockCache.end() || OI->second.OverDefined.empty())
continue;
- SmallPtrSetImpl<Value *> &ValueSet = OI->second;
+ auto &ValueSet = OI->second.OverDefined;
bool changed = false;
for (Value *V : ValsToClear) {
@@ -336,11 +293,6 @@ void LazyValueInfoCache::threadEdgeImpl(BasicBlock *OldSucc,
// If we removed anything, then we potentially need to update
// blocks successors too.
changed = true;
-
- if (ValueSet.empty()) {
- OverDefinedCache.erase(OI);
- break;
- }
}
if (!changed) continue;

Просмотреть файл

@ -0,0 +1,42 @@
diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp
index 220bc8f9835..4f7ce485777 100644
--- a/clang/lib/Driver/ToolChains/Darwin.cpp
+++ b/clang/lib/Driver/ToolChains/Darwin.cpp
@@ -1143,7 +1143,7 @@ void Darwin::addProfileRTLibs(const ArgList &Args,
// runtime's functionality.
if (hasExportSymbolDirective(Args)) {
if (ForGCOV) {
- addExportedSymbol(CmdArgs, "___gcov_flush");
+ addExportedSymbol(CmdArgs, "___custom_llvm_gcov_flush");
addExportedSymbol(CmdArgs, "_flush_fn_list");
addExportedSymbol(CmdArgs, "_writeout_fn_list");
} else {
diff --git a/compiler-rt/lib/profile/GCDAProfiling.c b/compiler-rt/lib/profile/GCDAProfiling.c
index 498c05900bf..b7257db10e7 100644
--- a/compiler-rt/lib/profile/GCDAProfiling.c
+++ b/compiler-rt/lib/profile/GCDAProfiling.c
@@ -619,7 +619,7 @@ void llvm_register_flush_function(fn_ptr fn) {
fn_list_insert(&flush_fn_list, fn);
}
-void __gcov_flush() {
+void __custom_llvm_gcov_flush() {
struct fn_node* curr = flush_fn_list.head;
while (curr) {
diff --git a/compiler-rt/test/tsan/pthread_atfork_deadlock2.c b/compiler-rt/test/tsan/pthread_atfork_deadlock2.c
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
index bf3e4ed3e31..37bdcfaeab8 100644
--- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
@@ -656,7 +656,7 @@ void GCOVProfiler::AddFlushBeforeForkAndExec() {
for (auto I : ForkAndExecs) {
IRBuilder<> Builder(I);
FunctionType *FTy = FunctionType::get(Builder.getVoidTy(), {}, false);
- FunctionCallee GCOVFlush = M->getOrInsertFunction("__gcov_flush", FTy);
+ FunctionCallee GCOVFlush = M->getOrInsertFunction("__custom_llvm_gcov_flush", FTy);
Builder.CreateCall(GCOVFlush);
I->getParent()->splitBasicBlock(I);
}

Просмотреть файл

@ -0,0 +1,100 @@
From be41a98ac222f33ed5558d86e1cede67249e99b5 Mon Sep 17 00:00:00 2001
From: Dmitry Vyukov <dvyukov@google.com>
Date: Sat, 21 Mar 2020 13:34:50 +0100
Subject: [PATCH] tsan: fix deadlock with pthread_atfork callbacks
This fixes the bug reported at:
https://groups.google.com/forum/#!topic/thread-sanitizer/e_zB9gYqFHM
A pthread_atfork callback triggers a data race
and we deadlock on the report_mtx. Ignore memory access
in the pthread_atfork callbacks to prevent the deadlock.
---
compiler-rt/lib/tsan/rtl/tsan_rtl.cc | 9 ++++
.../test/tsan/pthread_atfork_deadlock2.c | 49 +++++++++++++++++++
2 files changed, 58 insertions(+)
create mode 100644 compiler-rt/test/tsan/pthread_atfork_deadlock2.c
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
index 3f3c0cce119..5e324a0a5fd 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
@@ -494,14 +494,23 @@ int Finalize(ThreadState *thr) {
void ForkBefore(ThreadState *thr, uptr pc) {
ctx->thread_registry->Lock();
ctx->report_mtx.Lock();
+ // Ignore memory accesses in the pthread_atfork callbacks.
+ // If any of them triggers a data race we will deadlock
+ // on the report_mtx.
+ // We could ignore interceptors and sync operations as well,
+ // but so far it's unclear if it will do more good or harm.
+ // Unnecessarily ignoring things can lead to false positives later.
+ ThreadIgnoreBegin(thr, pc);
}
void ForkParentAfter(ThreadState *thr, uptr pc) {
+ ThreadIgnoreEnd(thr, pc); // Begin is in ForkBefore.
ctx->report_mtx.Unlock();
ctx->thread_registry->Unlock();
}
void ForkChildAfter(ThreadState *thr, uptr pc) {
+ ThreadIgnoreEnd(thr, pc); // Begin is in ForkBefore.
ctx->report_mtx.Unlock();
ctx->thread_registry->Unlock();
diff --git a/compiler-rt/test/tsan/pthread_atfork_deadlock2.c b/compiler-rt/test/tsan/pthread_atfork_deadlock2.c
new file mode 100644
index 00000000000..700507c1e63
--- /dev/null
+++ b/compiler-rt/test/tsan/pthread_atfork_deadlock2.c
@@ -0,0 +1,49 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+// Regression test for
+// https://groups.google.com/d/msg/thread-sanitizer/e_zB9gYqFHM/DmAiTsrLAwAJ
+// pthread_atfork() callback triggers a data race and we deadlocked
+// on the report_mtx as we lock it around fork.
+#include "test.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+int glob = 0;
+
+void *worker(void *unused) {
+ glob++;
+ barrier_wait(&barrier);
+ return NULL;
+}
+
+void atfork() {
+ glob++;
+}
+
+int main() {
+ barrier_init(&barrier, 2);
+ pthread_atfork(atfork, NULL, NULL);
+ pthread_t t;
+ pthread_create(&t, NULL, worker, NULL);
+ barrier_wait(&barrier);
+ pid_t pid = fork();
+ if (pid < 0) {
+ fprintf(stderr, "fork failed: %d\n", errno);
+ return 1;
+ }
+ if (pid == 0) {
+ fprintf(stderr, "CHILD\n");
+ return 0;
+ }
+ if (pid != waitpid(pid, NULL, 0)) {
+ fprintf(stderr, "waitpid failed: %d\n", errno);
+ return 1;
+ }
+ pthread_join(t, NULL);
+ fprintf(stderr, "PARENT\n");
+ return 0;
+}
+
+// CHECK-NOT: ThreadSanitizer: data race
+// CHECK: CHILD
+// CHECK: PARENT

Просмотреть файл

@ -0,0 +1,64 @@
[winasan] Unpoison the stack in NtTerminateThread
In long-running builds we've seen some ASan complaints during thread creation
that we suspect are due to leftover poisoning from previous threads whose stacks
occupied that memory. This patch adds a hook that unpoisons the stack just
before the NtTerminateThread syscall.
Differential Revision: https://reviews.llvm.org/D52091
** Update for clang 9 ** : After some backouts, this patch eventually landed
upstream in a different form, as the TLS handler `asan_thread_exit`, but that
variant causes failures in our test suite, so revert the TLS handler in favor of
the interceptor approach from the first patch.
diff --git a/compiler-rt/lib/asan/asan_win.cpp b/compiler-rt/lib/asan/asan_win.cpp
index 417892aaedd..5fe86db44f4 100644
--- a/compiler-rt/lib/asan/asan_win.cpp
+++ b/compiler-rt/lib/asan/asan_win.cpp
@@ -154,6 +154,14 @@ INTERCEPTOR_WINAPI(HANDLE, CreateThread, LPSECURITY_ATTRIBUTES security,
thr_flags, tid);
}
+INTERCEPTOR_WINAPI(void, NtTerminateThread, void *rcx) {
+ // Unpoison the terminating thread's stack because the memory may be re-used.
+ NT_TIB *tib = (NT_TIB *)NtCurrentTeb();
+ uptr stackSize = (uptr)tib->StackBase - (uptr)tib->StackLimit;
+ __asan_unpoison_memory_region(tib->StackLimit, stackSize);
+ return REAL(NtTerminateThread(rcx));
+}
+
// }}}
namespace __asan {
@@ -168,7 +176,9 @@ void InitializePlatformInterceptors() {
ASAN_INTERCEPT_FUNC(CreateThread);
ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter);
-
+ CHECK(::__interception::OverrideFunction("NtTerminateThread",
+ (uptr)WRAP(NtTerminateThread),
+ (uptr *)&REAL(NtTerminateThread)));
#ifdef _WIN64
ASAN_INTERCEPT_FUNC(__C_specific_handler);
#else
@@ -380,19 +390,6 @@ __declspec(allocate(".CRT$XLAB")) void(NTAPI *__asan_tls_init)(
void *, unsigned long, void *) = asan_thread_init;
#endif
-static void NTAPI asan_thread_exit(void *module, DWORD reason, void *reserved) {
- if (reason == DLL_THREAD_DETACH) {
- // Unpoison the thread's stack because the memory may be re-used.
- NT_TIB *tib = (NT_TIB *)NtCurrentTeb();
- uptr stackSize = (uptr)tib->StackBase - (uptr)tib->StackLimit;
- __asan_unpoison_memory_region(tib->StackLimit, stackSize);
- }
-}
-
-#pragma section(".CRT$XLY", long, read)
-__declspec(allocate(".CRT$XLY")) void(NTAPI *__asan_tls_exit)(
- void *, unsigned long, void *) = asan_thread_exit;
-
WIN_FORCE_LINK(__asan_dso_reg_hook)
// }}}

Просмотреть файл

@ -423,6 +423,13 @@ clang-9:
repo: https://github.com/llvm/llvm-project
revision: c1a0a213378a458fbea1a5c77b315c7dce08fd05
clang-10:
description: clang 10.0.0 source code
fetch:
type: git
repo: https://github.com/llvm/llvm-project
revision: d32170dbd5b0d54436537b6b75beaf44324e0c28
wasi-sdk:
description: wasi-sdk source code
fetch:

Просмотреть файл

@ -228,6 +228,27 @@ linux64-clang-9-win-cross:
- artifact: clang.tar.bz2
extract: false
linux64-clang-10:
description: "Clang 10 toolchain build"
treeherder:
symbol: TL(clang10)
run:
using: toolchain-script
script: build-clang.sh
arguments:
- 'build/build-clang/clang-10-linux64.json'
resources:
- 'build/build-clang/clang-10-linux64.json'
toolchain-artifact: public/build/clang.tar.xz
fetches:
fetch:
- clang-10
toolchain:
- linux64-binutils
- linux64-gcc-7
- wasi-sysroot
macosx64-clang:
description: "Clang toolchain build"
treeherder: