v8/test/unittests/allocation-unittest.cc

174 lines
5.8 KiB
C++
Raw Normal View History

// Copyright 2017 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 "src/allocation.h"
#if V8_OS_POSIX
#include <setjmp.h>
#include <signal.h>
#include <unistd.h> // NOLINT
#endif // V8_OS_POSIX
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
// TODO(eholk): Add a windows version of permissions tests.
#if V8_OS_POSIX
namespace {
// These tests make sure the routines to allocate memory do so with the correct
// permissions.
//
// Unfortunately, there is no API to find the protection of a memory address,
// so instead we test permissions by installing a signal handler, probing a
// memory location and recovering from the fault.
//
// We don't test the execution permission because to do so we'd have to
// dynamically generate code and test if we can execute it.
class MemoryAllocationPermissionsTest : public ::testing::Test {
static void SignalHandler(int signal, siginfo_t* info, void*) {
siglongjmp(continuation_, 1);
}
struct sigaction old_action_;
// On Mac, sometimes we get SIGBUS instead of SIGSEGV.
#if V8_OS_MACOSX
struct sigaction old_bus_action_;
#endif
protected:
void SetUp() override {
struct sigaction action;
action.sa_sigaction = SignalHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &action, &old_action_);
#if V8_OS_MACOSX
sigaction(SIGBUS, &action, &old_bus_action_);
#endif
}
void TearDown() override {
// Be a good citizen and restore the old signal handler.
sigaction(SIGSEGV, &old_action_, nullptr);
#if V8_OS_MACOSX
sigaction(SIGBUS, &old_bus_action_, nullptr);
#endif
}
public:
static sigjmp_buf continuation_;
enum class MemoryAction { kRead, kWrite };
void ProbeMemory(volatile int* buffer, MemoryAction action,
bool should_succeed) {
const int save_sigs = 1;
if (!sigsetjmp(continuation_, save_sigs)) {
switch (action) {
case MemoryAction::kRead: {
// static_cast to remove the reference and force a memory read.
USE(static_cast<int>(*buffer));
break;
}
case MemoryAction::kWrite: {
*buffer = 0;
break;
}
}
if (should_succeed) {
SUCCEED();
} else {
FAIL();
}
return;
}
if (should_succeed) {
FAIL();
} else {
SUCCEED();
}
}
void TestPermissions(PageAllocator::Permission permission, bool can_read,
bool can_write) {
v8::PageAllocator* page_allocator =
v8::internal::GetPlatformPageAllocator();
const size_t page_size = page_allocator->AllocatePageSize();
int* buffer = static_cast<int*>(AllocatePages(
page_allocator, nullptr, page_size, page_size, permission));
ProbeMemory(buffer, MemoryAction::kRead, can_read);
ProbeMemory(buffer, MemoryAction::kWrite, can_write);
CHECK(FreePages(page_allocator, buffer, page_size));
}
};
sigjmp_buf MemoryAllocationPermissionsTest::continuation_;
} // namespace
TEST_F(MemoryAllocationPermissionsTest, DoTest) {
TestPermissions(PageAllocator::Permission::kNoAccess, false, false);
TestPermissions(PageAllocator::Permission::kRead, true, false);
TestPermissions(PageAllocator::Permission::kReadWrite, true, true);
Revert "[platform] Remove {PageAllocator::kReadWriteExecute}." This reverts commit bf19e60cc564f0cacd345f3191a6f860bf36c9f8. Reason for revert: Two issues discovered with W^X in V8's 6.5 branch (see v8:7272 and chromium:793428). Still need a way to disable the feature. Original change's description: > [platform] Remove {PageAllocator::kReadWriteExecute}. > > Now that write-protection of code memory is enabled everywhere and V8 is > fully W^X compliant, we can remove the permission mode in question. > > R=​hpayer@chromium.org > BUG=v8:6792 > > Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng > Change-Id: I80fe95ac6bb0e2d1ad6d993154ce45d492d941be > Reviewed-on: https://chromium-review.googlesource.com/866855 > Commit-Queue: Michael Starzinger <mstarzinger@chromium.org> > Reviewed-by: Hannes Payer <hpayer@chromium.org> > Reviewed-by: Bill Budge <bbudge@chromium.org> > Cr-Commit-Position: refs/heads/master@{#50770} TBR=bbudge@chromium.org,mstarzinger@chromium.org,hpayer@chromium.org # Not skipping CQ checks because original CL landed > 1 day ago. Bug: v8:6792 Change-Id: If4a205497ac83084a4092560363affb13b391462 Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng Reviewed-on: https://chromium-review.googlesource.com/883461 Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Reviewed-by: Hannes Payer <hpayer@chromium.org> Commit-Queue: Michael Starzinger <mstarzinger@chromium.org> Cr-Commit-Position: refs/heads/master@{#50834}
2018-01-24 11:50:28 +00:00
TestPermissions(PageAllocator::Permission::kReadWriteExecute, true, true);
TestPermissions(PageAllocator::Permission::kReadExecute, true, false);
}
#endif // V8_OS_POSIX
// Basic tests of allocation.
class AllocationTest : public ::testing::Test {};
TEST(AllocationTest, AllocateAndFree) {
size_t page_size = v8::internal::AllocatePageSize();
CHECK_NE(0, page_size);
v8::PageAllocator* page_allocator = v8::internal::GetPlatformPageAllocator();
// A large allocation, aligned at native allocation granularity.
const size_t kAllocationSize = 1 * v8::internal::MB;
void* mem_addr = v8::internal::AllocatePages(
page_allocator, page_allocator->GetRandomMmapAddr(), kAllocationSize,
page_size, PageAllocator::Permission::kReadWrite);
CHECK_NOT_NULL(mem_addr);
CHECK(v8::internal::FreePages(page_allocator, mem_addr, kAllocationSize));
// A large allocation, aligned significantly beyond native granularity.
const size_t kBigAlignment = 64 * v8::internal::MB;
void* aligned_mem_addr = v8::internal::AllocatePages(
page_allocator,
AlignedAddress(page_allocator->GetRandomMmapAddr(), kBigAlignment),
kAllocationSize, kBigAlignment, PageAllocator::Permission::kReadWrite);
CHECK_NOT_NULL(aligned_mem_addr);
CHECK_EQ(aligned_mem_addr, AlignedAddress(aligned_mem_addr, kBigAlignment));
CHECK(v8::internal::FreePages(page_allocator, aligned_mem_addr,
kAllocationSize));
}
TEST(AllocationTest, ReserveMemory) {
v8::PageAllocator* page_allocator = v8::internal::GetPlatformPageAllocator();
size_t page_size = v8::internal::AllocatePageSize();
const size_t kAllocationSize = 1 * v8::internal::MB;
void* mem_addr = v8::internal::AllocatePages(
page_allocator, page_allocator->GetRandomMmapAddr(), kAllocationSize,
page_size, PageAllocator::Permission::kReadWrite);
CHECK_NE(0, page_size);
CHECK_NOT_NULL(mem_addr);
size_t commit_size = page_allocator->CommitPageSize();
CHECK(v8::internal::SetPermissions(page_allocator, mem_addr, commit_size,
PageAllocator::Permission::kReadWrite));
// Check whether we can write to memory.
int* addr = static_cast<int*>(mem_addr);
addr[v8::internal::KB - 1] = 2;
CHECK(v8::internal::SetPermissions(page_allocator, mem_addr, commit_size,
PageAllocator::Permission::kNoAccess));
CHECK(v8::internal::FreePages(page_allocator, mem_addr, kAllocationSize));
}
} // namespace internal
} // namespace v8