blob: 9b6135e8b2fb12cb4787bee0a0fc235569bb4281 [file] [log] [blame]
[email protected]b05df6b2011-12-01 23:19:311// Copyright (c) 2011 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitd7cae122008-07-26 21:49:384
[email protected]99873aa2013-03-29 17:46:235#include "base/memory/shared_memory.h"
initial.commitd7cae122008-07-26 21:49:386
[email protected]6d6797eb2014-08-07 22:07:437#include <aclapi.h>
avi9beac252015-12-24 08:44:478#include <stddef.h>
9#include <stdint.h>
[email protected]6d6797eb2014-08-07 22:07:4310
initial.commitd7cae122008-07-26 21:49:3811#include "base/logging.h"
erikchen1d7cb7e2016-05-20 23:34:0412#include "base/metrics/histogram_macros.h"
[email protected]dc84fcc2014-07-24 11:42:5913#include "base/rand_util.h"
14#include "base/strings/stringprintf.h"
[email protected]a4ea1f12013-06-07 18:37:0715#include "base/strings/utf_string_conversions.h"
initial.commitd7cae122008-07-26 21:49:3816
[email protected]67ea5072013-03-28 02:02:1817namespace {
18
erikchen1d7cb7e2016-05-20 23:34:0419// Errors that can occur during Shared Memory construction.
20// These match tools/metrics/histograms/histograms.xml.
21// This enum is append-only.
22enum CreateError {
23 SUCCESS = 0,
24 SIZE_ZERO = 1,
25 SIZE_TOO_LARGE = 2,
26 INITIALIZE_ACL_FAILURE = 3,
27 INITIALIZE_SECURITY_DESC_FAILURE = 4,
28 SET_SECURITY_DESC_FAILURE = 5,
29 CREATE_FILE_MAPPING_FAILURE = 6,
30 REDUCE_PERMISSIONS_FAILURE = 7,
31 ALREADY_EXISTS = 8,
32 CREATE_ERROR_LAST = ALREADY_EXISTS
33};
34
bcwhite8ea07912016-11-09 23:38:2635// Emits UMA metrics about encountered errors. Pass zero (0) for |winerror|
36// if there is no associated Windows error.
37void LogError(CreateError error, DWORD winerror) {
erikchen1d7cb7e2016-05-20 23:34:0438 UMA_HISTOGRAM_ENUMERATION("SharedMemory.CreateError", error,
39 CREATE_ERROR_LAST + 1);
bcwhite8ea07912016-11-09 23:38:2640 static_assert(ERROR_SUCCESS == 0, "Windows error code changed!");
41 if (winerror != ERROR_SUCCESS)
42 UMA_HISTOGRAM_SPARSE_SLOWLY("SharedMemory.CreateWinError", winerror);
erikchen1d7cb7e2016-05-20 23:34:0443}
44
forshaw0474abe2015-12-18 02:16:5945typedef enum _SECTION_INFORMATION_CLASS {
46 SectionBasicInformation,
47} SECTION_INFORMATION_CLASS;
48
49typedef struct _SECTION_BASIC_INFORMATION {
50 PVOID BaseAddress;
51 ULONG Attributes;
52 LARGE_INTEGER Size;
53} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
54
55typedef ULONG(__stdcall* NtQuerySectionType)(
56 HANDLE SectionHandle,
57 SECTION_INFORMATION_CLASS SectionInformationClass,
58 PVOID SectionInformation,
59 ULONG SectionInformationLength,
60 PULONG ResultLength);
61
[email protected]67ea5072013-03-28 02:02:1862// Returns the length of the memory section starting at the supplied address.
63size_t GetMemorySectionSize(void* address) {
64 MEMORY_BASIC_INFORMATION memory_info;
65 if (!::VirtualQuery(address, &memory_info, sizeof(memory_info)))
66 return 0;
67 return memory_info.RegionSize - (static_cast<char*>(address) -
68 static_cast<char*>(memory_info.AllocationBase));
69}
70
forshaw0474abe2015-12-18 02:16:5971// Checks if the section object is safe to map. At the moment this just means
72// it's not an image section.
73bool IsSectionSafeToMap(HANDLE handle) {
74 static NtQuerySectionType nt_query_section_func;
75 if (!nt_query_section_func) {
76 nt_query_section_func = reinterpret_cast<NtQuerySectionType>(
77 ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection"));
78 DCHECK(nt_query_section_func);
79 }
80
81 // The handle must have SECTION_QUERY access for this to succeed.
82 SECTION_BASIC_INFORMATION basic_information = {};
83 ULONG status =
84 nt_query_section_func(handle, SectionBasicInformation, &basic_information,
85 sizeof(basic_information), nullptr);
86 if (status)
87 return false;
88 return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE;
89}
90
erikchen4b12c0a2016-02-19 03:15:4391// Returns a HANDLE on success and |nullptr| on failure.
92// This function is similar to CreateFileMapping, but removes the permissions
93// WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE.
94//
95// A newly created file mapping has two sets of permissions. It has access
96// control permissions (WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE) and
97// file permissions (FILE_MAP_READ, FILE_MAP_WRITE, etc.). ::DuplicateHandle()
98// with the parameter DUPLICATE_SAME_ACCESS copies both sets of permissions.
99//
100// The Chrome sandbox prevents HANDLEs with the WRITE_DAC permission from being
101// duplicated into unprivileged processes. But the only way to copy file
102// permissions is with the parameter DUPLICATE_SAME_ACCESS. This means that
103// there is no way for a privileged process to duplicate a file mapping into an
104// unprivileged process while maintaining the previous file permissions.
105//
106// By removing all access control permissions of a file mapping immediately
107// after creation, ::DuplicateHandle() effectively only copies the file
108// permissions.
109HANDLE CreateFileMappingWithReducedPermissions(SECURITY_ATTRIBUTES* sa,
110 size_t rounded_size,
111 LPCWSTR name) {
112 HANDLE h = CreateFileMapping(INVALID_HANDLE_VALUE, sa, PAGE_READWRITE, 0,
113 static_cast<DWORD>(rounded_size), name);
erikchen1d7cb7e2016-05-20 23:34:04114 if (!h) {
bcwhite8ea07912016-11-09 23:38:26115 LogError(CREATE_FILE_MAPPING_FAILURE, GetLastError());
erikchen4b12c0a2016-02-19 03:15:43116 return nullptr;
erikchen1d7cb7e2016-05-20 23:34:04117 }
erikchen4b12c0a2016-02-19 03:15:43118
119 HANDLE h2;
120 BOOL success = ::DuplicateHandle(
121 GetCurrentProcess(), h, GetCurrentProcess(), &h2,
122 FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY, FALSE, 0);
123 BOOL rv = ::CloseHandle(h);
124 DCHECK(rv);
erikchen1d7cb7e2016-05-20 23:34:04125
126 if (!success) {
bcwhite8ea07912016-11-09 23:38:26127 LogError(REDUCE_PERMISSIONS_FAILURE, GetLastError());
erikchen1d7cb7e2016-05-20 23:34:04128 return nullptr;
129 }
130
131 return h2;
erikchen4b12c0a2016-02-19 03:15:43132}
133
[email protected]67ea5072013-03-28 02:02:18134} // namespace.
135
[email protected]176aa482008-11-14 03:25:15136namespace base {
137
initial.commitd7cae122008-07-26 21:49:38138SharedMemory::SharedMemory()
forshaw0474abe2015-12-18 02:16:59139 : external_section_(false),
sammc5648c852015-07-02 01:25:00140 mapped_size_(0),
[email protected]8cc41942010-11-05 19:16:07141 memory_(NULL),
142 read_only_(false),
forshaw0474abe2015-12-18 02:16:59143 requested_size_(0) {}
144
145SharedMemory::SharedMemory(const std::wstring& name)
146 : external_section_(false),
147 name_(name),
forshaw0474abe2015-12-18 02:16:59148 mapped_size_(0),
149 memory_(NULL),
150 read_only_(false),
151 requested_size_(0) {}
[email protected]8cc41942010-11-05 19:16:07152
scottmgd19b4f72015-06-19 22:51:00153SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only)
forshaw0474abe2015-12-18 02:16:59154 : external_section_(true),
sammc5648c852015-07-02 01:25:00155 mapped_size_(0),
initial.commitd7cae122008-07-26 21:49:38156 memory_(NULL),
157 read_only_(read_only),
sammc5648c852015-07-02 01:25:00158 requested_size_(0) {
erikchen5ea2ab72015-09-25 22:34:31159 DCHECK(!handle.IsValid() || handle.BelongsToCurrentProcess());
stanisc2660facb2016-06-30 03:47:47160 mapped_file_.Set(handle.GetHandle());
initial.commitd7cae122008-07-26 21:49:38161}
162
initial.commitd7cae122008-07-26 21:49:38163SharedMemory::~SharedMemory() {
jbauman569918b2014-12-10 22:07:20164 Unmap();
initial.commitd7cae122008-07-26 21:49:38165 Close();
initial.commitd7cae122008-07-26 21:49:38166}
167
[email protected]5fe733de2009-02-11 18:59:20168// static
169bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
erikchen5ea2ab72015-09-25 22:34:31170 return handle.IsValid();
[email protected]5fe733de2009-02-11 18:59:20171}
172
[email protected]76aac1e2009-03-16 16:45:36173// static
174SharedMemoryHandle SharedMemory::NULLHandle() {
erikchen5ea2ab72015-09-25 22:34:31175 return SharedMemoryHandle();
[email protected]76aac1e2009-03-16 16:45:36176}
177
[email protected]b0af04c2009-05-18 17:46:31178// static
179void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
erikchen5ea2ab72015-09-25 22:34:31180 handle.Close();
[email protected]b0af04c2009-05-18 17:46:31181}
182
[email protected]c14eda92013-05-09 23:15:40183// static
184size_t SharedMemory::GetHandleLimit() {
185 // Rounded down from value reported here:
186 // http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx
187 return static_cast<size_t>(1 << 23);
188}
189
erikchen2096f622015-06-03 00:26:59190// static
191SharedMemoryHandle SharedMemory::DuplicateHandle(
erikchen8539d852015-05-30 01:49:19192 const SharedMemoryHandle& handle) {
erikchen5ea2ab72015-09-25 22:34:31193 DCHECK(handle.BelongsToCurrentProcess());
194 HANDLE duped_handle;
erikchen2096f622015-06-03 00:26:59195 ProcessHandle process = GetCurrentProcess();
erikchen5ea2ab72015-09-25 22:34:31196 BOOL success =
197 ::DuplicateHandle(process, handle.GetHandle(), process, &duped_handle, 0,
198 FALSE, DUPLICATE_SAME_ACCESS);
erikchenad6f6d82016-02-08 01:34:13199 if (success) {
200 base::SharedMemoryHandle handle(duped_handle, GetCurrentProcId());
201 handle.SetOwnershipPassesToIPC(true);
202 return handle;
203 }
erikchen5ea2ab72015-09-25 22:34:31204 return SharedMemoryHandle();
erikchen8539d852015-05-30 01:49:19205}
206
[email protected]374f1a82013-01-10 02:16:24207bool SharedMemory::CreateAndMapAnonymous(size_t size) {
[email protected]54e3dfa22010-10-27 18:16:06208 return CreateAnonymous(size) && Map(size);
209}
210
[email protected]b05df6b2011-12-01 23:19:31211bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
[email protected]67ea5072013-03-28 02:02:18212 // TODO(bsy,sehr): crbug.com/210609 NaCl forces us to round up 64k here,
213 // wasting 32k per mapping on average.
214 static const size_t kSectionMask = 65536 - 1;
[email protected]b05df6b2011-12-01 23:19:31215 DCHECK(!options.executable);
stanisc2660facb2016-06-30 03:47:47216 DCHECK(!mapped_file_.Get());
erikchen1d7cb7e2016-05-20 23:34:04217 if (options.size == 0) {
bcwhite8ea07912016-11-09 23:38:26218 LogError(SIZE_ZERO, 0);
[email protected]54e3dfa22010-10-27 18:16:06219 return false;
erikchen1d7cb7e2016-05-20 23:34:04220 }
initial.commitd7cae122008-07-26 21:49:38221
[email protected]67ea5072013-03-28 02:02:18222 // Check maximum accounting for overflow.
223 if (options.size >
erikchen1d7cb7e2016-05-20 23:34:04224 static_cast<size_t>(std::numeric_limits<int>::max()) - kSectionMask) {
bcwhite8ea07912016-11-09 23:38:26225 LogError(SIZE_TOO_LARGE, 0);
[email protected]374f1a82013-01-10 02:16:24226 return false;
erikchen1d7cb7e2016-05-20 23:34:04227 }
[email protected]374f1a82013-01-10 02:16:24228
[email protected]67ea5072013-03-28 02:02:18229 size_t rounded_size = (options.size + kSectionMask) & ~kSectionMask;
thestig8badc792014-12-04 22:14:22230 name_ = options.name_deprecated ?
231 ASCIIToUTF16(*options.name_deprecated) : L"";
[email protected]6d6797eb2014-08-07 22:07:43232 SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, FALSE };
233 SECURITY_DESCRIPTOR sd;
234 ACL dacl;
235
erikchenb5856b12016-05-24 17:21:59236 if (name_.empty()) {
[email protected]6d6797eb2014-08-07 22:07:43237 // Add an empty DACL to enforce anonymous read-only sections.
238 sa.lpSecurityDescriptor = &sd;
erikchen1d7cb7e2016-05-20 23:34:04239 if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) {
bcwhite8ea07912016-11-09 23:38:26240 LogError(INITIALIZE_ACL_FAILURE, GetLastError());
[email protected]6d6797eb2014-08-07 22:07:43241 return false;
erikchen1d7cb7e2016-05-20 23:34:04242 }
243 if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
bcwhite8ea07912016-11-09 23:38:26244 LogError(INITIALIZE_SECURITY_DESC_FAILURE, GetLastError());
[email protected]6d6797eb2014-08-07 22:07:43245 return false;
erikchen1d7cb7e2016-05-20 23:34:04246 }
247 if (!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE)) {
bcwhite8ea07912016-11-09 23:38:26248 LogError(SET_SECURITY_DESC_FAILURE, GetLastError());
[email protected]6d6797eb2014-08-07 22:07:43249 return false;
erikchen1d7cb7e2016-05-20 23:34:04250 }
[email protected]6d6797eb2014-08-07 22:07:43251
[email protected]dc84fcc2014-07-24 11:42:59252 // Windows ignores DACLs on certain unnamed objects (like shared sections).
253 // So, we generate a random name when we need to enforce read-only.
254 uint64_t rand_values[4];
thestig8badc792014-12-04 22:14:22255 RandBytes(&rand_values, sizeof(rand_values));
brucedawson5604a11d2015-10-06 19:22:00256 name_ = StringPrintf(L"CrSharedMem_%016llx%016llx%016llx%016llx",
thestig8badc792014-12-04 22:14:22257 rand_values[0], rand_values[1],
258 rand_values[2], rand_values[3]);
[email protected]dc84fcc2014-07-24 11:42:59259 }
hajimehoshidf47edd2017-03-02 16:48:12260 DCHECK(!name_.empty());
261 mapped_file_.Set(CreateFileMappingWithReducedPermissions(&sa, rounded_size,
262 name_.c_str()));
stanisc2660facb2016-06-30 03:47:47263 if (!mapped_file_.IsValid()) {
erikchen1d7cb7e2016-05-20 23:34:04264 // The error is logged within CreateFileMappingWithReducedPermissions().
initial.commitd7cae122008-07-26 21:49:38265 return false;
erikchen1d7cb7e2016-05-20 23:34:04266 }
initial.commitd7cae122008-07-26 21:49:38267
[email protected]67ea5072013-03-28 02:02:18268 requested_size_ = options.size;
[email protected]54e3dfa22010-10-27 18:16:06269
initial.commitd7cae122008-07-26 21:49:38270 // Check if the shared memory pre-exists.
[email protected]54e3dfa22010-10-27 18:16:06271 if (GetLastError() == ERROR_ALREADY_EXISTS) {
[email protected]67ea5072013-03-28 02:02:18272 // If the file already existed, set requested_size_ to 0 to show that
[email protected]54e3dfa22010-10-27 18:16:06273 // we don't know the size.
[email protected]67ea5072013-03-28 02:02:18274 requested_size_ = 0;
forshaw0474abe2015-12-18 02:16:59275 external_section_ = true;
[email protected]ff672b72014-03-05 21:13:52276 if (!options.open_existing_deprecated) {
[email protected]54e3dfa22010-10-27 18:16:06277 Close();
bcwhite8ea07912016-11-09 23:38:26278 // From "if" above: GetLastError() == ERROR_ALREADY_EXISTS.
279 LogError(ALREADY_EXISTS, ERROR_ALREADY_EXISTS);
[email protected]54e3dfa22010-10-27 18:16:06280 return false;
281 }
initial.commitd7cae122008-07-26 21:49:38282 }
[email protected]54e3dfa22010-10-27 18:16:06283
bcwhite8ea07912016-11-09 23:38:26284 LogError(SUCCESS, ERROR_SUCCESS);
initial.commitd7cae122008-07-26 21:49:38285 return true;
286}
287
[email protected]b6413b49b2010-09-29 20:32:22288bool SharedMemory::Delete(const std::string& name) {
[email protected]9e51af92009-02-04 00:58:39289 // intentionally empty -- there is nothing for us to do on Windows.
290 return true;
291}
292
[email protected]b6413b49b2010-09-29 20:32:22293bool SharedMemory::Open(const std::string& name, bool read_only) {
stanisc2660facb2016-06-30 03:47:47294 DCHECK(!mapped_file_.Get());
forshaw0474abe2015-12-18 02:16:59295 DWORD access = FILE_MAP_READ | SECTION_QUERY;
296 if (!read_only)
297 access |= FILE_MAP_WRITE;
thestig8badc792014-12-04 22:14:22298 name_ = ASCIIToUTF16(name);
initial.commitd7cae122008-07-26 21:49:38299 read_only_ = read_only;
stanisc2660facb2016-06-30 03:47:47300 mapped_file_.Set(
301 OpenFileMapping(access, false, name_.empty() ? nullptr : name_.c_str()));
302 if (!mapped_file_.IsValid())
forshaw0474abe2015-12-18 02:16:59303 return false;
304 // If a name specified assume it's an external section.
305 if (!name_.empty())
306 external_section_ = true;
307 // Note: size_ is not set in this case.
308 return true;
initial.commitd7cae122008-07-26 21:49:38309}
310
[email protected]e29e3f552013-01-16 09:02:34311bool SharedMemory::MapAt(off_t offset, size_t bytes) {
stanisc2660facb2016-06-30 03:47:47312 if (!mapped_file_.Get())
initial.commitd7cae122008-07-26 21:49:38313 return false;
314
[email protected]374f1a82013-01-10 02:16:24315 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
316 return false;
317
[email protected]421c1502014-03-18 22:33:28318 if (memory_)
319 return false;
320
stanisc2660facb2016-06-30 03:47:47321 if (external_section_ && !IsSectionSafeToMap(mapped_file_.Get()))
forshaw0474abe2015-12-18 02:16:59322 return false;
323
avi9beac252015-12-24 08:44:47324 memory_ = MapViewOfFile(
stanisc2660facb2016-06-30 03:47:47325 mapped_file_.Get(),
326 read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE,
avi9beac252015-12-24 08:44:47327 static_cast<uint64_t>(offset) >> 32, static_cast<DWORD>(offset), bytes);
initial.commitd7cae122008-07-26 21:49:38328 if (memory_ != NULL) {
[email protected]404a0582012-08-18 02:17:26329 DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
330 (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
[email protected]67ea5072013-03-28 02:02:18331 mapped_size_ = GetMemorySectionSize(memory_);
initial.commitd7cae122008-07-26 21:49:38332 return true;
333 }
334 return false;
335}
336
337bool SharedMemory::Unmap() {
338 if (memory_ == NULL)
339 return false;
340
341 UnmapViewOfFile(memory_);
342 memory_ = NULL;
343 return true;
344}
345
346bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
thestig8badc792014-12-04 22:14:22347 SharedMemoryHandle* new_handle,
[email protected]5f58adab2013-11-20 23:41:25348 bool close_self,
349 ShareMode share_mode) {
erikchen5ea2ab72015-09-25 22:34:31350 *new_handle = SharedMemoryHandle();
forshaw0474abe2015-12-18 02:16:59351 DWORD access = FILE_MAP_READ | SECTION_QUERY;
initial.commitd7cae122008-07-26 21:49:38352 DWORD options = 0;
stanisc2660facb2016-06-30 03:47:47353 HANDLE mapped_file = mapped_file_.Get();
initial.commitd7cae122008-07-26 21:49:38354 HANDLE result;
[email protected]5f58adab2013-11-20 23:41:25355 if (share_mode == SHARE_CURRENT_MODE && !read_only_)
initial.commitd7cae122008-07-26 21:49:38356 access |= FILE_MAP_WRITE;
357 if (close_self) {
358 // DUPLICATE_CLOSE_SOURCE causes DuplicateHandle to close mapped_file.
359 options = DUPLICATE_CLOSE_SOURCE;
stanisc2660facb2016-06-30 03:47:47360 HANDLE detached_handle = mapped_file_.Take();
361 DCHECK_EQ(detached_handle, mapped_file);
initial.commitd7cae122008-07-26 21:49:38362 Unmap();
363 }
364
365 if (process == GetCurrentProcess() && close_self) {
erikchen5ea2ab72015-09-25 22:34:31366 *new_handle = SharedMemoryHandle(mapped_file, base::GetCurrentProcId());
initial.commitd7cae122008-07-26 21:49:38367 return true;
368 }
369
erikchen2096f622015-06-03 00:26:59370 if (!::DuplicateHandle(GetCurrentProcess(), mapped_file, process, &result,
371 access, FALSE, options)) {
initial.commitd7cae122008-07-26 21:49:38372 return false;
erikchen2096f622015-06-03 00:26:59373 }
erikchen5ea2ab72015-09-25 22:34:31374 *new_handle = SharedMemoryHandle(result, base::GetProcId(process));
erikchen71bc3b22016-02-01 22:14:28375 new_handle->SetOwnershipPassesToIPC(true);
initial.commitd7cae122008-07-26 21:49:38376 return true;
377}
378
379
380void SharedMemory::Close() {
stanisc2660facb2016-06-30 03:47:47381 mapped_file_.Close();
initial.commitd7cae122008-07-26 21:49:38382}
383
[email protected]5fe733de2009-02-11 18:59:20384SharedMemoryHandle SharedMemory::handle() const {
stanisc2660facb2016-06-30 03:47:47385 return SharedMemoryHandle(mapped_file_.Get(), base::GetCurrentProcId());
[email protected]5fe733de2009-02-11 18:59:20386}
387
sadrulf08f1e4a2016-11-15 00:40:02388SharedMemoryHandle SharedMemory::TakeHandle() {
389 SharedMemoryHandle handle(mapped_file_.Take(), base::GetCurrentProcId());
390 handle.SetOwnershipPassesToIPC(true);
391 memory_ = nullptr;
392 mapped_size_ = 0;
393 return handle;
394}
395
[email protected]176aa482008-11-14 03:25:15396} // namespace base