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 |