Commit 3e2d5bfa authored by Igor Sheludko's avatar Igor Sheludko Committed by V8 LUCI CQ

[rwx][mac] Extend PageAllocator API with RecommitPages()

It's necessary to support fast W^X permission switching on MacOS on
ARM64 ("Apple M1"/Apple Silicon) where permission modification of RWX
pages to anything else is prohibited.

On all the other architectures/platforms RecommitPages() is equivalent
to SetPermissions().

The new API will be used in a follow-up CLs.

Bug: v8:12797
Change-Id: Id0d8b8c42c81b80cd8fa6b47c227680d7d1f9b10
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3606231Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarSamuel Groß <saelo@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80190}
parent ef82f4dd
......@@ -429,6 +429,17 @@ class PageAllocator {
virtual bool SetPermissions(void* address, size_t length,
Permission permissions) = 0;
/**
* Recommits discarded pages in the given range with given permissions.
* Discarded pages must be recommitted with their original permissions
* before they are used again.
*/
virtual bool RecommitPages(void* address, size_t length,
Permission permissions) {
// TODO(v8:12797): make it pure once it's implemented on Chromium side.
return false;
}
/**
* Frees memory in the given [address, address + size) range. address and size
* should be operating system page-aligned. The next write to this
......@@ -820,6 +831,24 @@ class VirtualAddressSpace {
// takes a command enum as parameter.
//
/**
* Recommits discarded pages in the given range with given permissions.
* Discarded pages must be recommitted with their original permissions
* before they are used again.
*
* \param address The start address of the range. Must be aligned to
* page_size().
*
* \param size The size in bytes of the range. Must be a multiple
* of page_size().
*
* \param permissions The permissions for the range that the pages must have.
*
* \returns true on success, false otherwise.
*/
virtual V8_WARN_UNUSED_RESULT bool RecommitPages(
Address address, size_t size, PagePermissions permissions) = 0;
/**
* Frees memory in the given [address, address + size) range. address and
* size should be aligned to the page_size(). The next write to this memory
......
......@@ -203,6 +203,14 @@ bool BoundedPageAllocator::SetPermissions(void* address, size_t size,
return page_allocator_->SetPermissions(address, size, access);
}
bool BoundedPageAllocator::RecommitPages(void* address, size_t size,
PageAllocator::Permission access) {
DCHECK(IsAligned(reinterpret_cast<Address>(address), commit_page_size_));
DCHECK(IsAligned(size, commit_page_size_));
DCHECK(region_allocator_.contains(reinterpret_cast<Address>(address), size));
return page_allocator_->RecommitPages(address, size, access);
}
bool BoundedPageAllocator::DiscardSystemPages(void* address, size_t size) {
return page_allocator_->DiscardSystemPages(address, size);
}
......
......@@ -100,6 +100,9 @@ class V8_BASE_EXPORT BoundedPageAllocator : public v8::PageAllocator {
bool SetPermissions(void* address, size_t size, Permission access) override;
bool RecommitPages(void* address, size_t size,
PageAllocator::Permission access) override;
bool DiscardSystemPages(void* address, size_t size) override;
bool DecommitPages(void* address, size_t size) override;
......
......@@ -178,6 +178,12 @@ EmulatedVirtualAddressSubspace::AllocateSubspace(
UNREACHABLE();
}
bool EmulatedVirtualAddressSubspace::RecommitPages(
Address address, size_t size, PagePermissions permissions) {
DCHECK(Contains(address, size));
return parent_space_->RecommitPages(address, size, permissions);
}
bool EmulatedVirtualAddressSubspace::DiscardSystemPages(Address address,
size_t size) {
DCHECK(Contains(address, size));
......
......@@ -70,6 +70,9 @@ class V8_BASE_EXPORT EmulatedVirtualAddressSubspace final
Address hint, size_t size, size_t alignment,
PagePermissions max_page_permissions) override;
bool RecommitPages(Address address, size_t size,
PagePermissions permissions) override;
bool DiscardSystemPages(Address address, size_t size) override;
bool DecommitPages(Address address, size_t size) override;
......
......@@ -149,6 +149,12 @@ bool PageAllocator::SetPermissions(void* address, size_t size,
address, size, static_cast<base::OS::MemoryPermission>(access));
}
bool PageAllocator::RecommitPages(void* address, size_t size,
PageAllocator::Permission access) {
return base::OS::RecommitPages(
address, size, static_cast<base::OS::MemoryPermission>(access));
}
bool PageAllocator::DiscardSystemPages(void* address, size_t size) {
return base::OS::DiscardSystemPages(address, size);
}
......
......@@ -45,6 +45,9 @@ class V8_BASE_EXPORT PageAllocator
bool SetPermissions(void* address, size_t size,
PageAllocator::Permission access) override;
bool RecommitPages(void* address, size_t size,
PageAllocator::Permission access) override;
bool DiscardSystemPages(void* address, size_t size) override;
bool DecommitPages(void* address, size_t size) override;
......
......@@ -172,6 +172,11 @@ bool OS::SetPermissions(void* address, size_t size, MemoryPermission access) {
return VirtualAlloc(address, size, MEM_COMMIT, protect) != nullptr;
}
// static
bool OS::RecommitPages(void* address, size_t size, MemoryPermission access) {
return SetPermissions(address, size, access);
}
// static
bool OS::DiscardSystemPages(void* address, size_t size) {
// On Windows, discarded pages are not returned to the system immediately and
......
......@@ -284,6 +284,11 @@ bool OS::SetPermissions(void* address, size_t size, MemoryPermission access) {
address, size, access);
}
// static
bool OS::RecommitPages(void* address, size_t size, MemoryPermission access) {
return SetPermissions(address, size, access);
}
// static
bool OS::DiscardSystemPages(void* address, size_t size) {
return DiscardSystemPagesInternal(*zx::vmar::root_self(), CommitPageSize(),
......@@ -443,6 +448,11 @@ bool AddressSpaceReservation::SetPermissions(void* address, size_t size,
address, size, access);
}
bool AddressSpaceReservation::RecommitPages(void* address, size_t size,
OS::MemoryPermission access) {
return SetPermissions(address, size, access);
}
bool AddressSpaceReservation::DiscardSystemPages(void* address, size_t size) {
DCHECK(Contains(address, size));
return DiscardSystemPagesInternal(*zx::unowned_vmar(vmar_),
......
......@@ -500,6 +500,20 @@ bool OS::SetPermissions(void* address, size_t size, MemoryPermission access) {
return ret == 0;
}
// static
bool OS::RecommitPages(void* address, size_t size, MemoryPermission access) {
DCHECK_EQ(0, reinterpret_cast<uintptr_t>(address) % CommitPageSize());
DCHECK_EQ(0, size % CommitPageSize());
#if defined(V8_OS_DARWIN)
while (madvise(address, size, MADV_FREE_REUSE) == -1 && errno == EAGAIN) {
}
return true;
#else
return SetPermissions(address, size, access);
#endif // defined(V8_OS_DARWIN)
}
// static
bool OS::DiscardSystemPages(void* address, size_t size) {
// Roughly based on PartitionAlloc's DiscardSystemPagesInternal
......@@ -510,7 +524,10 @@ bool OS::DiscardSystemPages(void* address, size_t size) {
// On OSX, MADV_FREE_REUSABLE has comparable behavior to MADV_FREE, but also
// marks the pages with the reusable bit, which allows both Activity Monitor
// and memory-infra to correctly track the pages.
int ret = madvise(address, size, MADV_FREE_REUSABLE);
int ret;
do {
ret = madvise(address, size, MADV_FREE_REUSABLE);
} while (ret != 0 && errno == EAGAIN);
if (ret) {
// MADV_FREE_REUSABLE sometimes fails, so fall back to MADV_DONTNEED.
ret = madvise(address, size, MADV_DONTNEED);
......@@ -990,6 +1007,12 @@ bool AddressSpaceReservation::SetPermissions(void* address, size_t size,
return OS::SetPermissions(address, size, access);
}
bool AddressSpaceReservation::RecommitPages(void* address, size_t size,
OS::MemoryPermission access) {
DCHECK(Contains(address, size));
return OS::RecommitPages(address, size, access);
}
bool AddressSpaceReservation::DiscardSystemPages(void* address, size_t size) {
DCHECK(Contains(address, size));
return OS::DiscardSystemPages(address, size);
......
......@@ -224,6 +224,11 @@ bool OS::SetPermissions(void* address, size_t size, MemoryPermission access) {
return SbMemoryProtect(address, size, new_protection);
}
// static
bool OS::RecommitPages(void* address, size_t size, MemoryPermission access) {
return SetPermissions(address, size, access);
}
// static
bool OS::HasLazyCommits() {
SB_NOTIMPLEMENTED();
......
......@@ -1000,6 +1000,11 @@ bool OS::SetPermissions(void* address, size_t size, MemoryPermission access) {
return VirtualAllocWrapper(address, size, MEM_COMMIT, protect) != nullptr;
}
// static
bool OS::RecommitPages(void* address, size_t size, MemoryPermission access) {
return SetPermissions(address, size, access);
}
// static
bool OS::DiscardSystemPages(void* address, size_t size) {
// On Windows, discarded pages are not returned to the system immediately and
......@@ -1281,6 +1286,12 @@ bool AddressSpaceReservation::SetPermissions(void* address, size_t size,
return OS::SetPermissions(address, size, access);
}
bool AddressSpaceReservation::RecommitPages(void* address, size_t size,
OS::MemoryPermission access) {
DCHECK(Contains(address, size));
return OS::RecommitPages(address, size, access);
}
bool AddressSpaceReservation::DiscardSystemPages(void* address, size_t size) {
DCHECK(Contains(address, size));
return OS::DiscardSystemPages(address, size);
......
......@@ -376,6 +376,9 @@ class V8_BASE_EXPORT OS {
V8_WARN_UNUSED_RESULT static bool SetPermissions(void* address, size_t size,
MemoryPermission access);
V8_WARN_UNUSED_RESULT static bool RecommitPages(void* address, size_t size,
MemoryPermission access);
V8_WARN_UNUSED_RESULT static bool DiscardSystemPages(void* address,
size_t size);
......@@ -448,6 +451,9 @@ class V8_BASE_EXPORT AddressSpaceReservation {
V8_WARN_UNUSED_RESULT bool SetPermissions(void* address, size_t size,
OS::MemoryPermission access);
V8_WARN_UNUSED_RESULT bool RecommitPages(void* address, size_t size,
OS::MemoryPermission access);
V8_WARN_UNUSED_RESULT bool DiscardSystemPages(void* address, size_t size);
V8_WARN_UNUSED_RESULT bool DecommitPages(void* address, size_t size);
......
......@@ -49,6 +49,15 @@ class V8_BASE_EXPORT LsanPageAllocator : public v8::PageAllocator {
return page_allocator_->SetPermissions(address, size, access);
}
bool RecommitPages(void* address, size_t size,
PageAllocator::Permission access) override {
return page_allocator_->RecommitPages(address, size, access);
}
bool DiscardSystemPages(void* address, size_t size) override {
return page_allocator_->DiscardSystemPages(address, size);
}
bool DecommitPages(void* address, size_t size) override {
return page_allocator_->DecommitPages(address, size);
}
......
......@@ -47,6 +47,11 @@ class V8_BASE_EXPORT LsanVirtualAddressSpace final
return vas_->SetPagePermissions(address, size, permissions);
}
bool RecommitPages(Address address, size_t size,
PagePermissions permissions) override {
return vas_->RecommitPages(address, size, permissions);
}
bool AllocateGuardRegion(Address address, size_t size) override {
return vas_->AllocateGuardRegion(address, size);
}
......
......@@ -57,6 +57,12 @@ bool VirtualAddressSpacePageAllocator::SetPermissions(
static_cast<PagePermissions>(access));
}
bool VirtualAddressSpacePageAllocator::RecommitPages(
void* address, size_t size, PageAllocator::Permission access) {
return vas_->RecommitPages(reinterpret_cast<Address>(address), size,
static_cast<PagePermissions>(access));
}
bool VirtualAddressSpacePageAllocator::DiscardSystemPages(void* address,
size_t size) {
return vas_->DiscardSystemPages(reinterpret_cast<Address>(address), size);
......
......@@ -48,6 +48,9 @@ class V8_BASE_EXPORT VirtualAddressSpacePageAllocator
bool SetPermissions(void* address, size_t size, Permission access) override;
bool RecommitPages(void* address, size_t size,
PageAllocator::Permission access) override;
bool DiscardSystemPages(void* address, size_t size) override;
bool DecommitPages(void* address, size_t size) override;
......
......@@ -161,6 +161,15 @@ std::unique_ptr<v8::VirtualAddressSpace> VirtualAddressSpace::AllocateSubspace(
new VirtualAddressSubspace(*reservation, this, max_page_permissions));
}
bool VirtualAddressSpace::RecommitPages(Address address, size_t size,
PagePermissions permissions) {
DCHECK(IsAligned(address, page_size()));
DCHECK(IsAligned(size, page_size()));
return OS::RecommitPages(reinterpret_cast<void*>(address), size,
static_cast<OS::MemoryPermission>(permissions));
}
bool VirtualAddressSpace::DiscardSystemPages(Address address, size_t size) {
DCHECK(IsAligned(address, page_size()));
DCHECK(IsAligned(size, page_size()));
......@@ -350,6 +359,17 @@ VirtualAddressSubspace::AllocateSubspace(Address hint, size_t size,
new VirtualAddressSubspace(*reservation, this, max_page_permissions));
}
bool VirtualAddressSubspace::RecommitPages(Address address, size_t size,
PagePermissions permissions) {
DCHECK(IsAligned(address, page_size()));
DCHECK(IsAligned(size, page_size()));
DCHECK(IsSubset(permissions, max_page_permissions()));
return reservation_.RecommitPages(
reinterpret_cast<void*>(address), size,
static_cast<OS::MemoryPermission>(permissions));
}
bool VirtualAddressSubspace::DiscardSystemPages(Address address, size_t size) {
DCHECK(IsAligned(address, page_size()));
DCHECK(IsAligned(size, page_size()));
......
......@@ -81,6 +81,9 @@ class V8_BASE_EXPORT VirtualAddressSpace : public VirtualAddressSpaceBase {
Address hint, size_t size, size_t alignment,
PagePermissions max_page_permissions) override;
bool RecommitPages(Address address, size_t size,
PagePermissions access) override;
bool DiscardSystemPages(Address address, size_t size) override;
bool DecommitPages(Address address, size_t size) override;
......@@ -126,6 +129,9 @@ class V8_BASE_EXPORT VirtualAddressSubspace : public VirtualAddressSpaceBase {
Address hint, size_t size, size_t alignment,
PagePermissions max_page_permissions) override;
bool RecommitPages(Address address, size_t size,
PagePermissions permissions) override;
bool DiscardSystemPages(Address address, size_t size) override;
bool DecommitPages(Address address, size_t size) override;
......
......@@ -277,8 +277,25 @@ void VirtualMemory::Reset() {
bool VirtualMemory::SetPermissions(Address address, size_t size,
PageAllocator::Permission access) {
CHECK(InVM(address, size));
bool result =
v8::internal::SetPermissions(page_allocator_, address, size, access);
bool result = page_allocator_->SetPermissions(
reinterpret_cast<void*>(address), size, access);
DCHECK(result);
return result;
}
bool VirtualMemory::RecommitPages(Address address, size_t size,
PageAllocator::Permission access) {
CHECK(InVM(address, size));
bool result = page_allocator_->RecommitPages(reinterpret_cast<void*>(address),
size, access);
DCHECK(result);
return result;
}
bool VirtualMemory::DiscardSystemPages(Address address, size_t size) {
CHECK(InVM(address, size));
bool result = page_allocator_->DiscardSystemPages(
reinterpret_cast<void*>(address), size);
DCHECK(result);
return result;
}
......
......@@ -267,6 +267,20 @@ class VirtualMemory final {
V8_EXPORT_PRIVATE bool SetPermissions(Address address, size_t size,
PageAllocator::Permission access);
// Recommits discarded pages in the given range with given permissions.
// Discarded pages must be recommitted with their original permissions
// before they are used again. |address| and |size| must be multiples of
// CommitPageSize(). Returns true on success, otherwise false.
V8_EXPORT_PRIVATE bool RecommitPages(Address address, size_t size,
PageAllocator::Permission access);
// Frees memory in the given [address, address + size) range. address and size
// should be operating system page-aligned. The next write to this
// memory area brings the memory transparently back. This should be treated as
// a hint to the OS that the pages are no longer needed. It does not guarantee
// that the pages will be discarded immediately or at all.
V8_EXPORT_PRIVATE bool DiscardSystemPages(Address address, size_t size);
// Releases memory after |free_start|. Returns the number of bytes released.
V8_EXPORT_PRIVATE size_t Release(Address free_start);
......
......@@ -797,6 +797,10 @@ class FailingPageAllocator : public v8::PageAllocator {
Permission permissions) override {
return false;
}
bool RecommitPages(void* address, size_t length,
Permission permissions) override {
return false;
}
bool DecommitPages(void* address, size_t length) override { return false; }
};
} // namespace
......
......@@ -97,6 +97,15 @@ class TrackingPageAllocator : public ::v8::PageAllocator {
return result;
}
bool RecommitPages(void* address, size_t size,
PageAllocator::Permission access) override {
UNREACHABLE();
}
bool DiscardSystemPages(void* address, size_t size) override {
UNREACHABLE();
}
bool DecommitPages(void* address, size_t size) override {
bool result = page_allocator_->DecommitPages(address, size);
if (result) {
......
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