From 1cc76773d93380d2c2d072f5ef884fc2efb9485c Mon Sep 17 00:00:00 2001 From: Emilio Cota Date: Tue, 25 Mar 2025 09:26:51 -0400 Subject: [PATCH] [mlir] DistinctAttributeAllocator: fix race The bitfields added in #128566 (`threadingIsEnabled : 1` and `useThreadLocalAllocator : 1`) are accessed without synchronization. Example tsan report: ``` WARNING: ThreadSanitizer: data race (pid=337) Write of size 1 at 0x7260001d0ff0 by thread T224: #0 disableThreadLocalStorage third_party/llvm/llvm-project/mlir/lib/IR/AttributeDetail.h:434:29 #1 mlir::MLIRContext::disableThreadLocalStorage(bool) third_party/llvm/llvm-project/mlir/lib/IR/MLIRContext.cpp:723:40 #2 mlir::PassManager::runWithCrashRecovery(mlir::Operation*, mlir::AnalysisManager) third_party/llvm/llvm-project/mlir/lib/Pass/PassCrashRecovery.cpp:423:8 [...] Previous write of size 1 at 0x7260001d0ff0 by thread T222: #0 disableThreadLocalStorage third_party/llvm/llvm-project/mlir/lib/IR/AttributeDetail.h:434:29 #1 mlir::MLIRContext::disableThreadLocalStorage(bool) third_party/llvm/llvm-project/mlir/lib/IR/MLIRContext.cpp:723:40 #2 mlir::PassManager::runWithCrashRecovery(mlir::Operation*, mlir::AnalysisManager) third_party/llvm/llvm-project/mlir/lib/Pass/PassCrashRecovery.cpp:423:8 ``` Fix the race by serializing accesses to these fields with the existing `allocatorMutex`. --- mlir/lib/IR/AttributeDetail.h | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/mlir/lib/IR/AttributeDetail.h b/mlir/lib/IR/AttributeDetail.h index 8fed18140433c..6a481aad9141e 100644 --- a/mlir/lib/IR/AttributeDetail.h +++ b/mlir/lib/IR/AttributeDetail.h @@ -413,16 +413,17 @@ class DistinctAttributeAllocator { /// Allocates a distinct attribute storage using a thread local bump pointer /// allocator to enable synchronization free parallel allocations. DistinctAttrStorage *allocate(Attribute referencedAttr) { + std::unique_lock lock(allocatorMutex); if (!useThreadLocalAllocator && threadingIsEnabled) { - std::scoped_lock lock(allocatorMutex); - return allocateImpl(referencedAttr); + return allocateImpl(referencedAttr, lock); } - return allocateImpl(referencedAttr); + return allocateImpl(referencedAttr, lock); } /// Sets a flag that stores if multithreading is enabled. The flag is used to /// decide if locking is needed when using a non thread-safe allocator. void disableMultiThreading(bool disable = true) { + std::scoped_lock lock(allocatorMutex); threadingIsEnabled = !disable; } @@ -431,12 +432,15 @@ class DistinctAttributeAllocator { /// beyond the lifetime of a child thread calling this function while ensuring /// thread-safe allocation. void disableThreadLocalStorage(bool disable = true) { + std::scoped_lock lock(allocatorMutex); useThreadLocalAllocator = !disable; } private: - DistinctAttrStorage *allocateImpl(Attribute referencedAttr) { - return new (getAllocatorInUse().Allocate()) + DistinctAttrStorage *allocateImpl(Attribute referencedAttr, + const std::unique_lock &lock) { + assert(lock.owns_lock()); + return new (getAllocatorInUse(lock).Allocate()) DistinctAttrStorage(referencedAttr); } @@ -444,7 +448,9 @@ class DistinctAttributeAllocator { /// thread-local, non-thread safe bump pointer allocator is used instead to /// prevent use-after-free errors whenever attribute storage created on a /// crash recover thread is accessed after the thread joins. - llvm::BumpPtrAllocator &getAllocatorInUse() { + llvm::BumpPtrAllocator & + getAllocatorInUse(const std::unique_lock &lock) { + assert(lock.owns_lock()); if (useThreadLocalAllocator) return allocatorCache.get(); return allocator;