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
5 #ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
6 #define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
7
8 #include "third_party/base/allocator/partition_allocator/oom.h"
9 #include "third_party/base/allocator/partition_allocator/page_allocator_internal.h"
10
11 namespace pdfium {
12 namespace base {
13
14 // |VirtualAlloc| will fail if allocation at the hint address is blocked.
15 constexpr bool kHintIsAdvisory = false;
16 std::atomic<int32_t> s_allocPageErrorCode{ERROR_SUCCESS};
17
GetAccessFlags(PageAccessibilityConfiguration accessibility)18 int GetAccessFlags(PageAccessibilityConfiguration accessibility) {
19 switch (accessibility) {
20 case PageRead:
21 return PAGE_READONLY;
22 case PageReadWrite:
23 return PAGE_READWRITE;
24 case PageReadExecute:
25 return PAGE_EXECUTE_READ;
26 case PageReadWriteExecute:
27 return PAGE_EXECUTE_READWRITE;
28 default:
29 NOTREACHED();
30 FALLTHROUGH;
31 case PageInaccessible:
32 return PAGE_NOACCESS;
33 }
34 }
35
SystemAllocPagesInternal(void * hint,size_t length,PageAccessibilityConfiguration accessibility,PageTag page_tag,bool commit)36 void* SystemAllocPagesInternal(void* hint,
37 size_t length,
38 PageAccessibilityConfiguration accessibility,
39 PageTag page_tag,
40 bool commit) {
41 DWORD access_flag = GetAccessFlags(accessibility);
42 const DWORD type_flags = commit ? (MEM_RESERVE | MEM_COMMIT) : MEM_RESERVE;
43 void* ret = VirtualAlloc(hint, length, type_flags, access_flag);
44 if (ret == nullptr) {
45 s_allocPageErrorCode = GetLastError();
46 }
47 return ret;
48 }
49
TrimMappingInternal(void * base,size_t base_length,size_t trim_length,PageAccessibilityConfiguration accessibility,bool commit,size_t pre_slack,size_t post_slack)50 void* TrimMappingInternal(void* base,
51 size_t base_length,
52 size_t trim_length,
53 PageAccessibilityConfiguration accessibility,
54 bool commit,
55 size_t pre_slack,
56 size_t post_slack) {
57 void* ret = base;
58 if (pre_slack || post_slack) {
59 // We cannot resize the allocation run. Free it and retry at the aligned
60 // address within the freed range.
61 ret = reinterpret_cast<char*>(base) + pre_slack;
62 FreePages(base, base_length);
63 ret = SystemAllocPages(ret, trim_length, accessibility, PageTag::kChromium,
64 commit);
65 }
66 return ret;
67 }
68
TrySetSystemPagesAccessInternal(void * address,size_t length,PageAccessibilityConfiguration accessibility)69 bool TrySetSystemPagesAccessInternal(
70 void* address,
71 size_t length,
72 PageAccessibilityConfiguration accessibility) {
73 if (accessibility == PageInaccessible)
74 return VirtualFree(address, length, MEM_DECOMMIT) != 0;
75 return nullptr != VirtualAlloc(address, length, MEM_COMMIT,
76 GetAccessFlags(accessibility));
77 }
78
SetSystemPagesAccessInternal(void * address,size_t length,PageAccessibilityConfiguration accessibility)79 void SetSystemPagesAccessInternal(
80 void* address,
81 size_t length,
82 PageAccessibilityConfiguration accessibility) {
83 if (accessibility == PageInaccessible) {
84 if (!VirtualFree(address, length, MEM_DECOMMIT)) {
85 // We check `GetLastError` for `ERROR_SUCCESS` here so that in a crash
86 // report we get the error number.
87 CHECK_EQ(static_cast<uint32_t>(ERROR_SUCCESS), GetLastError());
88 }
89 } else {
90 if (!VirtualAlloc(address, length, MEM_COMMIT,
91 GetAccessFlags(accessibility))) {
92 int32_t error = GetLastError();
93 if (error == ERROR_COMMITMENT_LIMIT)
94 OOM_CRASH();
95 // We check `GetLastError` for `ERROR_SUCCESS` here so that in a crash
96 // report we get the error number.
97 CHECK_EQ(ERROR_SUCCESS, error);
98 }
99 }
100 }
101
FreePagesInternal(void * address,size_t length)102 void FreePagesInternal(void* address, size_t length) {
103 CHECK(VirtualFree(address, 0, MEM_RELEASE));
104 }
105
DecommitSystemPagesInternal(void * address,size_t length)106 void DecommitSystemPagesInternal(void* address, size_t length) {
107 SetSystemPagesAccess(address, length, PageInaccessible);
108 }
109
RecommitSystemPagesInternal(void * address,size_t length,PageAccessibilityConfiguration accessibility)110 bool RecommitSystemPagesInternal(void* address,
111 size_t length,
112 PageAccessibilityConfiguration accessibility) {
113 return TrySetSystemPagesAccess(address, length, accessibility);
114 }
115
DiscardSystemPagesInternal(void * address,size_t length)116 void DiscardSystemPagesInternal(void* address, size_t length) {
117 // On Windows, discarded pages are not returned to the system immediately and
118 // not guaranteed to be zeroed when returned to the application.
119 using DiscardVirtualMemoryFunction =
120 DWORD(WINAPI*)(PVOID virtualAddress, SIZE_T size);
121 static DiscardVirtualMemoryFunction discard_virtual_memory =
122 reinterpret_cast<DiscardVirtualMemoryFunction>(-1);
123 if (discard_virtual_memory ==
124 reinterpret_cast<DiscardVirtualMemoryFunction>(-1))
125 discard_virtual_memory =
126 reinterpret_cast<DiscardVirtualMemoryFunction>(GetProcAddress(
127 GetModuleHandle(L"Kernel32.dll"), "DiscardVirtualMemory"));
128 // Use DiscardVirtualMemory when available because it releases faster than
129 // MEM_RESET.
130 DWORD ret = 1;
131 if (discard_virtual_memory) {
132 ret = discard_virtual_memory(address, length);
133 }
134 // DiscardVirtualMemory is buggy in Win10 SP0, so fall back to MEM_RESET on
135 // failure.
136 if (ret) {
137 void* ptr = VirtualAlloc(address, length, MEM_RESET, PAGE_READWRITE);
138 CHECK(ptr);
139 }
140 }
141
142 } // namespace base
143 } // namespace pdfium
144
145 #endif // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
146