Commit 406d65d3 authored by Samuel Groß's avatar Samuel Groß Committed by V8 LUCI CQ

[base] Add VirtualAddressSpace::AllocateGuardRegion

Previously, guard regions were created by allocating pages with
PROT_NONE and relying on an allocation hint. This could fail however,
for example on Fuchsia (where it would allocate a VMO to back the guard
region) and possibly on Windows (where a placeholder mapping was
replaced by a "real" mapping).

Introducing an explicit VirtualAddressSpace::AllocateGuardRegion routine
now makes this operation more efficient and effectively guarantees that
it cannot fail if used correctly: in a regular subspace, there is no
need to allocate anything when creating guard regions since the address
space reservation backing the subspace is guaranteed to be inaccessible
when no pages are allocated in it.

Bug: chromium:1218005
Change-Id: I6945f17616b6b8dad47241af96d4cb1f660e8858
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/+/3366237Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Commit-Queue: Samuel Groß <saelo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78480}
parent 8f8d2fe4
......@@ -598,6 +598,7 @@ class VirtualAddressSpace {
* given address first. If that fails, the allocation is attempted to be
* placed elsewhere, possibly nearby, but that is not guaranteed. Specifying
* zero for the hint always causes this function to choose a random address.
* The hint, if specified, must be aligned to the specified alignment.
*
* \param size The size of the allocation in bytes. Must be a multiple of the
* allocation_granularity().
......@@ -645,6 +646,40 @@ class VirtualAddressSpace {
virtual V8_WARN_UNUSED_RESULT bool SetPagePermissions(
Address address, size_t size, PagePermissions permissions) = 0;
/**
* Creates a guard region at the specified address.
*
* Guard regions are guaranteed to cause a fault when accessed and generally
* do not count towards any memory consumption limits. Further, allocating
* guard regions can usually not fail in subspaces if the region does not
* overlap with another region, subspace, or page allocation.
*
* \param address The start address of the guard region. Must be aligned to
* the allocation_granularity().
*
* \param size The size of the guard region in bytes. Must be a multiple of
* the allocation_granularity().
*
* \returns true on success, false otherwise.
*/
virtual V8_WARN_UNUSED_RESULT bool AllocateGuardRegion(Address address,
size_t size) = 0;
/**
* Frees an existing guard region.
*
* \param address The start address of the guard region to free. This address
* must have previously been used as address parameter in a successful
* invocation of AllocateGuardRegion.
*
* \param size The size in bytes of the guard region to free. This must match
* the size passed to AllocateGuardRegion when the region was created.
*
* \returns true on success, false otherwise.
*/
virtual V8_WARN_UNUSED_RESULT bool FreeGuardRegion(Address address,
size_t size) = 0;
/**
* Whether this instance can allocate subspaces or not.
*
......
......@@ -110,6 +110,26 @@ bool EmulatedVirtualAddressSubspace::SetPagePermissions(
return parent_space_->SetPagePermissions(address, size, permissions);
}
bool EmulatedVirtualAddressSubspace::AllocateGuardRegion(Address address,
size_t size) {
if (MappedRegionContains(address, size)) {
MutexGuard guard(&mutex_);
return region_allocator_.AllocateRegionAt(address, size);
}
if (!UnmappedRegionContains(address, size)) return false;
return parent_space_->AllocateGuardRegion(address, size);
}
bool EmulatedVirtualAddressSubspace::FreeGuardRegion(Address address,
size_t size) {
if (MappedRegionContains(address, size)) {
MutexGuard guard(&mutex_);
return region_allocator_.FreeRegion(address) == size;
}
if (!UnmappedRegionContains(address, size)) return false;
return parent_space_->FreeGuardRegion(address, size);
}
bool EmulatedVirtualAddressSubspace::CanAllocateSubspaces() {
// This is not supported, mostly because it's not (yet) needed in practice.
return false;
......
......@@ -53,6 +53,10 @@ class V8_BASE_EXPORT EmulatedVirtualAddressSubspace final
bool SetPagePermissions(Address address, size_t size,
PagePermissions permissions) override;
bool AllocateGuardRegion(Address address, size_t size) override;
bool FreeGuardRegion(Address address, size_t size) override;
bool CanAllocateSubspaces() override;
std::unique_ptr<v8::VirtualAddressSpace> AllocateSubspace(
......
......@@ -383,6 +383,10 @@ inline void EnsureConsoleOutput() {
//
// This class provides the same memory management functions as OS but operates
// inside a previously reserved contiguous region of virtual address space.
//
// Reserved address space in which no pages have been allocated is guaranteed
// to be inaccessible and cause a fault on access. As such, creating guard
// regions requires no further action.
class V8_BASE_EXPORT AddressSpaceReservation {
public:
using Address = uintptr_t;
......
......@@ -40,6 +40,14 @@ class V8_BASE_EXPORT LsanVirtualAddressSpace final
return vas_->SetPagePermissions(address, size, permissions);
}
bool AllocateGuardRegion(Address address, size_t size) override {
return vas_->AllocateGuardRegion(address, size);
}
bool FreeGuardRegion(Address address, size_t size) override {
return vas_->FreeGuardRegion(address, size);
}
bool CanAllocateSubspaces() override { return vas_->CanAllocateSubspaces(); }
std::unique_ptr<VirtualAddressSpace> AllocateSubspace(
......
......@@ -77,6 +77,26 @@ bool VirtualAddressSpace::SetPagePermissions(Address address, size_t size,
static_cast<OS::MemoryPermission>(permissions));
}
bool VirtualAddressSpace::AllocateGuardRegion(Address address, size_t size) {
DCHECK(IsAligned(address, allocation_granularity()));
DCHECK(IsAligned(size, allocation_granularity()));
void* hint = reinterpret_cast<void*>(address);
void* result = OS::Allocate(hint, size, allocation_granularity(),
OS::MemoryPermission::kNoAccess);
if (result && result != hint) {
CHECK(OS::Free(result, size));
}
return result == hint;
}
bool VirtualAddressSpace::FreeGuardRegion(Address address, size_t size) {
DCHECK(IsAligned(address, allocation_granularity()));
DCHECK(IsAligned(size, allocation_granularity()));
return OS::Free(reinterpret_cast<void*>(address), size);
}
bool VirtualAddressSpace::CanAllocateSubspaces() {
return OS::CanReserveAddressSpace();
}
......@@ -204,6 +224,26 @@ bool VirtualAddressSubspace::SetPagePermissions(Address address, size_t size,
static_cast<OS::MemoryPermission>(permissions));
}
bool VirtualAddressSubspace::AllocateGuardRegion(Address address, size_t size) {
DCHECK(IsAligned(address, allocation_granularity()));
DCHECK(IsAligned(size, allocation_granularity()));
MutexGuard guard(&mutex_);
// It is guaranteed that reserved address space is inaccessible, so we just
// need to mark the region as in-use in the region allocator.
return region_allocator_.AllocateRegionAt(address, size);
}
bool VirtualAddressSubspace::FreeGuardRegion(Address address, size_t size) {
DCHECK(IsAligned(address, allocation_granularity()));
DCHECK(IsAligned(size, allocation_granularity()));
MutexGuard guard(&mutex_);
return region_allocator_.FreeRegion(address) == size;
}
std::unique_ptr<v8::VirtualAddressSpace>
VirtualAddressSubspace::AllocateSubspace(Address hint, size_t size,
size_t alignment,
......
......@@ -58,6 +58,10 @@ class V8_BASE_EXPORT VirtualAddressSpace : public VirtualAddressSpaceBase {
bool SetPagePermissions(Address address, size_t size,
PagePermissions access) override;
bool AllocateGuardRegion(Address address, size_t size) override;
bool FreeGuardRegion(Address address, size_t size) override;
bool CanAllocateSubspaces() override;
std::unique_ptr<v8::VirtualAddressSpace> AllocateSubspace(
......@@ -92,6 +96,10 @@ class V8_BASE_EXPORT VirtualAddressSubspace : public VirtualAddressSpaceBase {
bool SetPagePermissions(Address address, size_t size,
PagePermissions permissions) override;
bool AllocateGuardRegion(Address address, size_t size) override;
bool FreeGuardRegion(Address address, size_t size) override;
bool CanAllocateSubspaces() override { return true; }
std::unique_ptr<v8::VirtualAddressSpace> AllocateSubspace(
......
......@@ -197,14 +197,11 @@ bool Sandbox::Initialize(v8::VirtualAddressSpace* vas, size_t size,
reservation_size_ = reservation_size;
if (use_guard_regions) {
Address front = reservation_base_;
Address back = end_;
// These must succeed since nothing was allocated in the subspace yet.
CHECK_EQ(reservation_base_,
address_space_->AllocatePages(
reservation_base_, kSandboxGuardRegionSize,
vas->allocation_granularity(), PagePermissions::kNoAccess));
CHECK_EQ(end_, address_space_->AllocatePages(end_, kSandboxGuardRegionSize,
vas->allocation_granularity(),
PagePermissions::kNoAccess));
CHECK(address_space_->AllocateGuardRegion(front, kSandboxGuardRegionSize));
CHECK(address_space_->AllocateGuardRegion(back, kSandboxGuardRegionSize));
}
sandbox_page_allocator_ =
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment