page_allocator_internals_win.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // Copyright (c) 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
  5. #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
  6. #include <versionhelpers.h>
  7. #include <cstdint>
  8. #include "base/allocator/partition_allocator/oom.h"
  9. #include "base/allocator/partition_allocator/page_allocator_internal.h"
  10. #include "base/allocator/partition_allocator/partition_alloc_check.h"
  11. #include "base/allocator/partition_allocator/partition_alloc_notreached.h"
  12. namespace partition_alloc::internal {
  13. namespace {
  14. // On Windows, discarded pages are not returned to the system immediately and
  15. // not guaranteed to be zeroed when returned to the application.
  16. using DiscardVirtualMemoryFunction = DWORD(WINAPI*)(PVOID virtualAddress,
  17. SIZE_T size);
  18. DiscardVirtualMemoryFunction s_discard_virtual_memory =
  19. reinterpret_cast<DiscardVirtualMemoryFunction>(-1);
  20. } // namespace
  21. // |VirtualAlloc| will fail if allocation at the hint address is blocked.
  22. constexpr bool kHintIsAdvisory = false;
  23. std::atomic<int32_t> s_allocPageErrorCode{ERROR_SUCCESS};
  24. int GetAccessFlags(PageAccessibilityConfiguration accessibility) {
  25. switch (accessibility) {
  26. case PageAccessibilityConfiguration::kRead:
  27. return PAGE_READONLY;
  28. case PageAccessibilityConfiguration::kReadWrite:
  29. case PageAccessibilityConfiguration::kReadWriteTagged:
  30. return PAGE_READWRITE;
  31. case PageAccessibilityConfiguration::kReadExecute:
  32. case PageAccessibilityConfiguration::kReadExecuteProtected:
  33. return PAGE_EXECUTE_READ;
  34. case PageAccessibilityConfiguration::kReadWriteExecute:
  35. return PAGE_EXECUTE_READWRITE;
  36. default:
  37. PA_NOTREACHED();
  38. [[fallthrough]];
  39. case PageAccessibilityConfiguration::kInaccessible:
  40. return PAGE_NOACCESS;
  41. }
  42. }
  43. uintptr_t SystemAllocPagesInternal(uintptr_t hint,
  44. size_t length,
  45. PageAccessibilityConfiguration accessibility,
  46. PageTag page_tag) {
  47. DWORD access_flag = GetAccessFlags(accessibility);
  48. const DWORD type_flags =
  49. (accessibility != PageAccessibilityConfiguration::kInaccessible)
  50. ? (MEM_RESERVE | MEM_COMMIT)
  51. : MEM_RESERVE;
  52. void* ret = VirtualAlloc(reinterpret_cast<void*>(hint), length, type_flags,
  53. access_flag);
  54. if (ret == nullptr) {
  55. s_allocPageErrorCode = GetLastError();
  56. }
  57. return reinterpret_cast<uintptr_t>(ret);
  58. }
  59. uintptr_t TrimMappingInternal(uintptr_t base_address,
  60. size_t base_length,
  61. size_t trim_length,
  62. PageAccessibilityConfiguration accessibility,
  63. size_t pre_slack,
  64. size_t post_slack) {
  65. uintptr_t ret = base_address;
  66. if (pre_slack || post_slack) {
  67. // We cannot resize the allocation run. Free it and retry at the aligned
  68. // address within the freed range.
  69. ret = base_address + pre_slack;
  70. FreePages(base_address, base_length);
  71. ret = SystemAllocPages(ret, trim_length, accessibility, PageTag::kChromium);
  72. }
  73. return ret;
  74. }
  75. bool TrySetSystemPagesAccessInternal(
  76. uintptr_t address,
  77. size_t length,
  78. PageAccessibilityConfiguration accessibility) {
  79. void* ptr = reinterpret_cast<void*>(address);
  80. if (accessibility == PageAccessibilityConfiguration::kInaccessible)
  81. return VirtualFree(ptr, length, MEM_DECOMMIT) != 0;
  82. return nullptr !=
  83. VirtualAlloc(ptr, length, MEM_COMMIT, GetAccessFlags(accessibility));
  84. }
  85. void SetSystemPagesAccessInternal(
  86. uintptr_t address,
  87. size_t length,
  88. PageAccessibilityConfiguration accessibility) {
  89. void* ptr = reinterpret_cast<void*>(address);
  90. if (accessibility == PageAccessibilityConfiguration::kInaccessible) {
  91. if (!VirtualFree(ptr, length, MEM_DECOMMIT)) {
  92. // We check `GetLastError` for `ERROR_SUCCESS` here so that in a crash
  93. // report we get the error number.
  94. PA_CHECK(static_cast<uint32_t>(ERROR_SUCCESS) == GetLastError());
  95. }
  96. } else {
  97. if (!VirtualAlloc(ptr, length, MEM_COMMIT, GetAccessFlags(accessibility))) {
  98. int32_t error = GetLastError();
  99. if (error == ERROR_COMMITMENT_LIMIT)
  100. OOM_CRASH(length);
  101. // We check `GetLastError` for `ERROR_SUCCESS` here so that in a crash
  102. // report we get the error number.
  103. PA_CHECK(ERROR_SUCCESS == error);
  104. }
  105. }
  106. }
  107. void FreePagesInternal(uintptr_t address, size_t length) {
  108. PA_CHECK(VirtualFree(reinterpret_cast<void*>(address), 0, MEM_RELEASE));
  109. }
  110. void DecommitSystemPagesInternal(
  111. uintptr_t address,
  112. size_t length,
  113. PageAccessibilityDisposition accessibility_disposition) {
  114. // Ignore accessibility_disposition, because decommitting is equivalent to
  115. // making pages inaccessible.
  116. SetSystemPagesAccess(address, length,
  117. PageAccessibilityConfiguration::kInaccessible);
  118. }
  119. void DecommitAndZeroSystemPagesInternal(uintptr_t address, size_t length) {
  120. // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualfree:
  121. // "If a page is decommitted but not released, its state changes to reserved.
  122. // Subsequently, you can call VirtualAlloc to commit it, or VirtualFree to
  123. // release it. Attempts to read from or write to a reserved page results in an
  124. // access violation exception."
  125. // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
  126. // for MEM_COMMIT: "The function also guarantees that when the caller later
  127. // initially accesses the memory, the contents will be zero."
  128. PA_CHECK(VirtualFree(reinterpret_cast<void*>(address), length, MEM_DECOMMIT));
  129. }
  130. void RecommitSystemPagesInternal(
  131. uintptr_t address,
  132. size_t length,
  133. PageAccessibilityConfiguration accessibility,
  134. PageAccessibilityDisposition accessibility_disposition) {
  135. // Ignore accessibility_disposition, because decommitting is equivalent to
  136. // making pages inaccessible.
  137. SetSystemPagesAccess(address, length, accessibility);
  138. }
  139. bool TryRecommitSystemPagesInternal(
  140. uintptr_t address,
  141. size_t length,
  142. PageAccessibilityConfiguration accessibility,
  143. PageAccessibilityDisposition accessibility_disposition) {
  144. // Ignore accessibility_disposition, because decommitting is equivalent to
  145. // making pages inaccessible.
  146. return TrySetSystemPagesAccess(address, length, accessibility);
  147. }
  148. void DiscardSystemPagesInternal(uintptr_t address, size_t length) {
  149. if (s_discard_virtual_memory ==
  150. reinterpret_cast<DiscardVirtualMemoryFunction>(-1)) {
  151. // DiscardVirtualMemory's minimum supported client is Windows 8.1 Update.
  152. // So skip GetProcAddress("DiscardVirtualMemory") if windows version is
  153. // smaller than Windows 8.1.
  154. if (IsWindows8Point1OrGreater()) {
  155. s_discard_virtual_memory =
  156. reinterpret_cast<DiscardVirtualMemoryFunction>(GetProcAddress(
  157. GetModuleHandle(L"Kernel32.dll"), "DiscardVirtualMemory"));
  158. } else {
  159. s_discard_virtual_memory = nullptr;
  160. }
  161. }
  162. void* ptr = reinterpret_cast<void*>(address);
  163. // Use DiscardVirtualMemory when available because it releases faster than
  164. // MEM_RESET.
  165. DWORD ret = 1;
  166. if (s_discard_virtual_memory) {
  167. ret = s_discard_virtual_memory(ptr, length);
  168. }
  169. // DiscardVirtualMemory is buggy in Win10 SP0, so fall back to MEM_RESET on
  170. // failure.
  171. if (ret) {
  172. PA_CHECK(VirtualAlloc(ptr, length, MEM_RESET, PAGE_READWRITE));
  173. }
  174. }
  175. } // namespace partition_alloc::internal
  176. #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_