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"
16 # include "mozmemory_utils.h"
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
{
31 } SECTION_BASIC_INFORMATION
, *PSECTION_BASIC_INFORMATION
;
33 typedef ULONG(__stdcall
* NtQuerySectionType
)(
34 HANDLE SectionHandle
, SECTION_INFORMATION_CLASS SectionInformationClass
,
35 PVOID SectionInformation
, ULONG SectionInformationLength
,
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
,
50 sizeof(basic_information
), nullptr);
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,
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
) {
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");
85 // We can't do anything for errors other than OOM.
91 // Retry as many times as desired (possibly zero).
92 const mozilla::StallSpecs stallSpecs
= mozilla::GetAllocatorStallSpecs();
95 stallSpecs
.StallAndRetry(&::Sleep
, [&]() -> std::optional
<HANDLE
> {
96 HANDLE handle
= ::CreateFileMappingW(
97 INVALID_HANDLE_VALUE
, lpFileMappingAttributes
, flProtect
,
98 dwMaximumSizeHigh
, dwMaximumSizeLow
, lpName
);
101 MOZ_DIAGNOSTIC_ASSERT(handle
!= INVALID_HANDLE_VALUE
,
102 "::CreateFileMapping should return NULL, not "
103 "INVALID_HANDLE_VALUE, on failure");
107 // Failure for some reason other than OOM.
115 return ret
.value_or(nullptr);
117 return ::CreateFileMappingW(INVALID_HANDLE_VALUE
, lpFileMappingAttributes
,
118 flProtect
, dwMaximumSizeHigh
, dwMaximumSizeLow
,
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
;
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
))) {
150 auto handle
= MozCreateFileMappingW(psa
, PAGE_READWRITE
, 0,
151 static_cast<DWORD
>(aSize
), nullptr);
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
;
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
;
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!");
188 bool Platform::Freeze(FreezableHandle
& aHandle
) {
190 if (!::DuplicateHandle(::GetCurrentProcess(), aHandle
.mHandle
.get(),
191 ::GetCurrentProcess(), &ro_handle
,
192 GENERIC_READ
| FILE_MAP_READ
, false, 0)) {
196 aHandle
.mHandle
.reset(ro_handle
);
200 Maybe
<void*> Platform::Map(const HandleBase
& aHandle
, void* aFixedAddress
,
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
);
207 MOZ_ASSERT(!aFixedAddress
|| mem
== aFixedAddress
,
208 "MapViewOfFileEx returned an expected address");
214 void Platform::Unmap(void* aMemory
, size_t aSize
) {
215 ::UnmapViewOfFile(aMemory
);
218 bool Platform::Protect(char* aAddr
, size_t aSize
, Access aAccess
) {
220 if ((aAccess
& AccessReadWrite
) == AccessReadWrite
)
221 flags
= PAGE_READWRITE
;
222 else if (aAccess
& AccessRead
)
223 flags
= PAGE_READONLY
;
225 flags
= PAGE_NOACCESS
;
228 return ::VirtualProtect(aAddr
, aSize
, flags
, &oldflags
);
231 void* Platform::FindFreeAddressSpace(size_t aSize
) {
232 void* memory
= ::VirtualAlloc(NULL
, aSize
, MEM_RESERVE
, PAGE_NOACCESS
);
234 ::VirtualFree(memory
, 0, MEM_RELEASE
);
239 size_t Platform::PageSize() {
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