Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / ipc / glue / SharedMemoryPlatform_windows.cpp
blob8f1b0127de157235bcb55d92ed53c60b3c4400a5
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* This source code was derived from Chromium code, and as such is also subject
8 * to the [Chromium license](ipc/chromium/src/LICENSE). */
10 #include "SharedMemoryPlatform.h"
12 #include <windows.h>
14 #include "nsDebug.h"
15 #ifdef MOZ_MEMORY
16 # include "mozmemory_utils.h"
17 #endif
19 namespace {
20 // NtQuerySection is an internal (but believed to be stable) API and the
21 // structures it uses are defined in nt_internals.h.
22 // So we have to define them ourselves.
23 typedef enum _SECTION_INFORMATION_CLASS {
24 SectionBasicInformation,
25 } SECTION_INFORMATION_CLASS;
27 typedef struct _SECTION_BASIC_INFORMATION {
28 PVOID BaseAddress;
29 ULONG Attributes;
30 LARGE_INTEGER Size;
31 } SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
33 typedef ULONG(__stdcall* NtQuerySectionType)(
34 HANDLE SectionHandle, SECTION_INFORMATION_CLASS SectionInformationClass,
35 PVOID SectionInformation, ULONG SectionInformationLength,
36 PULONG ResultLength);
38 // Checks if the section object is safe to map. At the moment this just means
39 // it's not an image section.
40 bool IsSectionSafeToMap(HANDLE aHandle) {
41 static NtQuerySectionType nt_query_section_func =
42 reinterpret_cast<NtQuerySectionType>(
43 ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection"));
44 DCHECK(nt_query_section_func);
46 // The handle must have SECTION_QUERY access for this to succeed.
47 SECTION_BASIC_INFORMATION basic_information = {};
48 ULONG status = nt_query_section_func(aHandle, SectionBasicInformation,
49 &basic_information,
50 sizeof(basic_information), nullptr);
51 if (status) {
52 return false;
55 return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE;
58 // Wrapper around CreateFileMappingW for pagefile-backed regions. When out of
59 // memory, may attempt to stall and retry rather than returning immediately, in
60 // hopes that the page file is about to be expanded by Windows. (bug 1822383,
61 // bug 1716727)
63 // This method is largely a copy of the MozVirtualAlloc method from
64 // mozjemalloc.cpp, which implements this strategy for VirtualAlloc calls,
65 // except re-purposed to handle CreateFileMapping.
66 HANDLE MozCreateFileMappingW(LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
67 DWORD flProtect, DWORD dwMaximumSizeHigh,
68 DWORD dwMaximumSizeLow, LPCWSTR lpName) {
69 #ifdef MOZ_MEMORY
70 constexpr auto IsOOMError = [] {
71 return ::GetLastError() == ERROR_COMMITMENT_LIMIT;
75 HANDLE handle = ::CreateFileMappingW(
76 INVALID_HANDLE_VALUE, lpFileMappingAttributes, flProtect,
77 dwMaximumSizeHigh, dwMaximumSizeLow, lpName);
78 if (MOZ_LIKELY(handle)) {
79 MOZ_DIAGNOSTIC_ASSERT(handle != INVALID_HANDLE_VALUE,
80 "::CreateFileMapping should return NULL, not "
81 "INVALID_HANDLE_VALUE, on failure");
82 return handle;
85 // We can't do anything for errors other than OOM.
86 if (!IsOOMError()) {
87 return nullptr;
91 // Retry as many times as desired (possibly zero).
92 const mozilla::StallSpecs stallSpecs = mozilla::GetAllocatorStallSpecs();
94 const auto ret =
95 stallSpecs.StallAndRetry(&::Sleep, [&]() -> std::optional<HANDLE> {
96 HANDLE handle = ::CreateFileMappingW(
97 INVALID_HANDLE_VALUE, lpFileMappingAttributes, flProtect,
98 dwMaximumSizeHigh, dwMaximumSizeLow, lpName);
100 if (handle) {
101 MOZ_DIAGNOSTIC_ASSERT(handle != INVALID_HANDLE_VALUE,
102 "::CreateFileMapping should return NULL, not "
103 "INVALID_HANDLE_VALUE, on failure");
104 return handle;
107 // Failure for some reason other than OOM.
108 if (!IsOOMError()) {
109 return nullptr;
112 return std::nullopt;
115 return ret.value_or(nullptr);
116 #else
117 return ::CreateFileMappingW(INVALID_HANDLE_VALUE, lpFileMappingAttributes,
118 flProtect, dwMaximumSizeHigh, dwMaximumSizeLow,
119 lpName);
120 #endif
123 } // namespace
125 namespace mozilla::ipc::shared_memory {
127 static Maybe<PlatformHandle> CreateImpl(size_t aSize, bool aFreezable) {
128 // If the shared memory object has no DACL, any process can
129 // duplicate its handles with any access rights; e.g., re-add write
130 // access to a read-only handle. To prevent that, we give it an
131 // empty DACL, so that no process can do that.
132 SECURITY_ATTRIBUTES sa, *psa = nullptr;
133 SECURITY_DESCRIPTOR sd;
134 ACL dacl;
136 if (aFreezable) {
137 psa = &sa;
138 sa.nLength = sizeof(sa);
139 sa.lpSecurityDescriptor = &sd;
140 sa.bInheritHandle = FALSE;
142 if (NS_WARN_IF(!::InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) ||
143 NS_WARN_IF(!::InitializeSecurityDescriptor(
144 &sd, SECURITY_DESCRIPTOR_REVISION)) ||
145 NS_WARN_IF(!::SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE))) {
146 return Nothing();
150 auto handle = MozCreateFileMappingW(psa, PAGE_READWRITE, 0,
151 static_cast<DWORD>(aSize), nullptr);
152 if (!handle) {
153 return Nothing();
154 } else {
155 return Some(handle);
159 bool Platform::Create(Handle& aHandle, size_t aSize) {
160 if (auto ph = CreateImpl(aSize, false)) {
161 aHandle.mHandle = std::move(*ph);
162 aHandle.mSize = aSize;
163 return true;
165 return false;
168 bool Platform::CreateFreezable(FreezableHandle& aHandle, size_t aSize) {
169 if (auto ph = CreateImpl(aSize, true)) {
170 aHandle.mHandle = std::move(*ph);
171 aHandle.mSize = aSize;
172 return true;
174 return false;
177 PlatformHandle Platform::CloneHandle(const PlatformHandle& aHandle) {
178 HANDLE h = INVALID_HANDLE_VALUE;
179 if (::DuplicateHandle(::GetCurrentProcess(), aHandle.get(),
180 ::GetCurrentProcess(), &h, 0, false,
181 DUPLICATE_SAME_ACCESS)) {
182 return PlatformHandle(h);
184 NS_WARNING("DuplicateHandle Failed!");
185 return nullptr;
188 bool Platform::Freeze(FreezableHandle& aHandle) {
189 HANDLE ro_handle;
190 if (!::DuplicateHandle(::GetCurrentProcess(), aHandle.mHandle.get(),
191 ::GetCurrentProcess(), &ro_handle,
192 GENERIC_READ | FILE_MAP_READ, false, 0)) {
193 return false;
196 aHandle.mHandle.reset(ro_handle);
197 return true;
200 Maybe<void*> Platform::Map(const HandleBase& aHandle, void* aFixedAddress,
201 bool aReadOnly) {
202 void* mem = ::MapViewOfFileEx(
203 aHandle.mHandle.get(),
204 aReadOnly ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, 0, 0,
205 aHandle.Size(), aFixedAddress);
206 if (mem) {
207 MOZ_ASSERT(!aFixedAddress || mem == aFixedAddress,
208 "MapViewOfFileEx returned an expected address");
209 return Some(mem);
211 return Nothing();
214 void Platform::Unmap(void* aMemory, size_t aSize) {
215 ::UnmapViewOfFile(aMemory);
218 bool Platform::Protect(char* aAddr, size_t aSize, Access aAccess) {
219 DWORD flags;
220 if ((aAccess & AccessReadWrite) == AccessReadWrite)
221 flags = PAGE_READWRITE;
222 else if (aAccess & AccessRead)
223 flags = PAGE_READONLY;
224 else
225 flags = PAGE_NOACCESS;
227 DWORD oldflags;
228 return ::VirtualProtect(aAddr, aSize, flags, &oldflags);
231 void* Platform::FindFreeAddressSpace(size_t aSize) {
232 void* memory = ::VirtualAlloc(NULL, aSize, MEM_RESERVE, PAGE_NOACCESS);
233 if (memory) {
234 ::VirtualFree(memory, 0, MEM_RELEASE);
236 return memory;
239 size_t Platform::PageSize() {
240 SYSTEM_INFO si;
241 ::GetSystemInfo(&si);
242 return si.dwPageSize;
245 bool Platform::IsSafeToMap(const PlatformHandle& aHandle) {
246 return IsSectionSafeToMap(aHandle.get());
249 } // namespace mozilla::ipc::shared_memory