Skip to content

Commit 271b46a

Browse files
Fix mutex use after free in llvm_shutdown (#5463) (#5477)
This patch is consolidated from the following upstream patches: - llvm/llvm-project@3d5360a4398b - llvm/llvm-project@928071ae4ef5 - llvm/llvm-project@56349e8b6d85 Fixes #5119 Co-authored-by: Ellie Hermaszewska <[email protected]>
1 parent a7efdff commit 271b46a

2 files changed

Lines changed: 97 additions & 67 deletions

File tree

include/llvm/Support/ManagedStatic.h

Lines changed: 69 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,77 @@
1010
// This file defines the ManagedStatic class and the llvm_shutdown() function.
1111
//
1212
//===----------------------------------------------------------------------===//
13+
//
14+
// The following changes have been backported from upstream llvm:
15+
//
16+
// 56349e8b6d85 Fix for memory leak reported by Valgrind
17+
// 928071ae4ef5 [Support] Replace sys::Mutex with their standard equivalents.
18+
// 3d5360a4398b Replace llvm::MutexGuard/UniqueLock with their standard equivalents
19+
// c06a470fc843 Try once more to ensure constant initializaton of ManagedStatics
20+
// 41fe3a54c261 Ensure that ManagedStatic is constant initialized in MSVC 2017 & 2019
21+
// 74de08031f5d [ManagedStatic] Avoid putting function pointers in template args.
22+
// f65e4ce2c48a [ADT, Support, TableGen] Fix some Clang-tidy modernize-use-default and Include What You Use warnings; other minor fixes (NFC).
23+
// 832d04207810 [ManagedStatic] Reimplement double-checked locking with std::atomic.
24+
// dd1463823a0c This is yet another attempt to re-instate r220932 as discussed in D19271.
25+
//
1326

1427
#ifndef LLVM_SUPPORT_MANAGEDSTATIC_H
1528
#define LLVM_SUPPORT_MANAGEDSTATIC_H
1629

17-
#include "llvm/Support/Atomic.h"
18-
#include "llvm/Support/Threading.h"
19-
#include "llvm/Support/Valgrind.h"
30+
#include <atomic>
31+
#include <cstddef>
2032

2133
namespace llvm {
2234

2335
/// object_creator - Helper method for ManagedStatic.
24-
template<class C>
25-
void* object_creator() {
26-
return new C();
27-
}
36+
template <class C> struct object_creator {
37+
static void *call() { return new C(); }
38+
};
2839

2940
/// object_deleter - Helper method for ManagedStatic.
3041
///
31-
template<typename T> struct object_deleter {
32-
static void call(void * Ptr) { delete (T*)Ptr; }
42+
template <typename T> struct object_deleter {
43+
static void call(void *Ptr) { delete (T *)Ptr; }
3344
};
34-
template<typename T, size_t N> struct object_deleter<T[N]> {
35-
static void call(void * Ptr) { delete[] (T*)Ptr; }
45+
template <typename T, size_t N> struct object_deleter<T[N]> {
46+
static void call(void *Ptr) { delete[](T *)Ptr; }
3647
};
3748

49+
// ManagedStatic must be initialized to zero, and it must *not* have a dynamic
50+
// initializer because managed statics are often created while running other
51+
// dynamic initializers. In standard C++11, the best way to accomplish this is
52+
// with a constexpr default constructor. However, different versions of the
53+
// Visual C++ compiler have had bugs where, even though the constructor may be
54+
// constexpr, a dynamic initializer may be emitted depending on optimization
55+
// settings. For the affected versions of MSVC, use the old linker
56+
// initialization pattern of not providing a constructor and leaving the fields
57+
// uninitialized. See http://llvm.org/PR41367 for details.
58+
#if !defined(_MSC_VER) || (_MSC_VER >= 1925) || defined(__clang__)
59+
#define LLVM_USE_CONSTEXPR_CTOR
60+
#endif
61+
3862
/// ManagedStaticBase - Common base class for ManagedStatic instances.
3963
class ManagedStaticBase {
4064
protected:
65+
#ifdef LLVM_USE_CONSTEXPR_CTOR
66+
mutable std::atomic<void *> Ptr{};
67+
mutable void (*DeleterFn)(void *) = nullptr;
68+
mutable const ManagedStaticBase *Next = nullptr;
69+
#else
4170
// This should only be used as a static variable, which guarantees that this
4271
// will be zero initialized.
43-
mutable void *Ptr;
44-
mutable void (*DeleterFn)(void*);
72+
mutable std::atomic<void *> Ptr;
73+
mutable void (*DeleterFn)(void *);
4574
mutable const ManagedStaticBase *Next;
75+
#endif
4676

4777
void RegisterManagedStatic(void *(*creator)(), void (*deleter)(void*)) const;
78+
4879
public:
80+
#ifdef LLVM_USE_CONSTEXPR_CTOR
81+
constexpr ManagedStaticBase() = default;
82+
#endif
83+
4984
/// isConstructed - Return true if this object has not been created yet.
5085
bool isConstructed() const { return Ptr != nullptr; }
5186

@@ -57,42 +92,35 @@ class ManagedStaticBase {
5792
/// libraries that link in LLVM components) and for making destruction be
5893
/// explicit through the llvm_shutdown() function call.
5994
///
60-
template<class C>
95+
template <class C, class Creator = object_creator<C>,
96+
class Deleter = object_deleter<C>>
6197
class ManagedStatic : public ManagedStaticBase {
6298
public:
63-
6499
// Accessors.
65100
C &operator*() {
66-
void* tmp = Ptr;
67-
if (llvm_is_multithreaded()) sys::MemoryFence();
68-
if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
69-
TsanHappensAfter(this);
101+
void *Tmp = Ptr.load(std::memory_order_acquire);
102+
if (!Tmp)
103+
RegisterManagedStatic(Creator::call, Deleter::call);
70104

71-
return *static_cast<C*>(Ptr);
105+
return *static_cast<C *>(Ptr.load(std::memory_order_relaxed));
72106
}
73-
C *operator->() {
74-
void* tmp = Ptr;
75-
if (llvm_is_multithreaded()) sys::MemoryFence();
76-
if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
77-
TsanHappensAfter(this);
78107

79-
return static_cast<C*>(Ptr);
80-
}
108+
C *operator->() { return &**this; }
109+
81110
const C &operator*() const {
82-
void* tmp = Ptr;
83-
if (llvm_is_multithreaded()) sys::MemoryFence();
84-
if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
85-
TsanHappensAfter(this);
111+
void *Tmp = Ptr.load(std::memory_order_acquire);
112+
if (!Tmp)
113+
RegisterManagedStatic(Creator::call, Deleter::call);
86114

87-
return *static_cast<C*>(Ptr);
115+
return *static_cast<C *>(Ptr.load(std::memory_order_relaxed));
88116
}
89-
const C *operator->() const {
90-
void* tmp = Ptr;
91-
if (llvm_is_multithreaded()) sys::MemoryFence();
92-
if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
93-
TsanHappensAfter(this);
94117

95-
return static_cast<C*>(Ptr);
118+
const C *operator->() const { return &**this; }
119+
120+
// Extract the instance, leaving the ManagedStatic uninitialized. The
121+
// user is then responsible for the lifetime of the returned instance.
122+
C *claim() {
123+
return static_cast<C *>(Ptr.exchange(nullptr));
96124
}
97125
};
98126

@@ -102,10 +130,10 @@ void llvm_shutdown();
102130
/// llvm_shutdown_obj - This is a simple helper class that calls
103131
/// llvm_shutdown() when it is destroyed.
104132
struct llvm_shutdown_obj {
105-
llvm_shutdown_obj() { }
133+
llvm_shutdown_obj() = default;
106134
~llvm_shutdown_obj() { llvm_shutdown(); }
107135
};
108136

109-
}
137+
} // end namespace llvm
110138

111-
#endif
139+
#endif // LLVM_SUPPORT_MANAGEDSTATIC_H

lib/Support/ManagedStatic.cpp

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,45 +10,46 @@
1010
// This file implements the ManagedStatic class and llvm_shutdown().
1111
//
1212
//===----------------------------------------------------------------------===//
13+
//
14+
// The following changes have been backported from upstream llvm:
15+
//
16+
// 56349e8b6d85 Fix for memory leak reported by Valgrind
17+
// 928071ae4ef5 [Support] Replace sys::Mutex with their standard equivalents.
18+
// 3d5360a4398b Replace llvm::MutexGuard/UniqueLock with their standard equivalents
19+
// c06a470fc843 Try once more to ensure constant initializaton of ManagedStatics
20+
// 41fe3a54c261 Ensure that ManagedStatic is constant initialized in MSVC 2017 & 2019
21+
// 74de08031f5d [ManagedStatic] Avoid putting function pointers in template args.
22+
// f65e4ce2c48a [ADT, Support, TableGen] Fix some Clang-tidy modernize-use-default and Include What You Use warnings; other minor fixes (NFC).
23+
// 832d04207810 [ManagedStatic] Reimplement double-checked locking with std::atomic.
24+
// dd1463823a0c This is yet another attempt to re-instate r220932 as discussed in D19271.
25+
//
1326

1427
#include "llvm/Support/ManagedStatic.h"
1528
#include "llvm/Config/config.h"
16-
#include "llvm/Support/Atomic.h"
17-
#include "llvm/Support/Mutex.h"
18-
#include "llvm/Support/MutexGuard.h"
29+
#include "llvm/Support/Threading.h"
1930
#include <cassert>
31+
#include <mutex>
2032
using namespace llvm;
2133

2234
static const ManagedStaticBase *StaticList = nullptr;
2335

24-
static sys::Mutex& getManagedStaticMutex() {
25-
// We need to use a function local static here, since this can get called
26-
// during a static constructor and we need to guarantee that it's initialized
27-
// correctly.
28-
static sys::Mutex ManagedStaticMutex;
29-
return ManagedStaticMutex;
36+
static std::recursive_mutex *getManagedStaticMutex() {
37+
static std::recursive_mutex m;
38+
return &m;
3039
}
3140

3241
void ManagedStaticBase::RegisterManagedStatic(void *(*Creator)(),
3342
void (*Deleter)(void*)) const {
3443
assert(Creator);
3544
if (llvm_is_multithreaded()) {
36-
MutexGuard Lock(getManagedStaticMutex());
37-
38-
if (!Ptr) {
39-
void* tmp = Creator();
45+
std::lock_guard<std::recursive_mutex> Lock(*getManagedStaticMutex());
4046

41-
TsanHappensBefore(this);
42-
sys::MemoryFence();
47+
if (!Ptr.load(std::memory_order_relaxed)) {
48+
void *Tmp = Creator();
4349

44-
// This write is racy against the first read in the ManagedStatic
45-
// accessors. The race is benign because it does a second read after a
46-
// memory fence, at which point it isn't possible to get a partial value.
47-
TsanIgnoreWritesBegin();
48-
Ptr = tmp;
49-
TsanIgnoreWritesEnd();
50+
Ptr.store(Tmp, std::memory_order_release);
5051
DeleterFn = Deleter;
51-
52+
5253
// Add to list of managed statics.
5354
Next = StaticList;
5455
StaticList = this;
@@ -58,7 +59,7 @@ void ManagedStaticBase::RegisterManagedStatic(void *(*Creator)(),
5859
"Partially initialized ManagedStatic!?");
5960
Ptr = Creator();
6061
DeleterFn = Deleter;
61-
62+
6263
// Add to list of managed statics.
6364
Next = StaticList;
6465
StaticList = this;
@@ -75,16 +76,17 @@ void ManagedStaticBase::destroy() const {
7576

7677
// Destroy memory.
7778
DeleterFn(Ptr);
78-
79+
7980
// Cleanup.
8081
Ptr = nullptr;
8182
DeleterFn = nullptr;
8283
}
8384

8485
/// llvm_shutdown - Deallocate and destroy all ManagedStatic variables.
86+
/// IMPORTANT: it's only safe to call llvm_shutdown() in single thread,
87+
/// without any other threads executing LLVM APIs.
88+
/// llvm_shutdown() should be the last use of LLVM APIs.
8589
void llvm::llvm_shutdown() {
86-
MutexGuard Lock(getManagedStaticMutex());
87-
8890
while (StaticList)
8991
StaticList->destroy();
9092
}

0 commit comments

Comments
 (0)