123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- // Copyright (c) 2018 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
- #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
- #include <versionhelpers.h>
- #include <cstdint>
- #include "base/allocator/partition_allocator/oom.h"
- #include "base/allocator/partition_allocator/page_allocator_internal.h"
- #include "base/allocator/partition_allocator/partition_alloc_check.h"
- #include "base/allocator/partition_allocator/partition_alloc_notreached.h"
- namespace partition_alloc::internal {
- namespace {
- // On Windows, discarded pages are not returned to the system immediately and
- // not guaranteed to be zeroed when returned to the application.
- using DiscardVirtualMemoryFunction = DWORD(WINAPI*)(PVOID virtualAddress,
- SIZE_T size);
- DiscardVirtualMemoryFunction s_discard_virtual_memory =
- reinterpret_cast<DiscardVirtualMemoryFunction>(-1);
- } // namespace
- // |VirtualAlloc| will fail if allocation at the hint address is blocked.
- constexpr bool kHintIsAdvisory = false;
- std::atomic<int32_t> s_allocPageErrorCode{ERROR_SUCCESS};
- int GetAccessFlags(PageAccessibilityConfiguration accessibility) {
- switch (accessibility) {
- case PageAccessibilityConfiguration::kRead:
- return PAGE_READONLY;
- case PageAccessibilityConfiguration::kReadWrite:
- case PageAccessibilityConfiguration::kReadWriteTagged:
- return PAGE_READWRITE;
- case PageAccessibilityConfiguration::kReadExecute:
- case PageAccessibilityConfiguration::kReadExecuteProtected:
- return PAGE_EXECUTE_READ;
- case PageAccessibilityConfiguration::kReadWriteExecute:
- return PAGE_EXECUTE_READWRITE;
- default:
- PA_NOTREACHED();
- [[fallthrough]];
- case PageAccessibilityConfiguration::kInaccessible:
- return PAGE_NOACCESS;
- }
- }
- uintptr_t SystemAllocPagesInternal(uintptr_t hint,
- size_t length,
- PageAccessibilityConfiguration accessibility,
- PageTag page_tag) {
- DWORD access_flag = GetAccessFlags(accessibility);
- const DWORD type_flags =
- (accessibility != PageAccessibilityConfiguration::kInaccessible)
- ? (MEM_RESERVE | MEM_COMMIT)
- : MEM_RESERVE;
- void* ret = VirtualAlloc(reinterpret_cast<void*>(hint), length, type_flags,
- access_flag);
- if (ret == nullptr) {
- s_allocPageErrorCode = GetLastError();
- }
- return reinterpret_cast<uintptr_t>(ret);
- }
- uintptr_t TrimMappingInternal(uintptr_t base_address,
- size_t base_length,
- size_t trim_length,
- PageAccessibilityConfiguration accessibility,
- size_t pre_slack,
- size_t post_slack) {
- uintptr_t ret = base_address;
- if (pre_slack || post_slack) {
- // We cannot resize the allocation run. Free it and retry at the aligned
- // address within the freed range.
- ret = base_address + pre_slack;
- FreePages(base_address, base_length);
- ret = SystemAllocPages(ret, trim_length, accessibility, PageTag::kChromium);
- }
- return ret;
- }
- bool TrySetSystemPagesAccessInternal(
- uintptr_t address,
- size_t length,
- PageAccessibilityConfiguration accessibility) {
- void* ptr = reinterpret_cast<void*>(address);
- if (accessibility == PageAccessibilityConfiguration::kInaccessible)
- return VirtualFree(ptr, length, MEM_DECOMMIT) != 0;
- return nullptr !=
- VirtualAlloc(ptr, length, MEM_COMMIT, GetAccessFlags(accessibility));
- }
- void SetSystemPagesAccessInternal(
- uintptr_t address,
- size_t length,
- PageAccessibilityConfiguration accessibility) {
- void* ptr = reinterpret_cast<void*>(address);
- if (accessibility == PageAccessibilityConfiguration::kInaccessible) {
- if (!VirtualFree(ptr, length, MEM_DECOMMIT)) {
- // We check `GetLastError` for `ERROR_SUCCESS` here so that in a crash
- // report we get the error number.
- PA_CHECK(static_cast<uint32_t>(ERROR_SUCCESS) == GetLastError());
- }
- } else {
- if (!VirtualAlloc(ptr, length, MEM_COMMIT, GetAccessFlags(accessibility))) {
- int32_t error = GetLastError();
- if (error == ERROR_COMMITMENT_LIMIT)
- OOM_CRASH(length);
- // We check `GetLastError` for `ERROR_SUCCESS` here so that in a crash
- // report we get the error number.
- PA_CHECK(ERROR_SUCCESS == error);
- }
- }
- }
- void FreePagesInternal(uintptr_t address, size_t length) {
- PA_CHECK(VirtualFree(reinterpret_cast<void*>(address), 0, MEM_RELEASE));
- }
- void DecommitSystemPagesInternal(
- uintptr_t address,
- size_t length,
- PageAccessibilityDisposition accessibility_disposition) {
- // Ignore accessibility_disposition, because decommitting is equivalent to
- // making pages inaccessible.
- SetSystemPagesAccess(address, length,
- PageAccessibilityConfiguration::kInaccessible);
- }
- void DecommitAndZeroSystemPagesInternal(uintptr_t address, size_t length) {
- // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualfree:
- // "If a page is decommitted but not released, its state changes to reserved.
- // Subsequently, you can call VirtualAlloc to commit it, or VirtualFree to
- // release it. Attempts to read from or write to a reserved page results in an
- // access violation exception."
- // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
- // for MEM_COMMIT: "The function also guarantees that when the caller later
- // initially accesses the memory, the contents will be zero."
- PA_CHECK(VirtualFree(reinterpret_cast<void*>(address), length, MEM_DECOMMIT));
- }
- void RecommitSystemPagesInternal(
- uintptr_t address,
- size_t length,
- PageAccessibilityConfiguration accessibility,
- PageAccessibilityDisposition accessibility_disposition) {
- // Ignore accessibility_disposition, because decommitting is equivalent to
- // making pages inaccessible.
- SetSystemPagesAccess(address, length, accessibility);
- }
- bool TryRecommitSystemPagesInternal(
- uintptr_t address,
- size_t length,
- PageAccessibilityConfiguration accessibility,
- PageAccessibilityDisposition accessibility_disposition) {
- // Ignore accessibility_disposition, because decommitting is equivalent to
- // making pages inaccessible.
- return TrySetSystemPagesAccess(address, length, accessibility);
- }
- void DiscardSystemPagesInternal(uintptr_t address, size_t length) {
- if (s_discard_virtual_memory ==
- reinterpret_cast<DiscardVirtualMemoryFunction>(-1)) {
- // DiscardVirtualMemory's minimum supported client is Windows 8.1 Update.
- // So skip GetProcAddress("DiscardVirtualMemory") if windows version is
- // smaller than Windows 8.1.
- if (IsWindows8Point1OrGreater()) {
- s_discard_virtual_memory =
- reinterpret_cast<DiscardVirtualMemoryFunction>(GetProcAddress(
- GetModuleHandle(L"Kernel32.dll"), "DiscardVirtualMemory"));
- } else {
- s_discard_virtual_memory = nullptr;
- }
- }
- void* ptr = reinterpret_cast<void*>(address);
- // Use DiscardVirtualMemory when available because it releases faster than
- // MEM_RESET.
- DWORD ret = 1;
- if (s_discard_virtual_memory) {
- ret = s_discard_virtual_memory(ptr, length);
- }
- // DiscardVirtualMemory is buggy in Win10 SP0, so fall back to MEM_RESET on
- // failure.
- if (ret) {
- PA_CHECK(VirtualAlloc(ptr, length, MEM_RESET, PAGE_READWRITE));
- }
- }
- } // namespace partition_alloc::internal
- #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
|