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 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
6
7 #include "base/shared_memory.h"
8
9 #include "base/logging.h"
10 #include "base/win_util.h"
11 #include "base/string_util.h"
12 #include "mozilla/ipc/ProtocolUtils.h"
13 #include "mozilla/RandomNum.h"
14 #include "mozilla/WindowsVersion.h"
15 #include "nsDebug.h"
16 #include "nsString.h"
17
18 namespace {
19 // NtQuerySection is an internal (but believed to be stable) API and the
20 // structures it uses are defined in nt_internals.h.
21 // So we have to define them ourselves.
22 typedef enum _SECTION_INFORMATION_CLASS {
23 SectionBasicInformation,
24 } SECTION_INFORMATION_CLASS;
25
26 typedef struct _SECTION_BASIC_INFORMATION {
27 PVOID BaseAddress;
28 ULONG Attributes;
29 LARGE_INTEGER Size;
30 } SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
31
32 typedef ULONG(__stdcall* NtQuerySectionType)(
33 HANDLE SectionHandle, SECTION_INFORMATION_CLASS SectionInformationClass,
34 PVOID SectionInformation, ULONG SectionInformationLength,
35 PULONG ResultLength);
36
37 // Checks if the section object is safe to map. At the moment this just means
38 // it's not an image section.
IsSectionSafeToMap(HANDLE handle)39 bool IsSectionSafeToMap(HANDLE handle) {
40 static NtQuerySectionType nt_query_section_func =
41 reinterpret_cast<NtQuerySectionType>(
42 ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection"));
43 DCHECK(nt_query_section_func);
44
45 // The handle must have SECTION_QUERY access for this to succeed.
46 SECTION_BASIC_INFORMATION basic_information = {};
47 ULONG status =
48 nt_query_section_func(handle, SectionBasicInformation, &basic_information,
49 sizeof(basic_information), nullptr);
50 if (status) {
51 return false;
52 }
53
54 return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE;
55 }
56
57 } // namespace
58
59 namespace base {
60
operator ()(void * ptr)61 void SharedMemory::MappingDeleter::operator()(void* ptr) {
62 UnmapViewOfFile(ptr);
63 }
64
65 SharedMemory::~SharedMemory() = default;
66
SetHandle(SharedMemoryHandle handle,bool read_only)67 bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
68 DCHECK(!mapped_file_);
69
70 external_section_ = true;
71 freezeable_ = false; // just in case
72 mapped_file_.reset(handle);
73 read_only_ = read_only;
74 return true;
75 }
76
77 // static
IsHandleValid(const SharedMemoryHandle & handle)78 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
79 return handle != nullptr;
80 }
81
82 // static
NULLHandle()83 SharedMemoryHandle SharedMemory::NULLHandle() { return nullptr; }
84
CreateInternal(size_t size,bool freezeable)85 bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
86 DCHECK(!mapped_file_);
87 read_only_ = false;
88
89 // If the shared memory object has no DACL, any process can
90 // duplicate its handles with any access rights; e.g., re-add write
91 // access to a read-only handle. To prevent that, we give it an
92 // empty DACL, so that no process can do that.
93 SECURITY_ATTRIBUTES sa, *psa = nullptr;
94 SECURITY_DESCRIPTOR sd;
95 ACL dacl;
96 nsAutoStringN<sizeof("MozSharedMem_") + 16 * 4> name;
97
98 if (freezeable) {
99 psa = &sa;
100 sa.nLength = sizeof(sa);
101 sa.lpSecurityDescriptor = &sd;
102 sa.bInheritHandle = FALSE;
103
104 if (NS_WARN_IF(!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) ||
105 NS_WARN_IF(
106 !InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) ||
107 NS_WARN_IF(!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE))) {
108 return false;
109 }
110
111 // Older versions of Windows will silently ignore the security
112 // attributes unless the object has a name.
113 if (!mozilla::IsWin8Point1OrLater()) {
114 name.AssignLiteral("MozSharedMem_");
115 for (size_t i = 0; i < 4; ++i) {
116 mozilla::Maybe<uint64_t> randomNum = mozilla::RandomUint64();
117 if (NS_WARN_IF(randomNum.isNothing())) {
118 return false;
119 }
120 name.AppendPrintf("%016llx", *randomNum);
121 }
122 }
123 }
124
125 mapped_file_.reset(CreateFileMapping(
126 INVALID_HANDLE_VALUE, psa, PAGE_READWRITE, 0, static_cast<DWORD>(size),
127 name.IsEmpty() ? nullptr : name.get()));
128 if (!mapped_file_) return false;
129
130 max_size_ = size;
131 freezeable_ = freezeable;
132 return true;
133 }
134
ReadOnlyCopy(SharedMemory * ro_out)135 bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) {
136 DCHECK(!read_only_);
137 CHECK(freezeable_);
138
139 if (ro_out == this) {
140 DCHECK(!memory_);
141 }
142
143 HANDLE ro_handle;
144 if (!::DuplicateHandle(GetCurrentProcess(), mapped_file_.release(),
145 GetCurrentProcess(), &ro_handle,
146 GENERIC_READ | FILE_MAP_READ, false,
147 DUPLICATE_CLOSE_SOURCE)) {
148 // DUPLICATE_CLOSE_SOURCE applies even if there is an error.
149 return false;
150 }
151
152 freezeable_ = false;
153
154 ro_out->Close();
155 ro_out->mapped_file_.reset(ro_handle);
156 ro_out->max_size_ = max_size_;
157 ro_out->read_only_ = true;
158 ro_out->freezeable_ = false;
159 ro_out->external_section_ = external_section_;
160
161 return true;
162 }
163
Map(size_t bytes,void * fixed_address)164 bool SharedMemory::Map(size_t bytes, void* fixed_address) {
165 if (!mapped_file_) {
166 return false;
167 }
168
169 if (external_section_ && !IsSectionSafeToMap(mapped_file_.get())) {
170 return false;
171 }
172
173 void* mem = MapViewOfFileEx(
174 mapped_file_.get(),
175 read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, bytes,
176 fixed_address);
177 if (mem) {
178 MOZ_ASSERT(!fixed_address || mem == fixed_address,
179 "MapViewOfFileEx returned an expected address");
180 memory_.reset(mem);
181 return true;
182 }
183 return false;
184 }
185
FindFreeAddressSpace(size_t size)186 void* SharedMemory::FindFreeAddressSpace(size_t size) {
187 void* memory = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
188 if (memory) {
189 VirtualFree(memory, 0, MEM_RELEASE);
190 }
191 return memory;
192 }
193
ShareToProcessCommon(ProcessId processId,SharedMemoryHandle * new_handle,bool close_self)194 bool SharedMemory::ShareToProcessCommon(ProcessId processId,
195 SharedMemoryHandle* new_handle,
196 bool close_self) {
197 freezeable_ = false;
198 *new_handle = 0;
199 DWORD access = FILE_MAP_READ | SECTION_QUERY;
200 DWORD options = 0;
201 HANDLE mapped_file;
202 HANDLE result;
203 if (!read_only_) {
204 access |= FILE_MAP_WRITE;
205 }
206 if (close_self) {
207 // DUPLICATE_CLOSE_SOURCE causes DuplicateHandle to close mapped_file.
208 mapped_file = mapped_file_.release();
209 options = DUPLICATE_CLOSE_SOURCE;
210 Unmap();
211 } else {
212 mapped_file = mapped_file_.get();
213 }
214
215 if (processId == GetCurrentProcId() && close_self) {
216 *new_handle = mapped_file;
217 return true;
218 }
219
220 if (!mozilla::ipc::DuplicateHandle(mapped_file, processId, &result, access,
221 options)) {
222 return false;
223 }
224
225 *new_handle = result;
226 return true;
227 }
228
Close(bool unmap_view)229 void SharedMemory::Close(bool unmap_view) {
230 if (unmap_view) {
231 Unmap();
232 }
233
234 mapped_file_ = nullptr;
235 }
236
237 } // namespace base
238