blob: ffa7bd72bd1530d3f3180de8c73f328fc1663d24 [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
[email protected]b0af04c2009-05-18 17:46:31174void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
erikchen5ea2ab72015-09-25 22:34:31175 handle.Close();
[email protected]b0af04c2009-05-18 17:46:31176}
177
[email protected]c14eda92013-05-09 23:15:40178// static
179size_t SharedMemory::GetHandleLimit() {
180 // Rounded down from value reported here:
181 // http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx
182 return static_cast<size_t>(1 << 23);
183}
184
erikchen2096f622015-06-03 00:26:59185// static
186SharedMemoryHandle SharedMemory::DuplicateHandle(
erikchen8539d852015-05-30 01:49:19187 const SharedMemoryHandle& handle) {
erikchen63840882017-05-02 20:52:31188 return handle.Duplicate();
erikchen8539d852015-05-30 01:49:19189}
190
[email protected]374f1a82013-01-10 02:16:24191bool SharedMemory::CreateAndMapAnonymous(size_t size) {
[email protected]54e3dfa22010-10-27 18:16:06192 return CreateAnonymous(size) && Map(size);
193}
194
[email protected]b05df6b2011-12-01 23:19:31195bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
[email protected]67ea5072013-03-28 02:02:18196 // TODO(bsy,sehr): crbug.com/210609 NaCl forces us to round up 64k here,
197 // wasting 32k per mapping on average.
198 static const size_t kSectionMask = 65536 - 1;
[email protected]b05df6b2011-12-01 23:19:31199 DCHECK(!options.executable);
stanisc2660facb2016-06-30 03:47:47200 DCHECK(!mapped_file_.Get());
erikchen1d7cb7e2016-05-20 23:34:04201 if (options.size == 0) {
bcwhite8ea07912016-11-09 23:38:26202 LogError(SIZE_ZERO, 0);
[email protected]54e3dfa22010-10-27 18:16:06203 return false;
erikchen1d7cb7e2016-05-20 23:34:04204 }
initial.commitd7cae122008-07-26 21:49:38205
[email protected]67ea5072013-03-28 02:02:18206 // Check maximum accounting for overflow.
207 if (options.size >
erikchen1d7cb7e2016-05-20 23:34:04208 static_cast<size_t>(std::numeric_limits<int>::max()) - kSectionMask) {
bcwhite8ea07912016-11-09 23:38:26209 LogError(SIZE_TOO_LARGE, 0);
[email protected]374f1a82013-01-10 02:16:24210 return false;
erikchen1d7cb7e2016-05-20 23:34:04211 }
[email protected]374f1a82013-01-10 02:16:24212
[email protected]67ea5072013-03-28 02:02:18213 size_t rounded_size = (options.size + kSectionMask) & ~kSectionMask;
thestig8badc792014-12-04 22:14:22214 name_ = options.name_deprecated ?
215 ASCIIToUTF16(*options.name_deprecated) : L"";
[email protected]6d6797eb2014-08-07 22:07:43216 SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, FALSE };
217 SECURITY_DESCRIPTOR sd;
218 ACL dacl;
219
erikchenb5856b12016-05-24 17:21:59220 if (name_.empty()) {
[email protected]6d6797eb2014-08-07 22:07:43221 // Add an empty DACL to enforce anonymous read-only sections.
222 sa.lpSecurityDescriptor = &sd;
erikchen1d7cb7e2016-05-20 23:34:04223 if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) {
bcwhite8ea07912016-11-09 23:38:26224 LogError(INITIALIZE_ACL_FAILURE, GetLastError());
[email protected]6d6797eb2014-08-07 22:07:43225 return false;
erikchen1d7cb7e2016-05-20 23:34:04226 }
227 if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
bcwhite8ea07912016-11-09 23:38:26228 LogError(INITIALIZE_SECURITY_DESC_FAILURE, GetLastError());
[email protected]6d6797eb2014-08-07 22:07:43229 return false;
erikchen1d7cb7e2016-05-20 23:34:04230 }
231 if (!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE)) {
bcwhite8ea07912016-11-09 23:38:26232 LogError(SET_SECURITY_DESC_FAILURE, GetLastError());
[email protected]6d6797eb2014-08-07 22:07:43233 return false;
erikchen1d7cb7e2016-05-20 23:34:04234 }
[email protected]6d6797eb2014-08-07 22:07:43235
[email protected]dc84fcc2014-07-24 11:42:59236 // Windows ignores DACLs on certain unnamed objects (like shared sections).
237 // So, we generate a random name when we need to enforce read-only.
238 uint64_t rand_values[4];
thestig8badc792014-12-04 22:14:22239 RandBytes(&rand_values, sizeof(rand_values));
brucedawson5604a11d2015-10-06 19:22:00240 name_ = StringPrintf(L"CrSharedMem_%016llx%016llx%016llx%016llx",
thestig8badc792014-12-04 22:14:22241 rand_values[0], rand_values[1],
242 rand_values[2], rand_values[3]);
[email protected]dc84fcc2014-07-24 11:42:59243 }
hajimehoshidf47edd2017-03-02 16:48:12244 DCHECK(!name_.empty());
245 mapped_file_.Set(CreateFileMappingWithReducedPermissions(&sa, rounded_size,
246 name_.c_str()));
stanisc2660facb2016-06-30 03:47:47247 if (!mapped_file_.IsValid()) {
erikchen1d7cb7e2016-05-20 23:34:04248 // The error is logged within CreateFileMappingWithReducedPermissions().
initial.commitd7cae122008-07-26 21:49:38249 return false;
erikchen1d7cb7e2016-05-20 23:34:04250 }
initial.commitd7cae122008-07-26 21:49:38251
[email protected]67ea5072013-03-28 02:02:18252 requested_size_ = options.size;
[email protected]54e3dfa22010-10-27 18:16:06253
initial.commitd7cae122008-07-26 21:49:38254 // Check if the shared memory pre-exists.
[email protected]54e3dfa22010-10-27 18:16:06255 if (GetLastError() == ERROR_ALREADY_EXISTS) {
[email protected]67ea5072013-03-28 02:02:18256 // If the file already existed, set requested_size_ to 0 to show that
[email protected]54e3dfa22010-10-27 18:16:06257 // we don't know the size.
[email protected]67ea5072013-03-28 02:02:18258 requested_size_ = 0;
forshaw0474abe2015-12-18 02:16:59259 external_section_ = true;
[email protected]ff672b72014-03-05 21:13:52260 if (!options.open_existing_deprecated) {
[email protected]54e3dfa22010-10-27 18:16:06261 Close();
bcwhite8ea07912016-11-09 23:38:26262 // From "if" above: GetLastError() == ERROR_ALREADY_EXISTS.
263 LogError(ALREADY_EXISTS, ERROR_ALREADY_EXISTS);
[email protected]54e3dfa22010-10-27 18:16:06264 return false;
265 }
initial.commitd7cae122008-07-26 21:49:38266 }
[email protected]54e3dfa22010-10-27 18:16:06267
bcwhite8ea07912016-11-09 23:38:26268 LogError(SUCCESS, ERROR_SUCCESS);
initial.commitd7cae122008-07-26 21:49:38269 return true;
270}
271
[email protected]b6413b49b2010-09-29 20:32:22272bool SharedMemory::Delete(const std::string& name) {
[email protected]9e51af92009-02-04 00:58:39273 // intentionally empty -- there is nothing for us to do on Windows.
274 return true;
275}
276
[email protected]b6413b49b2010-09-29 20:32:22277bool SharedMemory::Open(const std::string& name, bool read_only) {
stanisc2660facb2016-06-30 03:47:47278 DCHECK(!mapped_file_.Get());
forshaw0474abe2015-12-18 02:16:59279 DWORD access = FILE_MAP_READ | SECTION_QUERY;
280 if (!read_only)
281 access |= FILE_MAP_WRITE;
thestig8badc792014-12-04 22:14:22282 name_ = ASCIIToUTF16(name);
initial.commitd7cae122008-07-26 21:49:38283 read_only_ = read_only;
stanisc2660facb2016-06-30 03:47:47284 mapped_file_.Set(
285 OpenFileMapping(access, false, name_.empty() ? nullptr : name_.c_str()));
286 if (!mapped_file_.IsValid())
forshaw0474abe2015-12-18 02:16:59287 return false;
288 // If a name specified assume it's an external section.
289 if (!name_.empty())
290 external_section_ = true;
291 // Note: size_ is not set in this case.
292 return true;
initial.commitd7cae122008-07-26 21:49:38293}
294
[email protected]e29e3f552013-01-16 09:02:34295bool SharedMemory::MapAt(off_t offset, size_t bytes) {
stanisc2660facb2016-06-30 03:47:47296 if (!mapped_file_.Get())
initial.commitd7cae122008-07-26 21:49:38297 return false;
298
[email protected]374f1a82013-01-10 02:16:24299 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
300 return false;
301
[email protected]421c1502014-03-18 22:33:28302 if (memory_)
303 return false;
304
stanisc2660facb2016-06-30 03:47:47305 if (external_section_ && !IsSectionSafeToMap(mapped_file_.Get()))
forshaw0474abe2015-12-18 02:16:59306 return false;
307
avi9beac252015-12-24 08:44:47308 memory_ = MapViewOfFile(
stanisc2660facb2016-06-30 03:47:47309 mapped_file_.Get(),
310 read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE,
avi9beac252015-12-24 08:44:47311 static_cast<uint64_t>(offset) >> 32, static_cast<DWORD>(offset), bytes);
initial.commitd7cae122008-07-26 21:49:38312 if (memory_ != NULL) {
[email protected]404a0582012-08-18 02:17:26313 DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
314 (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
[email protected]67ea5072013-03-28 02:02:18315 mapped_size_ = GetMemorySectionSize(memory_);
initial.commitd7cae122008-07-26 21:49:38316 return true;
317 }
318 return false;
319}
320
321bool SharedMemory::Unmap() {
322 if (memory_ == NULL)
323 return false;
324
325 UnmapViewOfFile(memory_);
326 memory_ = NULL;
327 return true;
328}
329
erikchenc87903e2017-05-02 19:05:01330SharedMemoryHandle SharedMemory::GetReadOnlyHandle() {
331 HANDLE result;
332 ProcessHandle process = GetCurrentProcess();
333 if (!::DuplicateHandle(process, mapped_file_.Get(), process, &result,
334 FILE_MAP_READ | SECTION_QUERY, FALSE, 0)) {
335 return SharedMemoryHandle();
336 }
337 SharedMemoryHandle handle =
338 SharedMemoryHandle(result, base::GetProcId(process));
339 handle.SetOwnershipPassesToIPC(true);
340 return handle;
341}
342
initial.commitd7cae122008-07-26 21:49:38343bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
erikchen63840882017-05-02 20:52:31344 SharedMemoryHandle* new_handle) {
erikchen5ea2ab72015-09-25 22:34:31345 *new_handle = SharedMemoryHandle();
forshaw0474abe2015-12-18 02:16:59346 DWORD access = FILE_MAP_READ | SECTION_QUERY;
initial.commitd7cae122008-07-26 21:49:38347 DWORD options = 0;
stanisc2660facb2016-06-30 03:47:47348 HANDLE mapped_file = mapped_file_.Get();
initial.commitd7cae122008-07-26 21:49:38349 HANDLE result;
erikchenc87903e2017-05-02 19:05:01350 if (!read_only_)
initial.commitd7cae122008-07-26 21:49:38351 access |= FILE_MAP_WRITE;
initial.commitd7cae122008-07-26 21:49:38352
erikchen2096f622015-06-03 00:26:59353 if (!::DuplicateHandle(GetCurrentProcess(), mapped_file, process, &result,
354 access, FALSE, options)) {
initial.commitd7cae122008-07-26 21:49:38355 return false;
erikchen2096f622015-06-03 00:26:59356 }
erikchen5ea2ab72015-09-25 22:34:31357 *new_handle = SharedMemoryHandle(result, base::GetProcId(process));
erikchen71bc3b22016-02-01 22:14:28358 new_handle->SetOwnershipPassesToIPC(true);
initial.commitd7cae122008-07-26 21:49:38359 return true;
360}
361
362
363void SharedMemory::Close() {
stanisc2660facb2016-06-30 03:47:47364 mapped_file_.Close();
initial.commitd7cae122008-07-26 21:49:38365}
366
[email protected]5fe733de2009-02-11 18:59:20367SharedMemoryHandle SharedMemory::handle() const {
stanisc2660facb2016-06-30 03:47:47368 return SharedMemoryHandle(mapped_file_.Get(), base::GetCurrentProcId());
[email protected]5fe733de2009-02-11 18:59:20369}
370
sadrulf08f1e4a2016-11-15 00:40:02371SharedMemoryHandle SharedMemory::TakeHandle() {
372 SharedMemoryHandle handle(mapped_file_.Take(), base::GetCurrentProcId());
373 handle.SetOwnershipPassesToIPC(true);
374 memory_ = nullptr;
375 mapped_size_ = 0;
376 return handle;
377}
378
[email protected]176aa482008-11-14 03:25:15379} // namespace base