[sandbox] Query OS for virtual address space limit
Instead of using IsWindows8Point1OrGreater which only works if the application is manifested for Windows 8.1 or Windows 10, now query the size of the virtual address space using GetSystemInfo (on Windows) or getrlimit (on Linux). Bug: v8:10391 Change-Id: If2322dbd82954640feb33c3cf93dde455038ec67 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4040664 Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Samuel Groß <saelo@chromium.org> Cr-Commit-Position: refs/heads/main@{#84588}
This commit is contained in:
parent
a6d73bb08a
commit
ff5e83144e
@ -127,5 +127,21 @@ int64_t SysInfo::AmountOfVirtualMemory() {
|
||||
#endif
|
||||
}
|
||||
|
||||
// static
|
||||
uintptr_t SysInfo::AddressSpaceEnd() {
|
||||
#if V8_OS_WIN
|
||||
SYSTEM_INFO info;
|
||||
GetSystemInfo(&info);
|
||||
uintptr_t max_address =
|
||||
reinterpret_cast<uintptr_t>(info.lpMaximumApplicationAddress);
|
||||
return max_address + 1;
|
||||
#else
|
||||
// We don't query POSIX rlimits here (e.g. RLIMIT_AS) as they limit the size
|
||||
// of memory mappings, but not the address space (e.g. even with a small
|
||||
// RLIMIT_AS, a process can still map pages at high addresses).
|
||||
return std::numeric_limits<uintptr_t>::max();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace v8
|
||||
|
@ -24,6 +24,12 @@ class V8_BASE_EXPORT SysInfo final {
|
||||
// Returns the number of bytes of virtual memory of this process. A return
|
||||
// value of zero means that there is no limit on the available virtual memory.
|
||||
static int64_t AmountOfVirtualMemory();
|
||||
|
||||
// Returns the end of the virtual address space available to this process.
|
||||
// Memory mappings at or above this address cannot be addressed by this
|
||||
// process, so all pointer values will be below this value.
|
||||
// If the virtual address space is not limited, this will return -1.
|
||||
static uintptr_t AddressSpaceEnd();
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "src/base/cpu.h"
|
||||
#include "src/base/emulated-virtual-address-subspace.h"
|
||||
#include "src/base/lazy-instance.h"
|
||||
#include "src/base/sys-info.h"
|
||||
#include "src/base/utils/random-number-generator.h"
|
||||
#include "src/base/virtual-address-space-page-allocator.h"
|
||||
#include "src/base/virtual-address-space.h"
|
||||
@ -17,19 +18,15 @@
|
||||
#include "src/sandbox/sandboxed-pointer.h"
|
||||
#include "src/utils/allocation.h"
|
||||
|
||||
#if defined(V8_OS_WIN)
|
||||
#include <windows.h>
|
||||
// This has to come after windows.h.
|
||||
#include <versionhelpers.h> // For IsWindows8Point1OrGreater().
|
||||
#endif
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
|
||||
// Best-effort helper function to determine the size of the userspace virtual
|
||||
// address space. Used to determine appropriate sandbox size and placement.
|
||||
// Best-effort function to determine the approximate size of the virtual
|
||||
// address space that can be addressed by this process. Used to determine
|
||||
// appropriate sandbox size and placement.
|
||||
// The value returned by this function will always be a power of two.
|
||||
static Address DetermineAddressSpaceLimit() {
|
||||
#ifndef V8_TARGET_ARCH_64_BIT
|
||||
#error Unsupported target architecture.
|
||||
@ -41,11 +38,11 @@ static Address DetermineAddressSpaceLimit() {
|
||||
constexpr unsigned kMinVirtualAddressBits = 36;
|
||||
constexpr unsigned kMaxVirtualAddressBits = 64;
|
||||
|
||||
Address virtual_address_bits = kDefaultVirtualAddressBits;
|
||||
unsigned hardware_virtual_address_bits = kDefaultVirtualAddressBits;
|
||||
#if defined(V8_TARGET_ARCH_X64)
|
||||
base::CPU cpu;
|
||||
if (cpu.exposes_num_virtual_address_bits()) {
|
||||
virtual_address_bits = cpu.num_virtual_address_bits();
|
||||
hardware_virtual_address_bits = cpu.num_virtual_address_bits();
|
||||
}
|
||||
#endif // V8_TARGET_ARCH_X64
|
||||
|
||||
@ -54,36 +51,39 @@ static Address DetermineAddressSpaceLimit() {
|
||||
// userspace and kernel each) as that appears to be the most common
|
||||
// configuration and there seems to be no easy way to retrieve the actual
|
||||
// number of virtual address bits from the CPU in userspace.
|
||||
virtual_address_bits = 40;
|
||||
hardware_virtual_address_bits = 40;
|
||||
#endif
|
||||
|
||||
// Assume virtual address space is split 50/50 between userspace and kernel.
|
||||
hardware_virtual_address_bits -= 1;
|
||||
|
||||
// Check if there is a software-imposed limits on the size of the address
|
||||
// space. For example, older Windows versions limit the address space to 8TB:
|
||||
// https://learn.microsoft.com/en-us/windows/win32/memory/memory-limits-for-windows-releases).
|
||||
Address software_limit = base::SysInfo::AddressSpaceEnd();
|
||||
// Compute the next power of two that is larger or equal to the limit.
|
||||
unsigned software_virtual_address_bits =
|
||||
64 - base::bits::CountLeadingZeros(software_limit - 1);
|
||||
|
||||
// The available address space is the smaller of the two limits.
|
||||
unsigned virtual_address_bits =
|
||||
std::min(hardware_virtual_address_bits, software_virtual_address_bits);
|
||||
|
||||
// Guard against nonsensical values.
|
||||
if (virtual_address_bits < kMinVirtualAddressBits ||
|
||||
virtual_address_bits > kMaxVirtualAddressBits) {
|
||||
virtual_address_bits = kDefaultVirtualAddressBits;
|
||||
}
|
||||
|
||||
// Assume virtual address space is split 50/50 between userspace and kernel.
|
||||
Address userspace_virtual_address_bits = virtual_address_bits - 1;
|
||||
Address address_space_limit = 1ULL << userspace_virtual_address_bits;
|
||||
|
||||
#if defined(V8_OS_WIN_X64)
|
||||
if (!IsWindows8Point1OrGreater()) {
|
||||
// On Windows pre 8.1 userspace is limited to 8TB on X64. See
|
||||
// https://docs.microsoft.com/en-us/windows/win32/memory/memory-limits-for-windows-releases
|
||||
address_space_limit = 8ULL * TB;
|
||||
}
|
||||
#endif // V8_OS_WIN_X64
|
||||
|
||||
return address_space_limit;
|
||||
return 1ULL << virtual_address_bits;
|
||||
}
|
||||
|
||||
void Sandbox::Initialize(v8::VirtualAddressSpace* vas) {
|
||||
// Take the number of virtual address bits into account when determining the
|
||||
// size of the address space reservation backing the sandbox. For example, if
|
||||
// there are only 40 bits available, split evenly between userspace and
|
||||
// kernel, then userspace can only address 512GB and so we use a quarter of
|
||||
// that, 128GB, as maximum reservation size.
|
||||
// Take the size of the virtual address space into account when determining
|
||||
// the size of the address space reservation backing the sandbox. For
|
||||
// example, if we only have a 40-bit address space, split evenly between
|
||||
// userspace and kernel, then userspace can only address 512GB and so we use
|
||||
// a quarter of that, 128GB, as maximum reservation size.
|
||||
Address address_space_limit = DetermineAddressSpaceLimit();
|
||||
// Note: this is technically the maximum reservation size excluding the guard
|
||||
// regions (which are not created for partially-reserved sandboxes).
|
||||
@ -109,33 +109,34 @@ void Sandbox::Initialize(v8::VirtualAddressSpace* vas) {
|
||||
|
||||
// If the maximum reservation size is less than the size of the sandbox, we
|
||||
// can only create a partially-reserved sandbox.
|
||||
if (max_reservation_size < kSandboxSize) {
|
||||
bool success;
|
||||
size_t reservation_size = std::min(kSandboxSize, max_reservation_size);
|
||||
DCHECK(base::bits::IsPowerOfTwo(reservation_size));
|
||||
if (reservation_size < kSandboxSize) {
|
||||
DCHECK_GE(max_reservation_size, kSandboxMinimumReservationSize);
|
||||
InitializeAsPartiallyReservedSandbox(vas, kSandboxSize,
|
||||
max_reservation_size);
|
||||
success = InitializeAsPartiallyReservedSandbox(vas, kSandboxSize,
|
||||
reservation_size);
|
||||
} else {
|
||||
DCHECK_EQ(kSandboxSize, reservation_size);
|
||||
constexpr bool use_guard_regions = true;
|
||||
bool success = Initialize(vas, kSandboxSize, use_guard_regions);
|
||||
// If sandboxed pointers are enabled, we need the sandbox to be initialized,
|
||||
// so fall back to creating a partially reserved sandbox.
|
||||
if (!success) {
|
||||
// Try halving the size of the backing reservation until the minimum
|
||||
// reservation size is reached.
|
||||
size_t next_reservation_size = kSandboxSize / 2;
|
||||
while (!success &&
|
||||
next_reservation_size >= kSandboxMinimumReservationSize) {
|
||||
success = InitializeAsPartiallyReservedSandbox(vas, kSandboxSize,
|
||||
next_reservation_size);
|
||||
next_reservation_size /= 2;
|
||||
}
|
||||
}
|
||||
success = Initialize(vas, kSandboxSize, use_guard_regions);
|
||||
}
|
||||
|
||||
if (!initialized_) {
|
||||
// Fall back to creating a (smaller) partially reserved sandbox.
|
||||
while (!success && reservation_size > kSandboxMinimumReservationSize) {
|
||||
reservation_size /= 2;
|
||||
DCHECK_GE(reservation_size, kSandboxMinimumReservationSize);
|
||||
success = InitializeAsPartiallyReservedSandbox(vas, kSandboxSize,
|
||||
reservation_size);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
V8::FatalProcessOutOfMemory(
|
||||
nullptr,
|
||||
"Failed to reserve the virtual address space for the V8 sandbox");
|
||||
}
|
||||
|
||||
DCHECK(initialized_);
|
||||
}
|
||||
|
||||
bool Sandbox::Initialize(v8::VirtualAddressSpace* vas, size_t size,
|
||||
|
Loading…
Reference in New Issue
Block a user