| // Copyright 2018 the V8 project 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 <windows.h> |
| |
| #include "include/v8.h" |
| #include "src/base/page-allocator.h" |
| #include "src/trap-handler/trap-handler.h" |
| #include "src/utils/allocation.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| #if V8_TRAP_HANDLER_SUPPORTED |
| |
| bool g_handler_got_executed = false; |
| // The start address of the virtual memory we use to cause an exception. |
| i::Address g_start_address; |
| |
| // When using V8::EnableWebAssemblyTrapHandler, we save the old one to fall back |
| // on if V8 doesn't handle the exception. This allows tools like ASan to |
| // register a handler early on during the process startup and still generate |
| // stack traces on failures. |
| class ExceptionHandlerFallbackTest : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| // Register this handler as the last handler. |
| registered_handler_ = AddVectoredExceptionHandler(/*first=*/0, TestHandler); |
| CHECK_NOT_NULL(registered_handler_); |
| |
| v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator(); |
| // We only need a single page. |
| size_t size = page_allocator->AllocatePageSize(); |
| void* hint = page_allocator->GetRandomMmapAddr(); |
| i::VirtualMemory mem(page_allocator, size, hint, size); |
| g_start_address = mem.address(); |
| // Set the permissions of the memory to no-access. |
| CHECK(mem.SetPermissions(g_start_address, size, |
| v8::PageAllocator::kNoAccess)); |
| mem_ = std::move(mem); |
| } |
| |
| void WriteToTestMemory(int value) { |
| *reinterpret_cast<volatile int*>(g_start_address) = value; |
| } |
| |
| int ReadFromTestMemory() { |
| return *reinterpret_cast<volatile int*>(g_start_address); |
| } |
| |
| void TearDown() override { |
| // be a good citizen and remove the exception handler. |
| ULONG result = RemoveVectoredExceptionHandler(registered_handler_); |
| CHECK(result); |
| } |
| |
| private: |
| static LONG WINAPI TestHandler(EXCEPTION_POINTERS* exception) { |
| g_handler_got_executed = true; |
| v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator(); |
| // Make the allocated memory accessible so that from now on memory accesses |
| // do not cause an exception anymore. |
| CHECK(i::SetPermissions(page_allocator, g_start_address, |
| page_allocator->AllocatePageSize(), |
| v8::PageAllocator::kReadWrite)); |
| // The memory access should work now, we can continue execution. |
| return EXCEPTION_CONTINUE_EXECUTION; |
| } |
| |
| i::VirtualMemory mem_; |
| void* registered_handler_; |
| }; |
| |
| TEST_F(ExceptionHandlerFallbackTest, DoTest) { |
| constexpr bool use_default_handler = true; |
| CHECK(v8::V8::EnableWebAssemblyTrapHandler(use_default_handler)); |
| // In the original test setup the test memory is protected against any kind of |
| // access. Therefore the access here causes an access violation exception, |
| // which should be caught by the exception handler we install above. In the |
| // exception handler we change the permission of the test memory to make it |
| // accessible, and then return from the exception handler to execute the |
| // memory access again. This time we expect the memory access to work. |
| constexpr int test_value = 42; |
| WriteToTestMemory(test_value); |
| CHECK_EQ(test_value, ReadFromTestMemory()); |
| CHECK(g_handler_got_executed); |
| v8::internal::trap_handler::RemoveTrapHandler(); |
| } |
| |
| #endif |
| |
| } // namespace |