| Index: chrome_frame/test/exception_barrier_unittest.cc
|
| diff --git a/chrome_frame/test/exception_barrier_unittest.cc b/chrome_frame/test/exception_barrier_unittest.cc
|
| deleted file mode 100644
|
| index 3274318cdd7e5b0aabb05118ace372f26479b6d9..0000000000000000000000000000000000000000
|
| --- a/chrome_frame/test/exception_barrier_unittest.cc
|
| +++ /dev/null
|
| @@ -1,358 +0,0 @@
|
| -// Copyright (c) 2010 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 "gtest/gtest.h"
|
| -#include "chrome_frame/exception_barrier.h"
|
| -
|
| -namespace {
|
| -
|
| -// retrieves the top SEH registration record
|
| -__declspec(naked) EXCEPTION_REGISTRATION* GetTopRegistration() {
|
| - __asm {
|
| - mov eax, FS:0
|
| - ret
|
| - }
|
| -}
|
| -
|
| -// This function walks the SEH chain and attempts to ascertain whether it's
|
| -// sane, or rather tests it for any obvious signs of insanity.
|
| -// The signs it's capable of looking for are:
|
| -// # Is each exception registration in bounds of our stack
|
| -// # Is the registration DWORD aligned
|
| -// # Does each exception handler point to a module, as opposed to
|
| -// e.g. into the stack or never-never land.
|
| -// # Do successive entries in the exception chain increase
|
| -// monotonically in address
|
| -void TestSEHChainSane() {
|
| - // get the skinny on our stack segment
|
| - MEMORY_BASIC_INFORMATION info = { 0 };
|
| - // Note that we pass the address of the info struct just as a handy
|
| - // moniker to anything at all inside our stack allocation
|
| - ASSERT_NE(0u, ::VirtualQuery(&info, &info, sizeof(info)));
|
| -
|
| - // The lower bound of our stack.
|
| - // We use the address of info as a lower bound, this assumes that if this
|
| - // function has an SEH handler, it'll be higher up in our invocation
|
| - // record.
|
| - EXCEPTION_REGISTRATION* limit =
|
| - reinterpret_cast<EXCEPTION_REGISTRATION*>(&info);
|
| - // the very top of our stack segment
|
| - EXCEPTION_REGISTRATION* top =
|
| - reinterpret_cast<EXCEPTION_REGISTRATION*>(
|
| - reinterpret_cast<char*>(info.BaseAddress) + info.RegionSize);
|
| -
|
| - EXCEPTION_REGISTRATION* curr = GetTopRegistration();
|
| - // there MUST be at least one registration
|
| - ASSERT_TRUE(NULL != curr);
|
| -
|
| - EXCEPTION_REGISTRATION* prev = NULL;
|
| - const EXCEPTION_REGISTRATION* kSentinel =
|
| - reinterpret_cast<EXCEPTION_REGISTRATION*>(0xFFFFFFFF);
|
| - for (; kSentinel != curr; prev = curr, curr = curr->prev) {
|
| - // registrations must increase monotonically
|
| - ASSERT_TRUE(curr > prev);
|
| - // Check it's in bounds
|
| - ASSERT_GE(top, curr);
|
| - ASSERT_LT(limit, curr);
|
| -
|
| - // check for DWORD alignment
|
| - ASSERT_EQ(0, (reinterpret_cast<UINT_PTR>(prev) & 0x00000003));
|
| -
|
| - // find the module hosting the handler
|
| - ASSERT_NE(0u, ::VirtualQuery(curr->handler, &info, sizeof(info)));
|
| - wchar_t module_filename[MAX_PATH];
|
| - ASSERT_NE(0u, ::GetModuleFileName(
|
| - reinterpret_cast<HMODULE>(info.AllocationBase),
|
| - module_filename, ARRAYSIZE(module_filename)));
|
| - }
|
| -}
|
| -
|
| -void AccessViolationCrash() {
|
| - volatile char* null = NULL;
|
| - *null = '\0';
|
| -}
|
| -
|
| -// A simple crash over the exception barrier
|
| -void CrashOverExceptionBarrier() {
|
| - ExceptionBarrierCustomHandler barrier;
|
| -
|
| - TestSEHChainSane();
|
| -
|
| - AccessViolationCrash();
|
| -
|
| - TestSEHChainSane();
|
| -}
|
| -
|
| -#pragma warning(push)
|
| -// Inline asm assigning to 'FS:0' : handler not registered as safe handler
|
| -// This warning is in error (the compiler can't know that we register the
|
| -// handler as a safe SEH handler in an .asm file)
|
| -#pragma warning(disable:4733)
|
| -// Hand-generate an SEH frame implicating the ExceptionBarrierCallCustomHandler,
|
| -// then crash to invoke it.
|
| -__declspec(naked) void CrashOnManualSEHBarrierHandler() {
|
| - __asm {
|
| - push ExceptionBarrierCallCustomHandler
|
| - push FS:0
|
| - mov FS:0, esp
|
| - call AccessViolationCrash
|
| - ret
|
| - }
|
| -}
|
| -#pragma warning(pop)
|
| -
|
| -
|
| -class ExceptionBarrierTest: public testing::Test {
|
| - public:
|
| - ExceptionBarrierTest() {
|
| - }
|
| -
|
| - // Install an exception handler for the ExceptionBarrier, and
|
| - // set the handled flag to false. This allows us to see whether
|
| - // the ExceptionBarrier gets to handle the exception
|
| - virtual void SetUp() {
|
| - ExceptionBarrierConfig::set_enabled(true);
|
| - ExceptionBarrierCustomHandler::set_custom_handler(&ExceptionHandler);
|
| - s_handled_ = false;
|
| -
|
| - TestSEHChainSane();
|
| - }
|
| -
|
| - virtual void TearDown() {
|
| - TestSEHChainSane();
|
| - ExceptionBarrierCustomHandler::set_custom_handler(NULL);
|
| - ExceptionBarrierConfig::set_enabled(false);
|
| - }
|
| -
|
| - // The exception notification callback, sets the handled flag.
|
| - static void CALLBACK ExceptionHandler(EXCEPTION_POINTERS* ptrs) {
|
| - TestSEHChainSane();
|
| - s_handled_ = true;
|
| - }
|
| -
|
| - // Flag is set by handler
|
| - static bool s_handled_;
|
| -};
|
| -
|
| -bool ExceptionBarrierTest::s_handled_ = false;
|
| -
|
| -bool TestExceptionExceptionBarrierHandler() {
|
| - TestSEHChainSane();
|
| - __try {
|
| - CrashOnManualSEHBarrierHandler();
|
| - return false; // not reached
|
| - } __except(EXCEPTION_ACCESS_VIOLATION == GetExceptionCode() ?
|
| - EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
|
| - TestSEHChainSane();
|
| - return true;
|
| - }
|
| -
|
| - return false; // not reached
|
| -}
|
| -
|
| -typedef EXCEPTION_DISPOSITION
|
| -(__cdecl* ExceptionBarrierHandlerFunc)(
|
| - struct _EXCEPTION_RECORD* exception_record,
|
| - void* establisher_frame,
|
| - struct _CONTEXT* context,
|
| - void* reserved);
|
| -
|
| -TEST_F(ExceptionBarrierTest, RegisterUnregister) {
|
| - // Assert that registration modifies the chain
|
| - // and the registered record as expected
|
| - EXCEPTION_REGISTRATION* top = GetTopRegistration();
|
| - ExceptionBarrierHandlerFunc handler = top->handler;
|
| - EXCEPTION_REGISTRATION* prev = top->prev;
|
| -
|
| - EXCEPTION_REGISTRATION registration;
|
| - ::RegisterExceptionRecord(®istration, ExceptionBarrierHandler);
|
| - EXPECT_EQ(GetTopRegistration(), ®istration);
|
| - EXPECT_EQ(&ExceptionBarrierHandler, registration.handler);
|
| - EXPECT_EQ(top, registration.prev);
|
| -
|
| - // test the whole chain for good measure
|
| - TestSEHChainSane();
|
| -
|
| - // Assert that unregistration restores
|
| - // everything as expected
|
| - ::UnregisterExceptionRecord(®istration);
|
| - EXPECT_EQ(top, GetTopRegistration());
|
| - EXPECT_EQ(handler, top->handler);
|
| - EXPECT_EQ(prev, top->prev);
|
| -
|
| - // and again test the whole chain for good measure
|
| - TestSEHChainSane();
|
| -}
|
| -
|
| -
|
| -TEST_F(ExceptionBarrierTest, ExceptionBarrierHandler) {
|
| - EXPECT_TRUE(TestExceptionExceptionBarrierHandler());
|
| - EXPECT_TRUE(s_handled_);
|
| -}
|
| -
|
| -bool TestExceptionBarrier() {
|
| - __try {
|
| - CrashOverExceptionBarrier();
|
| - } __except(EXCEPTION_ACCESS_VIOLATION == GetExceptionCode() ?
|
| - EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
|
| - TestSEHChainSane();
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| -TEST_F(ExceptionBarrierTest, HandlesAccessViolationException) {
|
| - TestExceptionBarrier();
|
| - EXPECT_TRUE(s_handled_);
|
| -}
|
| -
|
| -void RecurseAndCrash(int depth) {
|
| - __try {
|
| - __try {
|
| - if (0 == depth)
|
| - AccessViolationCrash();
|
| - else
|
| - RecurseAndCrash(depth - 1);
|
| -
|
| - TestSEHChainSane();
|
| - } __except(EXCEPTION_CONTINUE_SEARCH) {
|
| - TestSEHChainSane();
|
| - }
|
| - } __finally {
|
| - TestSEHChainSane();
|
| - }
|
| -}
|
| -
|
| -// This test exists only for comparison with TestExceptionBarrierChaining, and
|
| -// to "document" how the SEH chain is manipulated under our compiler.
|
| -// The two tests are expected to both fail if the particulars of the compiler's
|
| -// SEH implementation happens to change.
|
| -bool TestRegularChaining(EXCEPTION_REGISTRATION* top) {
|
| - // This test relies on compiler-dependent details, notably we rely on the
|
| - // compiler to generate a single SEH frame for the entire function, as
|
| - // opposed to e.g. generating a separate SEH frame for each __try __except
|
| - // statement.
|
| - EXCEPTION_REGISTRATION* my_top = GetTopRegistration();
|
| - if (my_top == top)
|
| - return false;
|
| -
|
| - __try {
|
| - // we should have the new entry in effect here still
|
| - if (GetTopRegistration() != my_top)
|
| - return false;
|
| - } __except(EXCEPTION_EXECUTE_HANDLER) {
|
| - return false;
|
| - }
|
| -
|
| - __try {
|
| - AccessViolationCrash();
|
| - return false; // not reached
|
| - } __except(EXCEPTION_EXECUTE_HANDLER) {
|
| - // and here
|
| - if (GetTopRegistration() != my_top)
|
| - return false;
|
| - }
|
| -
|
| - __try {
|
| - RecurseAndCrash(10);
|
| - return false; // not reached
|
| - } __except(EXCEPTION_EXECUTE_HANDLER) {
|
| - // we should have unrolled to our frame by now
|
| - if (GetTopRegistration() != my_top)
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void RecurseAndCrashOverBarrier(int depth, bool crash) {
|
| - ExceptionBarrierCustomHandler barrier;
|
| -
|
| - if (0 == depth) {
|
| - if (crash)
|
| - AccessViolationCrash();
|
| - } else {
|
| - RecurseAndCrashOverBarrier(depth - 1, crash);
|
| - }
|
| -}
|
| -
|
| -// Test that ExceptionBarrier doesn't molest the SEH chain, neither
|
| -// for regular unwinding, nor on exception unwinding cases.
|
| -//
|
| -// Note that while this test shows the ExceptionBarrier leaves the chain
|
| -// sane on both those cases, it's not clear that it does the right thing
|
| -// during first-chance exception handling. I can't think of a way to test
|
| -// that though, because first-chance exception handling is very difficult
|
| -// to hook into and to observe.
|
| -static bool TestExceptionBarrierChaining(EXCEPTION_REGISTRATION* top) {
|
| - TestSEHChainSane();
|
| -
|
| - // This test relies on compiler-dependent details, notably we rely on the
|
| - // compiler to generate a single SEH frame for the entire function, as
|
| - // opposed to e.g. generating a separate SEH frame for each __try __except
|
| - // statement.
|
| - // Unfortunately we can't use ASSERT macros here, because they create
|
| - // temporary objects and the compiler doesn't grok non POD objects
|
| - // intermingled with __try and other SEH constructs.
|
| - EXCEPTION_REGISTRATION* my_top = GetTopRegistration();
|
| - if (my_top == top)
|
| - return false;
|
| -
|
| - __try {
|
| - // we should have the new entry in effect here still
|
| - if (GetTopRegistration() != my_top)
|
| - return false;
|
| - } __except(EXCEPTION_EXECUTE_HANDLER) {
|
| - return false; // Not reached
|
| - }
|
| -
|
| - __try {
|
| - CrashOverExceptionBarrier();
|
| - return false; // Not reached
|
| - } __except(EXCEPTION_EXECUTE_HANDLER) {
|
| - // and here
|
| - if (GetTopRegistration() != my_top)
|
| - return false;
|
| - }
|
| - TestSEHChainSane();
|
| -
|
| - __try {
|
| - RecurseAndCrashOverBarrier(10, true);
|
| - return false; // not reached
|
| - } __except(EXCEPTION_EXECUTE_HANDLER) {
|
| - // we should have unrolled to our frame by now
|
| - if (GetTopRegistration() != my_top)
|
| - return false;
|
| - }
|
| - TestSEHChainSane();
|
| -
|
| - __try {
|
| - RecurseAndCrashOverBarrier(10, false);
|
| -
|
| - // we should have unrolled to our frame by now
|
| - if (GetTopRegistration() != my_top)
|
| - return false;
|
| - } __except(EXCEPTION_EXECUTE_HANDLER) {
|
| - return false; // not reached
|
| - }
|
| - TestSEHChainSane();
|
| -
|
| - // success.
|
| - return true;
|
| -}
|
| -
|
| -static bool TestChaining() {
|
| - EXCEPTION_REGISTRATION* top = GetTopRegistration();
|
| -
|
| - return TestRegularChaining(top) && TestExceptionBarrierChaining(top);
|
| -}
|
| -
|
| -// Test that the SEH chain is unmolested by exception barrier, both under
|
| -// regular unroll, and under exception unroll.
|
| -TEST_F(ExceptionBarrierTest, SEHChainIsSaneAfterException) {
|
| - EXPECT_TRUE(TestChaining());
|
| -}
|
| -
|
| -} // namespace
|
|
|