Revert "Implement a fake virtual memory cage mechanism"
This reverts commit 1ea76c1397
.
Reason for revert: The unit test added fails on the Fuchsia bot https://ci.chromium.org/p/v8/builders/ci/V8%20Fuchsia/25976?
Original change's description:
> Implement a fake virtual memory cage mechanism
>
> On operating systems where reserving virtual address space is expensive,
> notably Windows pre 8.1, it is not possible to create a proper virtual
> memory cage. In order to still be able to reference caged objects
> through offsets from the cage base on these systems, this CL introduces
> a fake cage mechanism. When the fake cage is used, most of the virtual
> memory for the cage is not actually reserved. Instead, the cage's page
> allocator simply relies on hints to the OS to obtain pages inside the
> cage. This does, however, not provide the same security benefits as a
> real cage as unrelated allocations might end up inside the cage.
>
> Bug: chromium:1218005
> Change-Id: Ie5314be23966ed0042a017917b63595481b5e7e3
> Cq-Include-Trybots: luci.v8.try:v8_linux64_heap_sandbox_dbg_ng,v8_linux_arm64_sim_heap_sandbox_dbg_ng
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3217200
> Commit-Queue: Samuel Groß <saelo@chromium.org>
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#77367}
Bug: chromium:1218005
Change-Id: I541bb9656ab2a6a080c2a30d372226fcc5c95391
Cq-Include-Trybots: luci.v8.try:v8_linux64_heap_sandbox_dbg_ng,v8_linux_arm64_sim_heap_sandbox_dbg_ng
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3219086
Auto-Submit: Deepti Gandluri <gdeepti@chromium.org>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Commit-Queue: Deepti Gandluri <gdeepti@chromium.org>
Owners-Override: Deepti Gandluri <gdeepti@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77368}
This commit is contained in:
parent
1ea76c1397
commit
1a0b993dc3
@ -494,13 +494,13 @@ constexpr bool VirtualMemoryCageIsEnabled() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef V8_VIRTUAL_MEMORY_CAGE_IS_AVAILABLE
|
#ifdef V8_VIRTUAL_MEMORY_CAGE
|
||||||
|
|
||||||
#define GB (1ULL << 30)
|
|
||||||
#define TB (1ULL << 40)
|
|
||||||
|
|
||||||
// Size of the virtual memory cage, excluding the guard regions surrounding it.
|
// Size of the virtual memory cage, excluding the guard regions surrounding it.
|
||||||
constexpr size_t kVirtualMemoryCageSize = 1ULL * TB;
|
constexpr size_t kVirtualMemoryCageSize = size_t{1} << 40; // 1 TB
|
||||||
|
|
||||||
|
static_assert(kVirtualMemoryCageSize > Internals::kPtrComprCageReservationSize,
|
||||||
|
"The virtual memory cage must be larger than the pointer "
|
||||||
|
"compression cage contained within it.");
|
||||||
|
|
||||||
// Required alignment of the virtual memory cage. For simplicity, we require the
|
// Required alignment of the virtual memory cage. For simplicity, we require the
|
||||||
// size of the guard regions to be a multiple of this, so that this specifies
|
// size of the guard regions to be a multiple of this, so that this specifies
|
||||||
@ -513,7 +513,7 @@ constexpr size_t kVirtualMemoryCageAlignment =
|
|||||||
// Size of the guard regions surrounding the virtual memory cage. This assumes a
|
// Size of the guard regions surrounding the virtual memory cage. This assumes a
|
||||||
// worst-case scenario of a 32-bit unsigned index being used to access an array
|
// worst-case scenario of a 32-bit unsigned index being used to access an array
|
||||||
// of 64-bit values.
|
// of 64-bit values.
|
||||||
constexpr size_t kVirtualMemoryCageGuardRegionSize = 32ULL * GB;
|
constexpr size_t kVirtualMemoryCageGuardRegionSize = size_t{32} << 30; // 32 GB
|
||||||
|
|
||||||
static_assert((kVirtualMemoryCageGuardRegionSize %
|
static_assert((kVirtualMemoryCageGuardRegionSize %
|
||||||
kVirtualMemoryCageAlignment) == 0,
|
kVirtualMemoryCageAlignment) == 0,
|
||||||
@ -525,31 +525,7 @@ static_assert((kVirtualMemoryCageGuardRegionSize %
|
|||||||
// until either the reservation succeeds or the minimum size is reached. A
|
// until either the reservation succeeds or the minimum size is reached. A
|
||||||
// minimum of 32GB allows the 4GB pointer compression region as well as the
|
// minimum of 32GB allows the 4GB pointer compression region as well as the
|
||||||
// ArrayBuffer partition and two 10GB WASM memory cages to fit into the cage.
|
// ArrayBuffer partition and two 10GB WASM memory cages to fit into the cage.
|
||||||
constexpr size_t kVirtualMemoryCageMinimumSize = 32ULL * GB;
|
constexpr size_t kVirtualMemoryCageMinimumSize = size_t{32} << 30; // 32 GB
|
||||||
|
|
||||||
static_assert(kVirtualMemoryCageMinimumSize <= kVirtualMemoryCageSize,
|
|
||||||
"The minimal size of the virtual memory cage must be smaller or "
|
|
||||||
"equal to the regular size.");
|
|
||||||
|
|
||||||
// On OSes where reservation virtual memory is too expensive to create a real
|
|
||||||
// cage, notably Windows pre 8.1, we create a fake cage that doesn't actually
|
|
||||||
// reserve most of the memory, and so doesn't have the desired security
|
|
||||||
// properties, but still ensures that objects that should be located inside the
|
|
||||||
// cage are allocated within kVirtualMemoryCageSize bytes from the start of the
|
|
||||||
// cage, and so appear to be inside the cage. The minimum size of the virtual
|
|
||||||
// memory range that is actually reserved for a fake cage is specified by this
|
|
||||||
// constant and should be big enough to contain the pointer compression region
|
|
||||||
// as well as the ArrayBuffer partition.
|
|
||||||
constexpr size_t kFakeVirtualMemoryCageMinReservationSize = 8ULL * GB;
|
|
||||||
|
|
||||||
static_assert(kVirtualMemoryCageMinimumSize >
|
|
||||||
Internals::kPtrComprCageReservationSize,
|
|
||||||
"The virtual memory cage must be larger than the pointer "
|
|
||||||
"compression cage contained within it.");
|
|
||||||
static_assert(kFakeVirtualMemoryCageMinReservationSize >
|
|
||||||
Internals::kPtrComprCageReservationSize,
|
|
||||||
"The reservation for a fake virtual memory cage must be larger "
|
|
||||||
"than the pointer compression cage contained within it.");
|
|
||||||
|
|
||||||
// For now, even if the virtual memory cage is enabled, we still allow backing
|
// For now, even if the virtual memory cage is enabled, we still allow backing
|
||||||
// stores to be allocated outside of it as fallback. This will simplify the
|
// stores to be allocated outside of it as fallback. This will simplify the
|
||||||
@ -561,10 +537,7 @@ constexpr bool kAllowBackingStoresOutsideCage = false;
|
|||||||
constexpr bool kAllowBackingStoresOutsideCage = true;
|
constexpr bool kAllowBackingStoresOutsideCage = true;
|
||||||
#endif // V8_HEAP_SANDBOX
|
#endif // V8_HEAP_SANDBOX
|
||||||
|
|
||||||
#undef GB
|
#endif // V8_VIRTUAL_MEMORY_CAGE
|
||||||
#undef TB
|
|
||||||
|
|
||||||
#endif // V8_VIRTUAL_MEMORY_CAGE_IS_AVAILABLE
|
|
||||||
|
|
||||||
// Only perform cast check for types derived from v8::Data since
|
// Only perform cast check for types derived from v8::Data since
|
||||||
// other types do not implement the Cast method.
|
// other types do not implement the Cast method.
|
||||||
|
@ -553,13 +553,6 @@ V8 shared library set USING_V8_SHARED.
|
|||||||
|
|
||||||
#endif // V8_OS_WIN
|
#endif // V8_OS_WIN
|
||||||
|
|
||||||
// The virtual memory cage is available (i.e. defined) when pointer compression
|
|
||||||
// is enabled, but it is only used when V8_VIRTUAL_MEMORY_CAGE is enabled as
|
|
||||||
// well. This allows better test coverage of the cage.
|
|
||||||
#if defined(V8_COMPRESS_POINTERS)
|
|
||||||
#define V8_VIRTUAL_MEMORY_CAGE_IS_AVAILABLE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
#undef V8_HAS_CPP_ATTRIBUTE
|
#undef V8_HAS_CPP_ATTRIBUTE
|
||||||
|
@ -33,25 +33,16 @@ void* BoundedPageAllocator::AllocatePages(void* hint, size_t size,
|
|||||||
DCHECK(IsAligned(alignment, region_allocator_.page_size()));
|
DCHECK(IsAligned(alignment, region_allocator_.page_size()));
|
||||||
DCHECK(IsAligned(alignment, allocate_page_size_));
|
DCHECK(IsAligned(alignment, allocate_page_size_));
|
||||||
|
|
||||||
Address address = RegionAllocator::kAllocationFailure;
|
Address address;
|
||||||
|
|
||||||
Address hint_address = reinterpret_cast<Address>(hint);
|
|
||||||
if (hint_address && IsAligned(hint_address, alignment) &&
|
|
||||||
region_allocator_.contains(hint_address, size)) {
|
|
||||||
if (region_allocator_.AllocateRegionAt(hint_address, size)) {
|
|
||||||
address = hint_address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address == RegionAllocator::kAllocationFailure) {
|
|
||||||
if (alignment <= allocate_page_size_) {
|
if (alignment <= allocate_page_size_) {
|
||||||
// TODO(ishell): Consider using randomized version here.
|
// TODO(ishell): Consider using randomized version here.
|
||||||
address = region_allocator_.AllocateRegion(size);
|
address = region_allocator_.AllocateRegion(size);
|
||||||
} else {
|
} else {
|
||||||
|
// Currently, this should only be necessary when V8_VIRTUAL_MEMORY_CAGE is
|
||||||
|
// enabled, in which case a bounded page allocator is used to allocate WASM
|
||||||
|
// memory buffers, which have a larger alignment.
|
||||||
address = region_allocator_.AllocateAlignedRegion(size, alignment);
|
address = region_allocator_.AllocateAlignedRegion(size, alignment);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (address == RegionAllocator::kAllocationFailure) {
|
if (address == RegionAllocator::kAllocationFailure) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -98,15 +98,8 @@ void IsolateAllocator::InitializeOncePerProcess() {
|
|||||||
// runs, and so this will be guaranteed. Currently however, it is possible
|
// runs, and so this will be guaranteed. Currently however, it is possible
|
||||||
// that the embedder accidentally uses the cage's page allocator prior to
|
// that the embedder accidentally uses the cage's page allocator prior to
|
||||||
// initializing V8, in which case this CHECK will likely fail.
|
// initializing V8, in which case this CHECK will likely fail.
|
||||||
// TODO(chromium:12180) here we rely on our BoundedPageAllocators to
|
CHECK(cage->page_allocator()->AllocatePagesAt(
|
||||||
// respect the hint parameter. Instead, it would probably be better to add
|
cage->base(), params.reservation_size, PageAllocator::kNoAccess));
|
||||||
// a new API that guarantees this, either directly to the PageAllocator
|
|
||||||
// interface or to a derived one.
|
|
||||||
void* hint = reinterpret_cast<void*>(cage->base());
|
|
||||||
void* base = cage->page_allocator()->AllocatePages(
|
|
||||||
hint, params.reservation_size, params.base_alignment,
|
|
||||||
PageAllocator::kNoAccess);
|
|
||||||
CHECK_EQ(base, hint);
|
|
||||||
existing_reservation =
|
existing_reservation =
|
||||||
base::AddressRegion(cage->base(), params.reservation_size);
|
base::AddressRegion(cage->base(), params.reservation_size);
|
||||||
params.page_allocator = cage->page_allocator();
|
params.page_allocator = cage->page_allocator();
|
||||||
|
@ -8,8 +8,6 @@
|
|||||||
#include "src/base/bits.h"
|
#include "src/base/bits.h"
|
||||||
#include "src/base/bounded-page-allocator.h"
|
#include "src/base/bounded-page-allocator.h"
|
||||||
#include "src/base/lazy-instance.h"
|
#include "src/base/lazy-instance.h"
|
||||||
#include "src/base/utils/random-number-generator.h"
|
|
||||||
#include "src/flags/flags.h"
|
|
||||||
#include "src/utils/allocation.h"
|
#include "src/utils/allocation.h"
|
||||||
|
|
||||||
#if defined(V8_OS_WIN)
|
#if defined(V8_OS_WIN)
|
||||||
@ -21,199 +19,22 @@
|
|||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
#ifdef V8_COMPRESS_POINTERS_IN_SHARED_CAGE
|
#ifdef V8_VIRTUAL_MEMORY_CAGE
|
||||||
|
|
||||||
// A PageAllocator that allocates pages inside a given virtual address range
|
|
||||||
// like the BoundedPageAllocator, except that only a (small) part of the range
|
|
||||||
// has actually been reserved. As such, this allocator relies on page
|
|
||||||
// allocation hints for the OS to obtain pages inside the non-reserved part.
|
|
||||||
// This allocator is used on OSes where reserving virtual address space (and
|
|
||||||
// thus a virtual memory cage) is too expensive, notabley Windows pre 8.1.
|
|
||||||
class FakeBoundedPageAllocator : public v8::PageAllocator {
|
|
||||||
public:
|
|
||||||
FakeBoundedPageAllocator(v8::PageAllocator* page_allocator, Address start,
|
|
||||||
size_t size, size_t reserved_size)
|
|
||||||
: page_allocator_(page_allocator),
|
|
||||||
start_(start),
|
|
||||||
size_(size),
|
|
||||||
reserved_size_(reserved_size),
|
|
||||||
end_of_reserved_region_(start + reserved_size) {
|
|
||||||
// The size is required to be a power of two so that obtaining a random
|
|
||||||
// address inside the managed region simply requires a fixed number of
|
|
||||||
// random bits as offset.
|
|
||||||
DCHECK(base::bits::IsPowerOfTwo(size));
|
|
||||||
DCHECK_LT(reserved_size, size);
|
|
||||||
|
|
||||||
if (FLAG_random_seed != 0) {
|
|
||||||
rng_.SetSeed(FLAG_random_seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
reserved_region_page_allocator_ =
|
|
||||||
std::make_unique<base::BoundedPageAllocator>(
|
|
||||||
page_allocator_, start_, reserved_size_,
|
|
||||||
page_allocator_->AllocatePageSize(),
|
|
||||||
base::PageInitializationMode::kAllocatedPagesMustBeZeroInitialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
~FakeBoundedPageAllocator() override = default;
|
|
||||||
|
|
||||||
size_t AllocatePageSize() override {
|
|
||||||
return page_allocator_->AllocatePageSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t CommitPageSize() override { return page_allocator_->CommitPageSize(); }
|
|
||||||
|
|
||||||
void SetRandomMmapSeed(int64_t seed) override { rng_.SetSeed(seed); }
|
|
||||||
|
|
||||||
void* GetRandomMmapAddr() override {
|
|
||||||
// Generate a random number between 0 and size_, then add that to the start
|
|
||||||
// address to obtain a random mmap address. We deliberately don't use our
|
|
||||||
// provided page allocator's GetRandomMmapAddr here since that could be
|
|
||||||
// biased, while we want uniformly distributed random numbers here.
|
|
||||||
Address addr = rng_.NextInt64() % size_ + start_;
|
|
||||||
addr = RoundDown(addr, AllocatePageSize());
|
|
||||||
void* ptr = reinterpret_cast<void*>(addr);
|
|
||||||
DCHECK(Contains(ptr, 1));
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* AllocatePages(void* hint, size_t size, size_t alignment,
|
|
||||||
Permission access) override {
|
|
||||||
DCHECK(IsAligned(size, AllocatePageSize()));
|
|
||||||
DCHECK(IsAligned(alignment, AllocatePageSize()));
|
|
||||||
|
|
||||||
// First, try allocating the memory inside the reserved region.
|
|
||||||
void* ptr = reserved_region_page_allocator_->AllocatePages(
|
|
||||||
hint, size, alignment, access);
|
|
||||||
if (ptr) return ptr;
|
|
||||||
|
|
||||||
// Then, fall back to allocating memory outside of the reserved region
|
|
||||||
// through page allocator hints.
|
|
||||||
|
|
||||||
// Somewhat arbitrary size limitation to ensure that the loop below for
|
|
||||||
// finding a fitting base address hint terminates quickly.
|
|
||||||
if (size >= size_ / 2) return nullptr;
|
|
||||||
|
|
||||||
if (!hint || !Contains(hint, size)) hint = GetRandomMmapAddr();
|
|
||||||
|
|
||||||
static constexpr int kMaxAttempts = 10;
|
|
||||||
for (int i = 0; i < kMaxAttempts; i++) {
|
|
||||||
// If the hint wouldn't result in the entire allocation being inside the
|
|
||||||
// managed region, simply retry. There is at least a 50% chance of
|
|
||||||
// getting a usable address due to the size restriction above.
|
|
||||||
while (!Contains(hint, size)) {
|
|
||||||
hint = GetRandomMmapAddr();
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr = page_allocator_->AllocatePages(hint, size, alignment, access);
|
|
||||||
if (ptr && Contains(ptr, size)) {
|
|
||||||
return ptr;
|
|
||||||
} else if (ptr) {
|
|
||||||
page_allocator_->FreePages(ptr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry at a different address.
|
|
||||||
hint = GetRandomMmapAddr();
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FreePages(void* address, size_t size) override {
|
|
||||||
return AllocatorFor(address)->FreePages(address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReleasePages(void* address, size_t size, size_t new_length) override {
|
|
||||||
return AllocatorFor(address)->ReleasePages(address, size, new_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SetPermissions(void* address, size_t size,
|
|
||||||
Permission permissions) override {
|
|
||||||
return AllocatorFor(address)->SetPermissions(address, size, permissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DiscardSystemPages(void* address, size_t size) override {
|
|
||||||
return AllocatorFor(address)->DiscardSystemPages(address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DecommitPages(void* address, size_t size) override {
|
|
||||||
return AllocatorFor(address)->DecommitPages(address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool Contains(void* ptr, size_t length) {
|
|
||||||
Address addr = reinterpret_cast<Address>(ptr);
|
|
||||||
return (addr >= start_) && ((addr + length) < (start_ + size_));
|
|
||||||
}
|
|
||||||
|
|
||||||
v8::PageAllocator* AllocatorFor(void* ptr) {
|
|
||||||
Address addr = reinterpret_cast<Address>(ptr);
|
|
||||||
if (addr < end_of_reserved_region_) {
|
|
||||||
DCHECK_GE(addr, start_);
|
|
||||||
return reserved_region_page_allocator_.get();
|
|
||||||
} else {
|
|
||||||
return page_allocator_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The page allocator through which pages inside the region are allocated.
|
|
||||||
v8::PageAllocator* const page_allocator_;
|
|
||||||
// The bounded page allocator managing the sub-region that was actually
|
|
||||||
// reserved.
|
|
||||||
std::unique_ptr<base::BoundedPageAllocator> reserved_region_page_allocator_;
|
|
||||||
|
|
||||||
// Random number generator for generating random addresses.
|
|
||||||
base::RandomNumberGenerator rng_;
|
|
||||||
|
|
||||||
// The start of the virtual memory region in which to allocate pages. This is
|
|
||||||
// also the start of the sub-region that was reserved.
|
|
||||||
const Address start_;
|
|
||||||
// The total size of the address space in which to allocate pages.
|
|
||||||
const size_t size_;
|
|
||||||
// The size of the sub-region that has actually been reserved.
|
|
||||||
const size_t reserved_size_;
|
|
||||||
// The end of the sub-region that has actually been reserved.
|
|
||||||
const Address end_of_reserved_region_;
|
|
||||||
};
|
|
||||||
|
|
||||||
static uintptr_t DetermineAddressSpaceLimit() {
|
|
||||||
// TODO(saelo) should this also take things like rlimits into account?
|
|
||||||
#ifdef V8_TARGET_ARCH_64_BIT
|
|
||||||
// TODO(saelo) this should be deteremined based on the CPU model being used
|
|
||||||
// and its number of virtual address bits.
|
|
||||||
uintptr_t virtual_address_bits = 48;
|
|
||||||
// Virtual address space is split 50/50 between userspace and kernel
|
|
||||||
uintptr_t userspace_virtual_address_bits = virtual_address_bits / 2;
|
|
||||||
uintptr_t address_space_limit = 1UL << userspace_virtual_address_bits;
|
|
||||||
return address_space_limit;
|
|
||||||
#else
|
|
||||||
#error Unsupported target architecture.
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool V8VirtualMemoryCage::Initialize(PageAllocator* page_allocator) {
|
bool V8VirtualMemoryCage::Initialize(PageAllocator* page_allocator) {
|
||||||
// TODO(saelo) We need to take the number of virtual address bits of the CPU
|
bool use_guard_regions = true;
|
||||||
// into account when deteriming the size of the cage. For example, if there
|
size_t size = kVirtualMemoryCageSize;
|
||||||
// are only 39 bits available (some older Intel CPUs), split evenly between
|
|
||||||
// userspace and kernel, then userspace can only address 256GB and so the
|
|
||||||
// maximum cage size should probably be something around 64GB to 128GB.
|
|
||||||
const size_t size = kVirtualMemoryCageSize;
|
|
||||||
#if defined(V8_OS_WIN)
|
#if defined(V8_OS_WIN)
|
||||||
if (!IsWindows8Point1OrGreater()) {
|
if (!IsWindows8Point1OrGreater()) {
|
||||||
// On Windows pre 8.1, reserving virtual memory is an expensive operation,
|
// On Windows pre 8.1, reserving virtual memory is an expensive operation,
|
||||||
// apparently because the OS already charges for the memory required for
|
// possibly because page table entries are created for the address range.
|
||||||
// all page table entries. For example, a 1TB reservation increases private
|
// For example, a 1TB reservation increases private memory usage by 2GB. As
|
||||||
// memory usage by 2GB. As such, it is not possible to create a proper
|
// such, we can unfortunately only create a minimal cage on these version,
|
||||||
// virtual memory cage there and so a fake cage is created which doesn't
|
// without guard regions and without our desired security properties.
|
||||||
// reserve most of the virtual memory, and so doesn't incur the cost, but
|
use_guard_regions = false;
|
||||||
// also doesn't provide the desired security benefits.
|
size = kVirtualMemoryCageMinimumSize;
|
||||||
const size_t size_to_reserve = kFakeVirtualMemoryCageMinReservationSize;
|
|
||||||
return InitializeAsFakeCage(page_allocator, size, size_to_reserve);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// TODO(saelo) if this fails, we could still fall back to creating a fake
|
|
||||||
// cage.
|
|
||||||
const bool use_guard_regions = true;
|
|
||||||
return Initialize(page_allocator, size, use_guard_regions);
|
return Initialize(page_allocator, size, use_guard_regions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,109 +54,34 @@ bool V8VirtualMemoryCage::Initialize(v8::PageAllocator* page_allocator,
|
|||||||
// doesn't reduce the cage's security properties if it has a smaller size.
|
// doesn't reduce the cage's security properties if it has a smaller size.
|
||||||
// Which of these options is ultimately taken likey depends on how frequently
|
// Which of these options is ultimately taken likey depends on how frequently
|
||||||
// cage reservation failures occur in practice.
|
// cage reservation failures occur in practice.
|
||||||
size_t reservation_size;
|
while (!base_ && size >= kVirtualMemoryCageMinimumSize) {
|
||||||
while (!reservation_base_ && size >= kVirtualMemoryCageMinimumSize) {
|
size_t reservation_size = size;
|
||||||
reservation_size = size;
|
|
||||||
if (use_guard_regions) {
|
if (use_guard_regions) {
|
||||||
reservation_size += 2 * kVirtualMemoryCageGuardRegionSize;
|
reservation_size += 2 * kVirtualMemoryCageGuardRegionSize;
|
||||||
}
|
}
|
||||||
|
base_ = reinterpret_cast<Address>(page_allocator->AllocatePages(
|
||||||
// Technically, we should use kNoAccessWillJitLater here instead since the
|
nullptr, reservation_size, kVirtualMemoryCageAlignment,
|
||||||
// cage will contain JIT pages. However, currently this is not required as
|
|
||||||
// PA anyway uses MAP_JIT for V8 mappings. Further, we want to eventually
|
|
||||||
// move JIT pages out of the cage, at which point we'd like to forbid
|
|
||||||
// making pages inside the cage executable, and so don't want MAP_JIT.
|
|
||||||
void* hint = page_allocator->GetRandomMmapAddr();
|
|
||||||
reservation_base_ = reinterpret_cast<Address>(page_allocator->AllocatePages(
|
|
||||||
hint, reservation_size, kVirtualMemoryCageAlignment,
|
|
||||||
PageAllocator::kNoAccess));
|
PageAllocator::kNoAccess));
|
||||||
if (!reservation_base_) {
|
if (!base_) {
|
||||||
size /= 2;
|
size /= 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reservation_base_) return false;
|
if (!base_) return false;
|
||||||
|
|
||||||
base_ = reservation_base_;
|
|
||||||
if (use_guard_regions) {
|
if (use_guard_regions) {
|
||||||
base_ += kVirtualMemoryCageGuardRegionSize;
|
base_ += kVirtualMemoryCageGuardRegionSize;
|
||||||
|
has_guard_regions_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
page_allocator_ = page_allocator;
|
page_allocator_ = page_allocator;
|
||||||
size_ = size;
|
size_ = size;
|
||||||
reservation_size_ = reservation_size;
|
|
||||||
|
|
||||||
cage_page_allocator_ = std::make_unique<base::BoundedPageAllocator>(
|
cage_page_allocator_ = std::make_unique<base::BoundedPageAllocator>(
|
||||||
page_allocator_, base_, size_, page_allocator_->AllocatePageSize(),
|
page_allocator_, base_, size_, page_allocator_->AllocatePageSize(),
|
||||||
base::PageInitializationMode::kAllocatedPagesMustBeZeroInitialized);
|
base::PageInitializationMode::kAllocatedPagesMustBeZeroInitialized);
|
||||||
|
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
is_fake_cage_ = false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool V8VirtualMemoryCage::InitializeAsFakeCage(
|
|
||||||
v8::PageAllocator* page_allocator, size_t size, size_t size_to_reserve) {
|
|
||||||
CHECK(!initialized_);
|
|
||||||
CHECK(!disabled_);
|
|
||||||
CHECK(base::bits::IsPowerOfTwo(size));
|
|
||||||
CHECK(base::bits::IsPowerOfTwo(size_to_reserve));
|
|
||||||
CHECK_GE(size, kVirtualMemoryCageMinimumSize);
|
|
||||||
CHECK_LT(size_to_reserve, size);
|
|
||||||
|
|
||||||
// Use a custom random number generator here to ensure that we get uniformly
|
|
||||||
// distributed random numbers. We figure out the available address space
|
|
||||||
// ourselves, and so are potentially better positioned to determine a good
|
|
||||||
// base address for the cage than the embedder-provided GetRandomMmapAddr().
|
|
||||||
base::RandomNumberGenerator rng;
|
|
||||||
if (FLAG_random_seed != 0) {
|
|
||||||
rng.SetSeed(FLAG_random_seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We try to ensure that base + size is still fully within the process'
|
|
||||||
// address space, even though we only reserve a fraction of the memory.
|
|
||||||
Address address_space_end = DetermineAddressSpaceLimit();
|
|
||||||
DCHECK(base::bits::IsPowerOfTwo(address_space_end));
|
|
||||||
Address highest_possible_address = address_space_end - size;
|
|
||||||
constexpr int kMaxAttempts = 10;
|
|
||||||
for (int i = 1; i <= kMaxAttempts; i++) {
|
|
||||||
// The size of the cage is small relative to the size of the usable address
|
|
||||||
// space, so we can just retry until we get a usable hint.
|
|
||||||
Address hint;
|
|
||||||
do {
|
|
||||||
hint = rng.NextInt64() % address_space_end;
|
|
||||||
} while (hint > highest_possible_address);
|
|
||||||
|
|
||||||
// Align to page size.
|
|
||||||
hint = RoundDown(hint, page_allocator->AllocatePageSize());
|
|
||||||
|
|
||||||
reservation_base_ = reinterpret_cast<Address>(page_allocator->AllocatePages(
|
|
||||||
reinterpret_cast<void*>(hint), size_to_reserve,
|
|
||||||
kVirtualMemoryCageAlignment, PageAllocator::kNoAccess));
|
|
||||||
|
|
||||||
if (!reservation_base_) return false;
|
|
||||||
|
|
||||||
// Take this base if it meets the requirements or if this is the last
|
|
||||||
// attempt.
|
|
||||||
if (reservation_base_ <= highest_possible_address || i == kMaxAttempts)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Can't use this base, so free the reservation and try again
|
|
||||||
page_allocator_->FreePages(reinterpret_cast<void*>(reservation_base_),
|
|
||||||
size_to_reserve);
|
|
||||||
reservation_base_ = kNullAddress;
|
|
||||||
}
|
|
||||||
DCHECK(reservation_base_);
|
|
||||||
|
|
||||||
base_ = reservation_base_;
|
|
||||||
size_ = size;
|
|
||||||
reservation_size_ = size_to_reserve;
|
|
||||||
initialized_ = true;
|
|
||||||
is_fake_cage_ = true;
|
|
||||||
page_allocator_ = page_allocator;
|
|
||||||
cage_page_allocator_ = std::make_unique<FakeBoundedPageAllocator>(
|
|
||||||
page_allocator_, base_, size_, reservation_size_);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -343,24 +89,26 @@ bool V8VirtualMemoryCage::InitializeAsFakeCage(
|
|||||||
void V8VirtualMemoryCage::TearDown() {
|
void V8VirtualMemoryCage::TearDown() {
|
||||||
if (initialized_) {
|
if (initialized_) {
|
||||||
cage_page_allocator_.reset();
|
cage_page_allocator_.reset();
|
||||||
CHECK(page_allocator_->FreePages(reinterpret_cast<void*>(reservation_base_),
|
Address reservation_base = base_;
|
||||||
reservation_size_));
|
size_t reservation_size = size_;
|
||||||
|
if (has_guard_regions_) {
|
||||||
|
reservation_base -= kVirtualMemoryCageGuardRegionSize;
|
||||||
|
reservation_size += 2 * kVirtualMemoryCageGuardRegionSize;
|
||||||
|
}
|
||||||
|
CHECK(page_allocator_->FreePages(reinterpret_cast<void*>(reservation_base),
|
||||||
|
reservation_size));
|
||||||
|
page_allocator_ = nullptr;
|
||||||
base_ = kNullAddress;
|
base_ = kNullAddress;
|
||||||
size_ = 0;
|
size_ = 0;
|
||||||
reservation_base_ = kNullAddress;
|
|
||||||
reservation_size_ = 0;
|
|
||||||
initialized_ = false;
|
initialized_ = false;
|
||||||
is_fake_cage_ = false;
|
has_guard_regions_ = false;
|
||||||
page_allocator_ = nullptr;
|
|
||||||
}
|
}
|
||||||
disabled_ = false;
|
disabled_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // V8_VIRTUAL_MEMORY_CAGE_IS_AVAILABLE
|
|
||||||
|
|
||||||
#ifdef V8_VIRTUAL_MEMORY_CAGE
|
|
||||||
DEFINE_LAZY_LEAKY_OBJECT_GETTER(V8VirtualMemoryCage,
|
DEFINE_LAZY_LEAKY_OBJECT_GETTER(V8VirtualMemoryCage,
|
||||||
GetProcessWideVirtualMemoryCage)
|
GetProcessWideVirtualMemoryCage)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -15,7 +15,7 @@ class PageAllocator;
|
|||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
#ifdef V8_VIRTUAL_MEMORY_CAGE_IS_AVAILABLE
|
#ifdef V8_VIRTUAL_MEMORY_CAGE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* V8 Virtual Memory Cage.
|
* V8 Virtual Memory Cage.
|
||||||
@ -70,12 +70,11 @@ class V8_EXPORT_PRIVATE V8VirtualMemoryCage {
|
|||||||
bool is_initialized() const { return initialized_; }
|
bool is_initialized() const { return initialized_; }
|
||||||
bool is_disabled() const { return disabled_; }
|
bool is_disabled() const { return disabled_; }
|
||||||
bool is_enabled() const { return !disabled_; }
|
bool is_enabled() const { return !disabled_; }
|
||||||
bool is_fake_cage() const { return is_fake_cage_; }
|
|
||||||
|
|
||||||
Address base() const { return base_; }
|
Address base() const { return base_; }
|
||||||
size_t size() const { return size_; }
|
size_t size() const { return size_; }
|
||||||
|
|
||||||
v8::PageAllocator* page_allocator() const {
|
base::BoundedPageAllocator* page_allocator() const {
|
||||||
return cage_page_allocator_.get();
|
return cage_page_allocator_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,48 +91,27 @@ class V8_EXPORT_PRIVATE V8VirtualMemoryCage {
|
|||||||
// cage without guard regions, which would otherwise consume too much memory.
|
// cage without guard regions, which would otherwise consume too much memory.
|
||||||
friend class SequentialUnmapperTest;
|
friend class SequentialUnmapperTest;
|
||||||
|
|
||||||
// These tests call the private Initialize methods below.
|
|
||||||
FRIEND_TEST(VirtualMemoryCageTest, InitializationWithSize);
|
|
||||||
FRIEND_TEST(VirtualMemoryCageTest, InitializationAsFakeCage);
|
|
||||||
FRIEND_TEST(VirtualMemoryCageTest, FakeCagePageAllocation);
|
|
||||||
|
|
||||||
// We allow tests to disable the guard regions around the cage. This is useful
|
// We allow tests to disable the guard regions around the cage. This is useful
|
||||||
// for example for tests like the SequentialUnmapperTest which track page
|
// for example for tests like the SequentialUnmapperTest which track page
|
||||||
// allocations and so would incur a large overhead from the guard regions.
|
// allocations and so would incur a large overhead from the guard regions.
|
||||||
bool Initialize(v8::PageAllocator* page_allocator, size_t size,
|
bool Initialize(v8::PageAllocator* page_allocator, size_t total_size,
|
||||||
bool use_guard_regions);
|
bool use_guard_regions);
|
||||||
|
|
||||||
// Used on OSes where reserving virtual memory is too expensive. A fake cage
|
|
||||||
// does not reserve all of the virtual memory and so doesn't have the desired
|
|
||||||
// security properties.
|
|
||||||
bool InitializeAsFakeCage(v8::PageAllocator* page_allocator, size_t size,
|
|
||||||
size_t size_to_reserve);
|
|
||||||
|
|
||||||
Address base_ = kNullAddress;
|
Address base_ = kNullAddress;
|
||||||
size_t size_ = 0;
|
size_t size_ = 0;
|
||||||
|
bool has_guard_regions_ = false;
|
||||||
// Base and size of the virtual memory reservation backing this cage. These
|
|
||||||
// can be different from the cage base and size due to guard regions or when a
|
|
||||||
// fake cage is used.
|
|
||||||
Address reservation_base_ = kNullAddress;
|
|
||||||
size_t reservation_size_ = 0;
|
|
||||||
|
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
bool disabled_ = false;
|
bool disabled_ = false;
|
||||||
bool is_fake_cage_ = false;
|
// The PageAllocator through which the virtual memory of the cage was
|
||||||
|
// allocated.
|
||||||
// The allocator through which the virtual memory of the cage was allocated.
|
|
||||||
v8::PageAllocator* page_allocator_ = nullptr;
|
v8::PageAllocator* page_allocator_ = nullptr;
|
||||||
// The allocator to allocate pages inside the cage.
|
// The BoundedPageAllocator to allocate pages inside the cage.
|
||||||
std::unique_ptr<v8::PageAllocator> cage_page_allocator_;
|
std::unique_ptr<base::BoundedPageAllocator> cage_page_allocator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // V8_VIRTUAL_MEMORY_CAGE_IS_AVAILABLE
|
|
||||||
|
|
||||||
#ifdef V8_VIRTUAL_MEMORY_CAGE
|
|
||||||
// This function is only available when the cage is actually used.
|
|
||||||
V8_EXPORT_PRIVATE V8VirtualMemoryCage* GetProcessWideVirtualMemoryCage();
|
V8_EXPORT_PRIVATE V8VirtualMemoryCage* GetProcessWideVirtualMemoryCage();
|
||||||
#endif
|
|
||||||
|
#endif // V8_VIRTUAL_MEMORY_CAGE
|
||||||
|
|
||||||
V8_INLINE bool IsValidBackingStorePointer(void* ptr) {
|
V8_INLINE bool IsValidBackingStorePointer(void* ptr) {
|
||||||
#ifdef V8_VIRTUAL_MEMORY_CAGE
|
#ifdef V8_VIRTUAL_MEMORY_CAGE
|
||||||
|
@ -291,6 +291,7 @@ v8_source_set("cctest_sources") {
|
|||||||
"test-utils.cc",
|
"test-utils.cc",
|
||||||
"test-verifiers.cc",
|
"test-verifiers.cc",
|
||||||
"test-version.cc",
|
"test-version.cc",
|
||||||
|
"test-virtual-memory-cage.cc",
|
||||||
"test-weakmaps.cc",
|
"test-weakmaps.cc",
|
||||||
"test-weaksets.cc",
|
"test-weaksets.cc",
|
||||||
"test-web-snapshots.cc",
|
"test-web-snapshots.cc",
|
||||||
|
36
test/cctest/test-virtual-memory-cage.cc
Normal file
36
test/cctest/test-virtual-memory-cage.cc
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2021 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/init/vm-cage.h"
|
||||||
|
#include "test/cctest/cctest.h"
|
||||||
|
|
||||||
|
#ifdef V8_VIRTUAL_MEMORY_CAGE
|
||||||
|
|
||||||
|
namespace v8 {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
UNINITIALIZED_TEST(VirtualMemoryCageCreation) {
|
||||||
|
base::PageAllocator page_allocator;
|
||||||
|
|
||||||
|
V8VirtualMemoryCage cage;
|
||||||
|
|
||||||
|
CHECK(!cage.is_initialized());
|
||||||
|
CHECK(!cage.is_disabled());
|
||||||
|
CHECK_EQ(cage.size(), 0);
|
||||||
|
|
||||||
|
CHECK(cage.Initialize(&page_allocator));
|
||||||
|
|
||||||
|
CHECK(cage.is_initialized());
|
||||||
|
CHECK_GT(cage.base(), 0);
|
||||||
|
CHECK_GT(cage.size(), 0);
|
||||||
|
|
||||||
|
cage.TearDown();
|
||||||
|
|
||||||
|
CHECK(!cage.is_initialized());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace v8
|
||||||
|
|
||||||
|
#endif // V8_VIRTUAL_MEMORY_CAGE
|
@ -376,7 +376,6 @@ v8_source_set("unittests_sources") {
|
|||||||
"regress/regress-crbug-938251-unittest.cc",
|
"regress/regress-crbug-938251-unittest.cc",
|
||||||
"run-all-unittests.cc",
|
"run-all-unittests.cc",
|
||||||
"runtime/runtime-debug-unittest.cc",
|
"runtime/runtime-debug-unittest.cc",
|
||||||
"security/virtual-memory-cage-unittest.cc",
|
|
||||||
"strings/char-predicates-unittest.cc",
|
"strings/char-predicates-unittest.cc",
|
||||||
"strings/unicode-unittest.cc",
|
"strings/unicode-unittest.cc",
|
||||||
"tasks/background-compile-task-unittest.cc",
|
"tasks/background-compile-task-unittest.cc",
|
||||||
|
@ -1,148 +0,0 @@
|
|||||||
// Copyright 2021 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 <vector>
|
|
||||||
|
|
||||||
#include "src/init/vm-cage.h"
|
|
||||||
#include "test/unittests/test-utils.h"
|
|
||||||
|
|
||||||
#ifdef V8_VIRTUAL_MEMORY_CAGE_IS_AVAILABLE
|
|
||||||
|
|
||||||
namespace v8 {
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
TEST(VirtualMemoryCageTest, Initialization) {
|
|
||||||
base::PageAllocator page_allocator;
|
|
||||||
|
|
||||||
V8VirtualMemoryCage cage;
|
|
||||||
|
|
||||||
EXPECT_FALSE(cage.is_initialized());
|
|
||||||
EXPECT_FALSE(cage.is_disabled());
|
|
||||||
EXPECT_FALSE(cage.is_fake_cage());
|
|
||||||
EXPECT_EQ(cage.size(), 0UL);
|
|
||||||
|
|
||||||
EXPECT_TRUE(cage.Initialize(&page_allocator));
|
|
||||||
|
|
||||||
EXPECT_TRUE(cage.is_initialized());
|
|
||||||
EXPECT_NE(cage.base(), 0UL);
|
|
||||||
EXPECT_GT(cage.size(), 0UL);
|
|
||||||
|
|
||||||
cage.TearDown();
|
|
||||||
|
|
||||||
EXPECT_FALSE(cage.is_initialized());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(VirtualMemoryCageTest, InitializationWithSize) {
|
|
||||||
base::PageAllocator page_allocator;
|
|
||||||
V8VirtualMemoryCage cage;
|
|
||||||
size_t size = kVirtualMemoryCageMinimumSize;
|
|
||||||
const bool use_guard_regions = false;
|
|
||||||
EXPECT_TRUE(cage.Initialize(&page_allocator, size, use_guard_regions));
|
|
||||||
|
|
||||||
EXPECT_TRUE(cage.is_initialized());
|
|
||||||
EXPECT_FALSE(cage.is_fake_cage());
|
|
||||||
EXPECT_EQ(cage.size(), size);
|
|
||||||
|
|
||||||
cage.TearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(VirtualMemoryCageTest, InitializationAsFakeCage) {
|
|
||||||
base::PageAllocator page_allocator;
|
|
||||||
V8VirtualMemoryCage cage;
|
|
||||||
// Total size of the fake cage.
|
|
||||||
size_t size = kVirtualMemoryCageSize;
|
|
||||||
// Size of the virtual memory that is actually reserved at the start of the
|
|
||||||
// cage.
|
|
||||||
size_t reserved_size = 2 * page_allocator.AllocatePageSize();
|
|
||||||
EXPECT_TRUE(cage.InitializeAsFakeCage(&page_allocator, size, reserved_size));
|
|
||||||
|
|
||||||
EXPECT_TRUE(cage.is_initialized());
|
|
||||||
EXPECT_TRUE(cage.is_fake_cage());
|
|
||||||
EXPECT_NE(cage.base(), 0UL);
|
|
||||||
EXPECT_EQ(cage.size(), size);
|
|
||||||
|
|
||||||
cage.TearDown();
|
|
||||||
|
|
||||||
EXPECT_FALSE(cage.is_initialized());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(VirtualMemloryCageTest, Contains) {
|
|
||||||
base::PageAllocator page_allocator;
|
|
||||||
V8VirtualMemoryCage cage;
|
|
||||||
EXPECT_TRUE(cage.Initialize(&page_allocator));
|
|
||||||
|
|
||||||
Address base = cage.base();
|
|
||||||
size_t size = cage.size();
|
|
||||||
base::RandomNumberGenerator rng(::testing::FLAGS_gtest_random_seed);
|
|
||||||
|
|
||||||
EXPECT_TRUE(cage.Contains(base));
|
|
||||||
EXPECT_TRUE(cage.Contains(base + size - 1));
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
size_t offset = rng.NextInt64() % size;
|
|
||||||
EXPECT_TRUE(cage.Contains(base + offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_FALSE(cage.Contains(base - 1));
|
|
||||||
EXPECT_FALSE(cage.Contains(base + size));
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
Address addr = rng.NextInt64();
|
|
||||||
if (addr < base || addr >= base + size) {
|
|
||||||
EXPECT_FALSE(cage.Contains(addr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cage.TearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestCagePageAllocation(V8VirtualMemoryCage& cage) {
|
|
||||||
const size_t kAllocatinSizesInPages[] = {1, 1, 2, 3, 5, 8, 13, 21, 34};
|
|
||||||
constexpr int kNumAllocations = arraysize(kAllocatinSizesInPages);
|
|
||||||
|
|
||||||
PageAllocator* allocator = cage.page_allocator();
|
|
||||||
size_t page_size = allocator->AllocatePageSize();
|
|
||||||
std::vector<void*> allocations;
|
|
||||||
for (int i = 0; i < kNumAllocations; i++) {
|
|
||||||
size_t length = page_size * kAllocatinSizesInPages[i];
|
|
||||||
size_t alignment = page_size;
|
|
||||||
void* ptr = allocator->AllocatePages(nullptr, length, alignment,
|
|
||||||
PageAllocator::kNoAccess);
|
|
||||||
EXPECT_NE(ptr, nullptr);
|
|
||||||
EXPECT_TRUE(cage.Contains(ptr));
|
|
||||||
allocations.push_back(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < kNumAllocations; i++) {
|
|
||||||
size_t length = page_size * kAllocatinSizesInPages[i];
|
|
||||||
allocator->FreePages(allocations[i], length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(VirtualMemoryCageTest, PageAllocation) {
|
|
||||||
base::PageAllocator page_allocator;
|
|
||||||
V8VirtualMemoryCage cage;
|
|
||||||
EXPECT_TRUE(cage.Initialize(&page_allocator));
|
|
||||||
|
|
||||||
TestCagePageAllocation(cage);
|
|
||||||
|
|
||||||
cage.TearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(VirtualMemoryCageTest, FakeCagePageAllocation) {
|
|
||||||
base::PageAllocator page_allocator;
|
|
||||||
V8VirtualMemoryCage cage;
|
|
||||||
size_t size = kVirtualMemoryCageSize;
|
|
||||||
// Only reserve two pages so the test will allocate memory inside and outside
|
|
||||||
// of the reserved region.
|
|
||||||
size_t reserved_size = 2 * page_allocator.AllocatePageSize();
|
|
||||||
EXPECT_TRUE(cage.InitializeAsFakeCage(&page_allocator, size, reserved_size));
|
|
||||||
|
|
||||||
TestCagePageAllocation(cage);
|
|
||||||
|
|
||||||
cage.TearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace v8
|
|
||||||
|
|
||||||
#endif // V8_VIRTUAL_MEMORY_CAGE_IS_AVAILABLE
|
|
Loading…
Reference in New Issue
Block a user