3e0ae6da5f
We are no longer using Dart VM in a setting where this matters as a security measure and it just complicates portability for no benefit. There is an indication that it is causing problems when running Linux build of Dart VM under Docker on Mac OS X. Fixes https://github.com/dart-lang/sdk/issues/54446 TEST=ci Cq-Include-Trybots: luci.dart.try:vm-fuchsia-release-arm64-try,vm-fuchsia-release-x64-try Change-Id: I11bdaa8faebaca1df6fd59097049bdaea9cb8e12 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/344581 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Slava Egorov <vegorov@google.com>
287 lines
10 KiB
C++
287 lines
10 KiB
C++
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/globals.h"
|
|
#if defined(DART_HOST_OS_FUCHSIA)
|
|
|
|
#include "vm/virtual_memory.h"
|
|
|
|
#include <zircon/process.h>
|
|
#include <zircon/status.h>
|
|
#include <zircon/syscalls.h>
|
|
|
|
#include "platform/assert.h"
|
|
#include "vm/allocation.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/isolate.h"
|
|
#include "vm/lockers.h"
|
|
#include "vm/memory_region.h"
|
|
#include "vm/os.h"
|
|
#include "vm/os_thread.h"
|
|
#include "vm/virtual_memory_compressed.h"
|
|
|
|
// #define VIRTUAL_MEMORY_LOGGING 1
|
|
#if defined(VIRTUAL_MEMORY_LOGGING)
|
|
#define LOG_ERR(msg, ...) \
|
|
OS::PrintErr("VMVM: %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__)
|
|
#define LOG_INFO(msg, ...) \
|
|
OS::PrintErr("VMVM: %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__)
|
|
#else
|
|
#define LOG_ERR(msg, ...)
|
|
#define LOG_INFO(msg, ...)
|
|
#endif // defined(VIRTUAL_MEMORY_LOGGING)
|
|
|
|
namespace dart {
|
|
|
|
DECLARE_FLAG(bool, write_protect_code);
|
|
|
|
uword VirtualMemory::page_size_ = 0;
|
|
|
|
#if defined(DART_COMPRESSED_POINTERS)
|
|
static zx_handle_t compressed_heap_vmar_ = ZX_HANDLE_INVALID;
|
|
static uword compressed_heap_base_ = 0;
|
|
#endif // defined(DART_COMPRESSED_POINTERS)
|
|
static zx_handle_t vmex_resource_ = ZX_HANDLE_INVALID;
|
|
|
|
intptr_t VirtualMemory::CalculatePageSize() {
|
|
const intptr_t page_size = getpagesize();
|
|
ASSERT(page_size != 0);
|
|
ASSERT(Utils::IsPowerOfTwo(page_size));
|
|
return page_size;
|
|
}
|
|
|
|
void VirtualMemory::Init(zx_handle_t vmex_resource) {
|
|
if (FLAG_old_gen_heap_size < 0 || FLAG_old_gen_heap_size > kMaxAddrSpaceMB) {
|
|
OS::PrintErr(
|
|
"warning: value specified for --old_gen_heap_size %d is larger than"
|
|
" the physically addressable range, using 0(unlimited) instead.`\n",
|
|
FLAG_old_gen_heap_size);
|
|
FLAG_old_gen_heap_size = 0;
|
|
}
|
|
if (FLAG_new_gen_semi_max_size < 0 ||
|
|
FLAG_new_gen_semi_max_size > kMaxAddrSpaceMB) {
|
|
OS::PrintErr(
|
|
"warning: value specified for --new_gen_semi_max_size %d is larger"
|
|
" than the physically addressable range, using %" Pd " instead.`\n",
|
|
FLAG_new_gen_semi_max_size, kDefaultNewGenSemiMaxSize);
|
|
FLAG_new_gen_semi_max_size = kDefaultNewGenSemiMaxSize;
|
|
}
|
|
|
|
#if defined(DART_COMPRESSED_POINTERS)
|
|
if (compressed_heap_vmar_ == ZX_HANDLE_INVALID) {
|
|
const zx_vm_option_t align_flag =
|
|
Utils::ShiftForPowerOfTwo(kCompressedHeapAlignment) << ZX_VM_ALIGN_BASE;
|
|
const zx_vm_option_t options = ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE |
|
|
ZX_VM_CAN_MAP_SPECIFIC | align_flag;
|
|
zx_vaddr_t region;
|
|
zx_status_t status =
|
|
zx_vmar_allocate(zx_vmar_root_self(), options, 0, kCompressedHeapSize,
|
|
&compressed_heap_vmar_, ®ion);
|
|
if (status != ZX_OK) {
|
|
LOG_ERR("zx_vmar_allocate(0x%lx) failed: %s\n", kCompressedHeapSize,
|
|
zx_status_get_string(status));
|
|
} else {
|
|
compressed_heap_base_ = reinterpret_cast<uword>(region);
|
|
ASSERT(Utils::IsAligned(compressed_heap_base_, kCompressedHeapAlignment));
|
|
}
|
|
}
|
|
#endif // defined(DART_COMPRESSED_POINTERS)
|
|
|
|
page_size_ = CalculatePageSize();
|
|
vmex_resource_ = vmex_resource;
|
|
}
|
|
|
|
void VirtualMemory::Cleanup() {
|
|
vmex_resource_ = ZX_HANDLE_INVALID;
|
|
page_size_ = 0;
|
|
|
|
#if defined(DART_COMPRESSED_POINTERS)
|
|
zx_vmar_destroy(compressed_heap_vmar_);
|
|
compressed_heap_vmar_ = ZX_HANDLE_INVALID;
|
|
compressed_heap_base_ = 0;
|
|
#endif // defined(DART_COMPRESSED_POINTERS)
|
|
}
|
|
|
|
static zx_handle_t getVmarForAddress(uword address) {
|
|
#if defined(DART_COMPRESSED_POINTERS)
|
|
if (address - compressed_heap_base_ < kCompressedHeapSize) {
|
|
return compressed_heap_vmar_;
|
|
}
|
|
#endif // defined(DART_COMPRESSED_POINTERS)
|
|
return zx_vmar_root_self();
|
|
}
|
|
|
|
static void Unmap(zx_handle_t vmar, uword start, uword end) {
|
|
ASSERT(start <= end);
|
|
const uword size = end - start;
|
|
if (size == 0) {
|
|
return;
|
|
}
|
|
|
|
zx_status_t status = zx_vmar_unmap(vmar, start, size);
|
|
if (status != ZX_OK) {
|
|
FATAL("zx_vmar_unmap failed: %s\n", zx_status_get_string(status));
|
|
}
|
|
}
|
|
|
|
VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size,
|
|
intptr_t alignment,
|
|
bool is_executable,
|
|
bool is_compressed,
|
|
const char* name) {
|
|
// When FLAG_write_protect_code is active, code memory (indicated by
|
|
// is_executable = true) is allocated as non-executable and later
|
|
// changed to executable via VirtualMemory::Protect, which requires
|
|
// ZX_RIGHT_EXECUTE on the underlying VMO.
|
|
ASSERT(Utils::IsAligned(size, page_size_));
|
|
ASSERT(Utils::IsPowerOfTwo(alignment));
|
|
ASSERT(Utils::IsAligned(alignment, page_size_));
|
|
|
|
const zx_vm_option_t align_flag = Utils::ShiftForPowerOfTwo(alignment)
|
|
<< ZX_VM_ALIGN_BASE;
|
|
ASSERT((ZX_VM_ALIGN_1KB <= align_flag) && (align_flag <= ZX_VM_ALIGN_4GB));
|
|
|
|
#if defined(DART_COMPRESSED_POINTERS)
|
|
zx_handle_t vmar;
|
|
if (is_compressed) {
|
|
RELEASE_ASSERT(!is_executable);
|
|
vmar = compressed_heap_vmar_;
|
|
} else {
|
|
vmar = zx_vmar_root_self();
|
|
}
|
|
#else
|
|
zx_handle_t vmar = zx_vmar_root_self();
|
|
#endif // defined(DART_COMPRESSED_POINTERS)
|
|
zx_handle_t vmo = ZX_HANDLE_INVALID;
|
|
zx_status_t status = zx_vmo_create(size, 0u, &vmo);
|
|
if (status == ZX_ERR_NO_MEMORY) {
|
|
LOG_ERR("zx_vmo_create(0x%lx) failed: %s\n", size,
|
|
zx_status_get_string(status));
|
|
return nullptr;
|
|
} else if (status != ZX_OK) {
|
|
FATAL("zx_vmo_create(0x%lx) failed: %s\n", size,
|
|
zx_status_get_string(status));
|
|
}
|
|
|
|
if (name != nullptr) {
|
|
zx_object_set_property(vmo, ZX_PROP_NAME, name, strlen(name));
|
|
}
|
|
|
|
if (is_executable) {
|
|
// Add ZX_RIGHT_EXECUTE permission to VMO, so it can be mapped
|
|
// into memory as executable (now or later).
|
|
status = zx_vmo_replace_as_executable(vmo, vmex_resource_, &vmo);
|
|
if (status == ZX_ERR_NO_MEMORY) {
|
|
LOG_ERR("zx_vmo_replace_as_executable() failed: %s\n",
|
|
zx_status_get_string(status));
|
|
zx_handle_close(vmo);
|
|
return nullptr;
|
|
} else if (status != ZX_OK) {
|
|
FATAL("zx_vmo_replace_as_executable() failed: %s\n",
|
|
zx_status_get_string(status));
|
|
}
|
|
}
|
|
|
|
const zx_vm_option_t region_options =
|
|
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | align_flag |
|
|
((is_executable && !FLAG_write_protect_code) ? ZX_VM_PERM_EXECUTE : 0);
|
|
uword base;
|
|
status = zx_vmar_map(vmar, region_options, 0, vmo, 0u, size, &base);
|
|
LOG_INFO("zx_vmar_map(%u, 0x%lx, 0x%lx)\n", region_options, base, size);
|
|
if (status != ZX_OK) {
|
|
LOG_ERR("zx_vmar_map(%u, 0x%lx, 0x%lx) failed: %s\n", region_options, base,
|
|
size, zx_status_get_string(status));
|
|
zx_handle_close(vmo);
|
|
return nullptr;
|
|
}
|
|
void* region_ptr = reinterpret_cast<void*>(base);
|
|
MemoryRegion region(region_ptr, size);
|
|
VirtualMemory* result = new VirtualMemory(region, region);
|
|
zx_handle_close(vmo);
|
|
|
|
#if defined(DART_COMPRESSED_POINTERS)
|
|
if (!is_executable) {
|
|
uword offset = result->start() - compressed_heap_base_;
|
|
ASSERT(offset < kCompressedHeapSize);
|
|
}
|
|
#endif // defined(DART_COMPRESSED_POINTERS)
|
|
|
|
return result;
|
|
}
|
|
|
|
VirtualMemory::~VirtualMemory() {
|
|
// Reserved region may be empty due to VirtualMemory::Truncate.
|
|
if (vm_owns_region() && reserved_.size() != 0) {
|
|
Unmap(getVmarForAddress(reserved_.start()), reserved_.start(),
|
|
reserved_.end());
|
|
LOG_INFO("zx_vmar_unmap(0x%lx, 0x%lx) success\n", reserved_.start(),
|
|
reserved_.size());
|
|
}
|
|
}
|
|
|
|
bool VirtualMemory::FreeSubSegment(void* address, intptr_t size) {
|
|
const uword start = reinterpret_cast<uword>(address);
|
|
Unmap(getVmarForAddress(start), start, start + size);
|
|
LOG_INFO("zx_vmar_unmap(0x%p, 0x%lx) success\n", address, size);
|
|
return true;
|
|
}
|
|
|
|
void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) {
|
|
#if defined(DEBUG)
|
|
Thread* thread = Thread::Current();
|
|
ASSERT(thread == nullptr || thread->IsDartMutatorThread() ||
|
|
thread->isolate() == nullptr ||
|
|
thread->isolate()->mutator_thread()->IsAtSafepoint());
|
|
#endif
|
|
const uword start_address = reinterpret_cast<uword>(address);
|
|
const uword end_address = start_address + size;
|
|
const uword page_address = Utils::RoundDown(start_address, PageSize());
|
|
uint32_t prot = 0;
|
|
switch (mode) {
|
|
case kNoAccess:
|
|
prot = 0;
|
|
break;
|
|
case kReadOnly:
|
|
prot = ZX_VM_PERM_READ;
|
|
break;
|
|
case kReadWrite:
|
|
prot = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
|
|
break;
|
|
case kReadExecute:
|
|
prot = ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE;
|
|
break;
|
|
case kReadWriteExecute:
|
|
prot = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE;
|
|
break;
|
|
}
|
|
zx_status_t status =
|
|
zx_vmar_protect(getVmarForAddress(page_address), prot, page_address,
|
|
end_address - page_address);
|
|
LOG_INFO("zx_vmar_protect(%u, 0x%lx, 0x%lx)\n", prot, page_address,
|
|
end_address - page_address);
|
|
if (status != ZX_OK) {
|
|
FATAL("zx_vmar_protect(0x%lx, 0x%lx) failed: %s\n", page_address,
|
|
end_address - page_address, zx_status_get_string(status));
|
|
}
|
|
}
|
|
|
|
void VirtualMemory::DontNeed(void* address, intptr_t size) {
|
|
uword start_address = reinterpret_cast<uword>(address);
|
|
uword end_address = start_address + size;
|
|
uword page_address = Utils::RoundDown(start_address, PageSize());
|
|
zx_status_t status = zx_vmar_op_range(
|
|
getVmarForAddress(reinterpret_cast<uword>(address)), ZX_VMAR_OP_DONT_NEED,
|
|
page_address, end_address - page_address, nullptr, 0);
|
|
LOG_INFO("zx_vmar_op_range(DONTNEED, 0x%lx, 0x%lx)\n", page_address,
|
|
end_address - page_address);
|
|
if (status != ZX_OK) {
|
|
FATAL("zx_vmar_op_range(DONTNEED, 0x%lx, 0x%lx) failed: %s\n", page_address,
|
|
end_address - page_address, zx_status_get_string(status));
|
|
}
|
|
}
|
|
|
|
} // namespace dart
|
|
|
|
#endif // defined(DART_HOST_OS_FUCHSIA)
|