forked from Pissandshittium/pissandshittium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Mac] Redo NSException handling, and generate better stacks for C++ e…
…xceptions. This introduces base::mac::CallWithEHFrame(), which will run a block in a stack frame that is set up to stop searching for an exception handler when it is reached. This is used to prevent a top-level exception handler, installed in CFRunLoopRunSpecific(), from catching and rethrowing C++ exceptions. When it does this, the resulting stack traces are not useful for triage purposes. By stoping the search for an exception handler, the runtime will call std::terminate directly from the throwing stack trace. In addition, NSException handling is converted from swizzling the designated initializer to an ObjC 2.0 exception preprocessor. Some exceptions could still escape this mechanism, if they are thrown without CallWithEHFrame() on the stack. This could happen if the exception were thrown outside of the MessagePump's work sources or -sendEvent:. Possible situations are things like the CFRunLoop block and Mach port callouts. This shouldn't happen from Chromium code, so it would only affect system-thrown exceptions. These exceptions would still be fatal, just with the less-useful run loop stack. To summarize: all uncaught exceptions are fatal one way or another. If the exception isn't caught before it unwinds to CallWithEHFrame, the crash will have an immediately actionable stack from the point of throw. If the exception is caught by the run loop or some other top-level exception handler, the exception will be rethrown and the crash stack will be less useful. In that case, if it's an ObjC exception, a crash key will still record a trace from the point-of- throw. Using try/catch is now unrestricted, and no special whitelist is maintained to allow certain NSExceptions to be alloc/init'd. BUG=503128 R=shess@chromium.org, thakis@chromium.org Review URL: https://codereview.chromium.org/1212093002 Cr-Commit-Position: refs/heads/master@{#338332}
- Loading branch information
Showing
9 changed files
with
374 additions
and
337 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Copyright 2015 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "base/mac/call_with_eh_frame.h" | ||
|
||
#include <unwind.h> | ||
|
||
#include "build/build_config.h" | ||
|
||
namespace base { | ||
namespace mac { | ||
|
||
_Unwind_Reason_Code CxxPersonalityRoutine( | ||
int version, | ||
_Unwind_Action actions, | ||
uint64_t exceptionClass, | ||
struct _Unwind_Exception* exceptionObject, | ||
struct _Unwind_Context* context) { | ||
// Tell libunwind that this is the end of the stack. When it encounters the | ||
// CallWithEHFrame, it will stop searching for an exception handler. The | ||
// result is that no exception handler has been found higher on the stack, | ||
// and any that are lower on the stack (e.g. in CFRunLoopRunSpecific), will | ||
// now be skipped. Since this is reporting the end of the stack, and no | ||
// exception handler will have been found, std::terminate() will be called. | ||
return _URC_END_OF_STACK; | ||
} | ||
|
||
#if defined(OS_IOS) | ||
// No iOS assembly implementation exists, so just call the block directly. | ||
void CallWithEHFrame(void (^block)(void)) { | ||
block(); | ||
} | ||
#endif | ||
|
||
} // namespace mac | ||
} // namespace base |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Copyright 2015 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#ifndef BASE_MAC_CALL_WITH_EH_FRAME_H_ | ||
#define BASE_MAC_CALL_WITH_EH_FRAME_H_ | ||
|
||
#include "base/base_export.h" | ||
|
||
namespace base { | ||
namespace mac { | ||
|
||
// Invokes the specified block in a stack frame with a special exception | ||
// handler. This function creates an exception handling stack frame that | ||
// specifies a custom C++ exception personality routine, which terminates the | ||
// search for an exception handler at this frame. | ||
// | ||
// The purpose of this function is to prevent a try/catch statement in system | ||
// libraries, acting as a global exception handler, from handling exceptions | ||
// in such a way that disrupts the generation of useful stack traces. | ||
void BASE_EXPORT CallWithEHFrame(void (^block)(void)); | ||
|
||
} // namespace mac | ||
} // namespace base | ||
|
||
#endif // BASE_MAC_CALL_WITH_EH_FRAME_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Copyright 2015 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
// base::mac::CallWithEHFrame(void () block_pointer) | ||
#define CALL_WITH_EH_FRAME __ZN4base3mac15CallWithEHFrameEU13block_pointerFvvE | ||
|
||
.section __TEXT,__text,regular,pure_instructions | ||
#if !defined(COMPONENT_BUILD) | ||
.private_extern CALL_WITH_EH_FRAME | ||
#endif | ||
.globl CALL_WITH_EH_FRAME | ||
.align 4 | ||
CALL_WITH_EH_FRAME: | ||
|
||
.cfi_startproc | ||
|
||
// Configure the C++ exception handler personality routine. Normally the | ||
// compiler would emit ___gxx_personality_v0 here. The purpose of this | ||
// function is to use a custom personality routine. | ||
.cfi_personality 155, __ZN4base3mac21CxxPersonalityRoutineEi14_Unwind_ActionyP17_Unwind_ExceptionP15_Unwind_Context | ||
.cfi_lsda 16, CallWithEHFrame_exception_table | ||
|
||
Lfunction_start: | ||
pushq %rbp | ||
.cfi_def_cfa_offset 16 | ||
.cfi_offset %rbp, -16 | ||
movq %rsp, %rbp | ||
.cfi_def_cfa_register %rbp | ||
|
||
// Load the function pointer from the block descriptor. | ||
movq 16(%rdi), %rax | ||
|
||
// Execute the block in the context of a C++ try{}. | ||
Ltry_start: | ||
callq *%rax | ||
Ltry_end: | ||
popq %rbp | ||
ret | ||
|
||
// Landing pad for the exception handler. This should never be called, since | ||
// the personality routine will stop the search for an exception handler, | ||
// which will cause the runtime to invoke the default terminate handler. | ||
Lcatch: | ||
movq %rax, %rdi | ||
callq ___cxa_begin_catch // The ABI requires a call to the catch handler. | ||
ud2 // In the event this is called, make it fatal. | ||
|
||
Lfunction_end: | ||
.cfi_endproc | ||
|
||
// The C++ exception table that is used to identify this frame as an | ||
// exception handler. See http://llvm.org/docs/ExceptionHandling.html and | ||
// http://mentorembedded.github.io/cxx-abi/exceptions.pdf. | ||
.section __TEXT,__gcc_except_tab | ||
.align 2 | ||
CallWithEHFrame_exception_table: | ||
.byte 255 // DW_EH_PE_omit | ||
.byte 155 // DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4 | ||
.asciz "\242\200\200" // LE int128 for the number of bytes in this table. | ||
.byte 3 // DW_EH_PE_udata4 | ||
.byte 26 // Callsite table length. | ||
|
||
// First callsite. | ||
CS1_begin = Ltry_start - Lfunction_start | ||
.long CS1_begin | ||
CS1_end = Ltry_end - Ltry_start | ||
.long CS1_end | ||
|
||
// First landing pad. | ||
LP1 = Lcatch - Lfunction_start | ||
.long LP1 | ||
.byte 1 // Action record. | ||
|
||
// Second callsite. | ||
CS2_begin = Ltry_end - Lfunction_start | ||
.long CS2_begin | ||
CS2_end = Lfunction_end - Ltry_end | ||
.long CS2_end | ||
|
||
// Second landing pad (none). | ||
.long 0 | ||
.byte 0 // No action. | ||
|
||
// Action table. | ||
.byte 1 // Action record 1. | ||
.byte 0 // No further action to take. | ||
.long 0 // No type filter for this catch(){} clause. | ||
.align 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Copyright 2015 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "base/mac/call_with_eh_frame.h" | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
#include "testing/gtest/include/gtest/gtest.h" | ||
|
||
namespace base { | ||
namespace mac { | ||
namespace { | ||
|
||
class CallWithEHFrameTest : public testing::Test { | ||
protected: | ||
void ThrowException() { | ||
[NSArray arrayWithObject:nil]; | ||
} | ||
}; | ||
|
||
// Catching from within the EHFrame is allowed. | ||
TEST_F(CallWithEHFrameTest, CatchExceptionHigher) { | ||
bool __block saw_exception = false; | ||
base::mac::CallWithEHFrame(^{ | ||
@try { | ||
ThrowException(); | ||
} @catch (NSException* exception) { | ||
saw_exception = true; | ||
} | ||
}); | ||
EXPECT_TRUE(saw_exception); | ||
} | ||
|
||
// Trying to catch an exception outside the EHFrame is blocked. | ||
TEST_F(CallWithEHFrameTest, CatchExceptionLower) { | ||
auto catch_exception_lower = ^{ | ||
bool saw_exception = false; | ||
@try { | ||
base::mac::CallWithEHFrame(^{ | ||
ThrowException(); | ||
}); | ||
} @catch (NSException* exception) { | ||
saw_exception = true; | ||
} | ||
ASSERT_FALSE(saw_exception); | ||
}; | ||
EXPECT_DEATH(catch_exception_lower(), ""); | ||
} | ||
|
||
} // namespace | ||
} // namespace mac | ||
} // namespace base |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.