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