Skip to content
This repository was archived by the owner on Mar 28, 2020. It is now read-only.

Commit aabc860

Browse files
committed
Re-land r303274: "[CrashRecovery] Use SEH __try instead of VEH when available"
We have to check gCrashRecoveryEnabled before using __try. In other words, SEH works too well and we ended up recovering from crashes in implicit module builds that we weren't supposed to. Only libclang is supposed to enable CrashRecoveryContext to allow implicit module builds to crash. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@303279 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 10c594e commit aabc860

File tree

3 files changed

+155
-48
lines changed

3 files changed

+155
-48
lines changed

lib/Support/CrashRecoveryContext.cpp

Lines changed: 71 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ static bool gCrashRecoveryEnabled = false;
7878
static ManagedStatic<sys::ThreadLocal<const CrashRecoveryContext>>
7979
tlIsRecoveringFromCrash;
8080

81+
static void installExceptionOrSignalHandlers();
82+
static void uninstallExceptionOrSignalHandlers();
83+
8184
CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {}
8285

8386
CrashRecoveryContext::~CrashRecoveryContext() {
@@ -113,6 +116,23 @@ CrashRecoveryContext *CrashRecoveryContext::GetCurrent() {
113116
return CRCI->CRC;
114117
}
115118

119+
void CrashRecoveryContext::Enable() {
120+
sys::ScopedLock L(*gCrashRecoveryContextMutex);
121+
// FIXME: Shouldn't this be a refcount or something?
122+
if (gCrashRecoveryEnabled)
123+
return;
124+
gCrashRecoveryEnabled = true;
125+
installExceptionOrSignalHandlers();
126+
}
127+
128+
void CrashRecoveryContext::Disable() {
129+
sys::ScopedLock L(*gCrashRecoveryContextMutex);
130+
if (!gCrashRecoveryEnabled)
131+
return;
132+
gCrashRecoveryEnabled = false;
133+
uninstallExceptionOrSignalHandlers();
134+
}
135+
116136
void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
117137
{
118138
if (!cleanup)
@@ -140,27 +160,56 @@ CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
140160
delete cleanup;
141161
}
142162

143-
#ifdef LLVM_ON_WIN32
163+
#if defined(_MSC_VER)
164+
// If _MSC_VER is defined, we must have SEH. Use it if it's available. It's way
165+
// better than VEH. Vectored exception handling catches all exceptions happening
166+
// on the thread with installed exception handlers, so it can interfere with
167+
// internal exception handling of other libraries on that thread. SEH works
168+
// exactly as you would expect normal exception handling to work: it only
169+
// catches exceptions if they would bubble out from the stack frame with __try /
170+
// __except.
144171

145-
#include "Windows/WindowsSupport.h"
172+
static void installExceptionOrSignalHandlers() {}
173+
static void uninstallExceptionOrSignalHandlers() {}
146174

147-
// On Windows, we can make use of vectored exception handling to
148-
// catch most crashing situations. Note that this does mean
149-
// we will be alerted of exceptions *before* structured exception
150-
// handling has the opportunity to catch it. But that isn't likely
151-
// to cause problems because nowhere in the project is SEH being
152-
// used.
175+
bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
176+
if (!gCrashRecoveryEnabled) {
177+
Fn();
178+
return true;
179+
}
180+
181+
bool Result = true;
182+
__try {
183+
Fn();
184+
} __except (1) { // Catch any exception.
185+
Result = false;
186+
}
187+
return Result;
188+
}
189+
190+
#else // !_MSC_VER
191+
192+
#if defined(LLVM_ON_WIN32)
193+
// This is a non-MSVC compiler, probably mingw gcc or clang without
194+
// -fms-extensions. Use vectored exception handling (VEH).
195+
//
196+
// On Windows, we can make use of vectored exception handling to catch most
197+
// crashing situations. Note that this does mean we will be alerted of
198+
// exceptions *before* structured exception handling has the opportunity to
199+
// catch it. Unfortunately, this causes problems in practice with other code
200+
// running on threads with LLVM crash recovery contexts, so we would like to
201+
// eventually move away from VEH.
153202
//
154-
// Vectored exception handling is built on top of SEH, and so it
155-
// works on a per-thread basis.
203+
// Vectored works on a per-thread basis, which is an advantage over
204+
// SetUnhandledExceptionFilter. SetUnhandledExceptionFilter also doesn't have
205+
// any native support for chaining exception handlers, but VEH allows more than
206+
// one.
156207
//
157208
// The vectored exception handler functionality was added in Windows
158209
// XP, so if support for older versions of Windows is required,
159210
// it will have to be added.
160-
//
161-
// If we want to support as far back as Win2k, we could use the
162-
// SetUnhandledExceptionFilter API, but there's a risk of that
163-
// being entirely overwritten (it's not a chain).
211+
212+
#include "Windows/WindowsSupport.h"
164213

165214
static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
166215
{
@@ -203,14 +252,7 @@ static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
203252
// non-NULL, valid VEH handles, or NULL.
204253
static sys::ThreadLocal<const void> sCurrentExceptionHandle;
205254

206-
void CrashRecoveryContext::Enable() {
207-
sys::ScopedLock L(*gCrashRecoveryContextMutex);
208-
209-
if (gCrashRecoveryEnabled)
210-
return;
211-
212-
gCrashRecoveryEnabled = true;
213-
255+
static void installExceptionOrSignalHandlers() {
214256
// We can set up vectored exception handling now. We will install our
215257
// handler as the front of the list, though there's no assurances that
216258
// it will remain at the front (another call could install itself before
@@ -219,14 +261,7 @@ void CrashRecoveryContext::Enable() {
219261
sCurrentExceptionHandle.set(handle);
220262
}
221263

222-
void CrashRecoveryContext::Disable() {
223-
sys::ScopedLock L(*gCrashRecoveryContextMutex);
224-
225-
if (!gCrashRecoveryEnabled)
226-
return;
227-
228-
gCrashRecoveryEnabled = false;
229-
264+
static void uninstallExceptionOrSignalHandlers() {
230265
PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get());
231266
if (currentHandle) {
232267
// Now we can remove the vectored exception handler from the chain
@@ -237,7 +272,7 @@ void CrashRecoveryContext::Disable() {
237272
}
238273
}
239274

240-
#else
275+
#else // !LLVM_ON_WIN32
241276

242277
// Generic POSIX implementation.
243278
//
@@ -289,14 +324,7 @@ static void CrashRecoverySignalHandler(int Signal) {
289324
const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
290325
}
291326

292-
void CrashRecoveryContext::Enable() {
293-
sys::ScopedLock L(*gCrashRecoveryContextMutex);
294-
295-
if (gCrashRecoveryEnabled)
296-
return;
297-
298-
gCrashRecoveryEnabled = true;
299-
327+
static void installExceptionOrSignalHandlers() {
300328
// Setup the signal handler.
301329
struct sigaction Handler;
302330
Handler.sa_handler = CrashRecoverySignalHandler;
@@ -308,20 +336,13 @@ void CrashRecoveryContext::Enable() {
308336
}
309337
}
310338

311-
void CrashRecoveryContext::Disable() {
312-
sys::ScopedLock L(*gCrashRecoveryContextMutex);
313-
314-
if (!gCrashRecoveryEnabled)
315-
return;
316-
317-
gCrashRecoveryEnabled = false;
318-
339+
static void uninstallExceptionOrSignalHandlers() {
319340
// Restore the previous signal handlers.
320341
for (unsigned i = 0; i != NumSignals; ++i)
321342
sigaction(Signals[i], &PrevActions[i], nullptr);
322343
}
323344

324-
#endif
345+
#endif // !LLVM_ON_WIN32
325346

326347
bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
327348
// If crash recovery is disabled, do nothing.
@@ -339,6 +360,8 @@ bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
339360
return true;
340361
}
341362

363+
#endif // !_MSC_VER
364+
342365
void CrashRecoveryContext::HandleCrash() {
343366
CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
344367
assert(CRCI && "Crash recovery context never initialized!");

unittests/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_llvm_unittest(SupportTests
1111
BlockFrequencyTest.cpp
1212
BranchProbabilityTest.cpp
1313
CachePruningTest.cpp
14+
CrashRecoveryTest.cpp
1415
Casting.cpp
1516
Chrono.cpp
1617
CommandLineTest.cpp
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//===- llvm/unittest/Support/CrashRecoveryTest.cpp ------------------------===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "llvm/Support/CrashRecoveryContext.h"
11+
#include "llvm/Support/Compiler.h"
12+
#include "gtest/gtest.h"
13+
14+
#ifdef LLVM_ON_WIN32
15+
#define WIN32_LEAN_AND_MEAN
16+
#define NOGDI
17+
#include <windows.h>
18+
#endif
19+
20+
using namespace llvm;
21+
using namespace llvm::sys;
22+
23+
static int GlobalInt = 0;
24+
static void nullDeref() { *(volatile int *)nullptr = 0; }
25+
static void incrementGlobal() { ++GlobalInt; }
26+
static void llvmTrap() { LLVM_BUILTIN_TRAP; }
27+
28+
TEST(CrashRecoveryTest, Basic) {
29+
llvm::CrashRecoveryContext::Enable();
30+
GlobalInt = 0;
31+
EXPECT_TRUE(CrashRecoveryContext().RunSafely(incrementGlobal));
32+
EXPECT_EQ(1, GlobalInt);
33+
EXPECT_FALSE(CrashRecoveryContext().RunSafely(nullDeref));
34+
EXPECT_FALSE(CrashRecoveryContext().RunSafely(llvmTrap));
35+
}
36+
37+
struct IncrementGlobalCleanup : CrashRecoveryContextCleanup {
38+
IncrementGlobalCleanup(CrashRecoveryContext *CRC)
39+
: CrashRecoveryContextCleanup(CRC) {}
40+
virtual void recoverResources() { ++GlobalInt; }
41+
};
42+
43+
static void noop() {}
44+
45+
TEST(CrashRecoveryTest, Cleanup) {
46+
llvm::CrashRecoveryContext::Enable();
47+
GlobalInt = 0;
48+
{
49+
CrashRecoveryContext CRC;
50+
CRC.registerCleanup(new IncrementGlobalCleanup(&CRC));
51+
EXPECT_TRUE(CRC.RunSafely(noop));
52+
} // run cleanups
53+
EXPECT_EQ(1, GlobalInt);
54+
55+
GlobalInt = 0;
56+
{
57+
CrashRecoveryContext CRC;
58+
CRC.registerCleanup(new IncrementGlobalCleanup(&CRC));
59+
EXPECT_FALSE(CRC.RunSafely(nullDeref));
60+
} // run cleanups
61+
EXPECT_EQ(1, GlobalInt);
62+
}
63+
64+
#ifdef LLVM_ON_WIN32
65+
static void raiseIt() {
66+
RaiseException(123, EXCEPTION_NONCONTINUABLE, 0, NULL);
67+
}
68+
69+
TEST(CrashRecoveryTest, RaiseException) {
70+
llvm::CrashRecoveryContext::Enable();
71+
EXPECT_FALSE(CrashRecoveryContext().RunSafely(raiseIt));
72+
}
73+
74+
static void outputString() {
75+
OutputDebugStringA("output for debugger\n");
76+
}
77+
78+
TEST(CrashRecoveryTest, CallOutputDebugString) {
79+
llvm::CrashRecoveryContext::Enable();
80+
EXPECT_TRUE(CrashRecoveryContext().RunSafely(outputString));
81+
}
82+
83+
#endif

0 commit comments

Comments
 (0)