blob: 280bc4e10c518f483d2a6948b89db200974e74d8 [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"
hajimehoshie80c5762017-05-26 05:49:3612#include "base/memory/shared_memory_tracker.h"
erikchen1d7cb7e2016-05-20 23:34:0413#include "base/metrics/histogram_macros.h"
[email protected]dc84fcc2014-07-24 11:42:5914#include "base/rand_util.h"
15#include "base/strings/stringprintf.h"
[email protected]a4ea1f12013-06-07 18:37:0716#include "base/strings/utf_string_conversions.h"
erikchen14525202017-05-06 19:16:5117#include "base/unguessable_token.h"
initial.commitd7cae122008-07-26 21:49:3818
[email protected]67ea5072013-03-28 02:02:1819namespace {
20
erikchen1d7cb7e2016-05-20 23:34:0421// Errors that can occur during Shared Memory construction.
22// These match tools/metrics/histograms/histograms.xml.
23// This enum is append-only.
24enum CreateError {
25 SUCCESS = 0,
26 SIZE_ZERO = 1,
27 SIZE_TOO_LARGE = 2,
28 INITIALIZE_ACL_FAILURE = 3,
29 INITIALIZE_SECURITY_DESC_FAILURE = 4,
30 SET_SECURITY_DESC_FAILURE = 5,
31 CREATE_FILE_MAPPING_FAILURE = 6,
32 REDUCE_PERMISSIONS_FAILURE = 7,
33 ALREADY_EXISTS = 8,
34 CREATE_ERROR_LAST = ALREADY_EXISTS
35};
36
bcwhite8ea07912016-11-09 23:38:2637// Emits UMA metrics about encountered errors. Pass zero (0) for |winerror|
38// if there is no associated Windows error.
39void LogError(CreateError error, DWORD winerror) {
erikchen1d7cb7e2016-05-20 23:34:0440 UMA_HISTOGRAM_ENUMERATION("SharedMemory.CreateError", error,
41 CREATE_ERROR_LAST + 1);
bcwhite8ea07912016-11-09 23:38:2642 static_assert(ERROR_SUCCESS == 0, "Windows error code changed!");
43 if (winerror != ERROR_SUCCESS)
44 UMA_HISTOGRAM_SPARSE_SLOWLY("SharedMemory.CreateWinError", winerror);
erikchen1d7cb7e2016-05-20 23:34:0445}
46
forshaw0474abe2015-12-18 02:16:5947typedef enum _SECTION_INFORMATION_CLASS {
48 SectionBasicInformation,
49} SECTION_INFORMATION_CLASS;
50
51typedef struct _SECTION_BASIC_INFORMATION {
52 PVOID BaseAddress;
53 ULONG Attributes;
54 LARGE_INTEGER Size;
55} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
56
57typedef ULONG(__stdcall* NtQuerySectionType)(
58 HANDLE SectionHandle,
59 SECTION_INFORMATION_CLASS SectionInformationClass,
60 PVOID SectionInformation,
61 ULONG SectionInformationLength,
62 PULONG ResultLength);
63
[email protected]67ea5072013-03-28 02:02:1864// Returns the length of the memory section starting at the supplied address.
65size_t GetMemorySectionSize(void* address) {
66 MEMORY_BASIC_INFORMATION memory_info;
67 if (!::VirtualQuery(address, &memory_info, sizeof(memory_info)))
68 return 0;
69 return memory_info.RegionSize - (static_cast<char*>(address) -
70 static_cast<char*>(memory_info.AllocationBase));
71}
72
forshaw0474abe2015-12-18 02:16:5973// Checks if the section object is safe to map. At the moment this just means
74// it's not an image section.
75bool IsSectionSafeToMap(HANDLE handle) {
76 static NtQuerySectionType nt_query_section_func;
77 if (!nt_query_section_func) {
78 nt_query_section_func = reinterpret_cast<NtQuerySectionType>(
79 ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection"));
80 DCHECK(nt_query_section_func);
81 }
82
83 // The handle must have SECTION_QUERY access for this to succeed.
84 SECTION_BASIC_INFORMATION basic_information = {};
85 ULONG status =
86 nt_query_section_func(handle, SectionBasicInformation, &basic_information,
87 sizeof(basic_information), nullptr);
88 if (status)
89 return false;
90 return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE;
91}
92
erikchen4b12c0a2016-02-19 03:15:4393// Returns a HANDLE on success and |nullptr| on failure.
94// This function is similar to CreateFileMapping, but removes the permissions
95// WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE.
96//
97// A newly created file mapping has two sets of permissions. It has access
98// control permissions (WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE) and
99// file permissions (FILE_MAP_READ, FILE_MAP_WRITE, etc.). ::DuplicateHandle()
100// with the parameter DUPLICATE_SAME_ACCESS copies both sets of permissions.
101//
102// The Chrome sandbox prevents HANDLEs with the WRITE_DAC permission from being
103// duplicated into unprivileged processes. But the only way to copy file
104// permissions is with the parameter DUPLICATE_SAME_ACCESS. This means that
105// there is no way for a privileged process to duplicate a file mapping into an
106// unprivileged process while maintaining the previous file permissions.
107//
108// By removing all access control permissions of a file mapping immediately
109// after creation, ::DuplicateHandle() effectively only copies the file
110// permissions.
111HANDLE CreateFileMappingWithReducedPermissions(SECURITY_ATTRIBUTES* sa,
112 size_t rounded_size,
113 LPCWSTR name) {
114 HANDLE h = CreateFileMapping(INVALID_HANDLE_VALUE, sa, PAGE_READWRITE, 0,
115 static_cast<DWORD>(rounded_size), name);
erikchen1d7cb7e2016-05-20 23:34:04116 if (!h) {
bcwhite8ea07912016-11-09 23:38:26117 LogError(CREATE_FILE_MAPPING_FAILURE, GetLastError());
erikchen4b12c0a2016-02-19 03:15:43118 return nullptr;
erikchen1d7cb7e2016-05-20 23:34:04119 }
erikchen4b12c0a2016-02-19 03:15:43120
121 HANDLE h2;
122 BOOL success = ::DuplicateHandle(
123 GetCurrentProcess(), h, GetCurrentProcess(), &h2,
124 FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY, FALSE, 0);
125 BOOL rv = ::CloseHandle(h);
126 DCHECK(rv);
erikchen1d7cb7e2016-05-20 23:34:04127
128 if (!success) {
bcwhite8ea07912016-11-09 23:38:26129 LogError(REDUCE_PERMISSIONS_FAILURE, GetLastError());
erikchen1d7cb7e2016-05-20 23:34:04130 return nullptr;
131 }
132
133 return h2;
erikchen4b12c0a2016-02-19 03:15:43134}
135
[email protected]67ea5072013-03-28 02:02:18136} // namespace.
137
[email protected]176aa482008-11-14 03:25:15138namespace base {
139
asvitkine182427f2017-05-10 20:06:18140SharedMemory::SharedMemory() {}
forshaw0474abe2015-12-18 02:16:59141
asvitkine182427f2017-05-10 20:06:18142SharedMemory::SharedMemory(const string16& name) : name_(name) {}
[email protected]8cc41942010-11-05 19:16:07143
scottmgd19b4f72015-06-19 22:51:00144SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only)
asvitkine182427f2017-05-10 20:06:18145 : external_section_(true), shm_(handle), read_only_(read_only) {}
initial.commitd7cae122008-07-26 21:49:38146
initial.commitd7cae122008-07-26 21:49:38147SharedMemory::~SharedMemory() {
jbauman569918b2014-12-10 22:07:20148 Unmap();
initial.commitd7cae122008-07-26 21:49:38149 Close();
initial.commitd7cae122008-07-26 21:49:38150}
151
[email protected]5fe733de2009-02-11 18:59:20152// static
153bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
erikchen5ea2ab72015-09-25 22:34:31154 return handle.IsValid();
[email protected]5fe733de2009-02-11 18:59:20155}
156
[email protected]76aac1e2009-03-16 16:45:36157// static
[email protected]b0af04c2009-05-18 17:46:31158void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
erikchen5ea2ab72015-09-25 22:34:31159 handle.Close();
[email protected]b0af04c2009-05-18 17:46:31160}
161
[email protected]c14eda92013-05-09 23:15:40162// static
163size_t SharedMemory::GetHandleLimit() {
164 // Rounded down from value reported here:
165 // http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx
166 return static_cast<size_t>(1 << 23);
167}
168
erikchen2096f622015-06-03 00:26:59169// static
170SharedMemoryHandle SharedMemory::DuplicateHandle(
erikchen8539d852015-05-30 01:49:19171 const SharedMemoryHandle& handle) {
erikchen63840882017-05-02 20:52:31172 return handle.Duplicate();
erikchen8539d852015-05-30 01:49:19173}
174
[email protected]374f1a82013-01-10 02:16:24175bool SharedMemory::CreateAndMapAnonymous(size_t size) {
[email protected]54e3dfa22010-10-27 18:16:06176 return CreateAnonymous(size) && Map(size);
177}
178
[email protected]b05df6b2011-12-01 23:19:31179bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
[email protected]67ea5072013-03-28 02:02:18180 // TODO(bsy,sehr): crbug.com/210609 NaCl forces us to round up 64k here,
181 // wasting 32k per mapping on average.
182 static const size_t kSectionMask = 65536 - 1;
[email protected]b05df6b2011-12-01 23:19:31183 DCHECK(!options.executable);
erikchen3df1dd52017-05-03 22:53:40184 DCHECK(!shm_.IsValid());
erikchen1d7cb7e2016-05-20 23:34:04185 if (options.size == 0) {
bcwhite8ea07912016-11-09 23:38:26186 LogError(SIZE_ZERO, 0);
[email protected]54e3dfa22010-10-27 18:16:06187 return false;
erikchen1d7cb7e2016-05-20 23:34:04188 }
initial.commitd7cae122008-07-26 21:49:38189
[email protected]67ea5072013-03-28 02:02:18190 // Check maximum accounting for overflow.
191 if (options.size >
erikchen1d7cb7e2016-05-20 23:34:04192 static_cast<size_t>(std::numeric_limits<int>::max()) - kSectionMask) {
bcwhite8ea07912016-11-09 23:38:26193 LogError(SIZE_TOO_LARGE, 0);
[email protected]374f1a82013-01-10 02:16:24194 return false;
erikchen1d7cb7e2016-05-20 23:34:04195 }
[email protected]374f1a82013-01-10 02:16:24196
[email protected]67ea5072013-03-28 02:02:18197 size_t rounded_size = (options.size + kSectionMask) & ~kSectionMask;
thestig8badc792014-12-04 22:14:22198 name_ = options.name_deprecated ?
199 ASCIIToUTF16(*options.name_deprecated) : L"";
[email protected]6d6797eb2014-08-07 22:07:43200 SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, FALSE };
201 SECURITY_DESCRIPTOR sd;
202 ACL dacl;
203
erikchenb5856b12016-05-24 17:21:59204 if (name_.empty()) {
[email protected]6d6797eb2014-08-07 22:07:43205 // Add an empty DACL to enforce anonymous read-only sections.
206 sa.lpSecurityDescriptor = &sd;
erikchen1d7cb7e2016-05-20 23:34:04207 if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) {
bcwhite8ea07912016-11-09 23:38:26208 LogError(INITIALIZE_ACL_FAILURE, GetLastError());
[email protected]6d6797eb2014-08-07 22:07:43209 return false;
erikchen1d7cb7e2016-05-20 23:34:04210 }
211 if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
bcwhite8ea07912016-11-09 23:38:26212 LogError(INITIALIZE_SECURITY_DESC_FAILURE, GetLastError());
[email protected]6d6797eb2014-08-07 22:07:43213 return false;
erikchen1d7cb7e2016-05-20 23:34:04214 }
215 if (!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE)) {
bcwhite8ea07912016-11-09 23:38:26216 LogError(SET_SECURITY_DESC_FAILURE, GetLastError());
[email protected]6d6797eb2014-08-07 22:07:43217 return false;
erikchen1d7cb7e2016-05-20 23:34:04218 }
[email protected]6d6797eb2014-08-07 22:07:43219
[email protected]dc84fcc2014-07-24 11:42:59220 // Windows ignores DACLs on certain unnamed objects (like shared sections).
221 // So, we generate a random name when we need to enforce read-only.
222 uint64_t rand_values[4];
thestig8badc792014-12-04 22:14:22223 RandBytes(&rand_values, sizeof(rand_values));
brucedawson5604a11d2015-10-06 19:22:00224 name_ = StringPrintf(L"CrSharedMem_%016llx%016llx%016llx%016llx",
thestig8badc792014-12-04 22:14:22225 rand_values[0], rand_values[1],
226 rand_values[2], rand_values[3]);
[email protected]dc84fcc2014-07-24 11:42:59227 }
hajimehoshidf47edd2017-03-02 16:48:12228 DCHECK(!name_.empty());
erikchen14525202017-05-06 19:16:51229 shm_ = SharedMemoryHandle(
230 CreateFileMappingWithReducedPermissions(&sa, rounded_size, name_.c_str()),
erikchen9d6afd712017-05-18 17:49:06231 rounded_size, UnguessableToken::Create());
erikchen3df1dd52017-05-03 22:53:40232 if (!shm_.IsValid()) {
erikchen1d7cb7e2016-05-20 23:34:04233 // The error is logged within CreateFileMappingWithReducedPermissions().
initial.commitd7cae122008-07-26 21:49:38234 return false;
erikchen1d7cb7e2016-05-20 23:34:04235 }
initial.commitd7cae122008-07-26 21:49:38236
[email protected]67ea5072013-03-28 02:02:18237 requested_size_ = options.size;
[email protected]54e3dfa22010-10-27 18:16:06238
initial.commitd7cae122008-07-26 21:49:38239 // Check if the shared memory pre-exists.
[email protected]54e3dfa22010-10-27 18:16:06240 if (GetLastError() == ERROR_ALREADY_EXISTS) {
[email protected]67ea5072013-03-28 02:02:18241 // If the file already existed, set requested_size_ to 0 to show that
[email protected]54e3dfa22010-10-27 18:16:06242 // we don't know the size.
[email protected]67ea5072013-03-28 02:02:18243 requested_size_ = 0;
forshaw0474abe2015-12-18 02:16:59244 external_section_ = true;
[email protected]ff672b72014-03-05 21:13:52245 if (!options.open_existing_deprecated) {
[email protected]54e3dfa22010-10-27 18:16:06246 Close();
bcwhite8ea07912016-11-09 23:38:26247 // From "if" above: GetLastError() == ERROR_ALREADY_EXISTS.
248 LogError(ALREADY_EXISTS, ERROR_ALREADY_EXISTS);
[email protected]54e3dfa22010-10-27 18:16:06249 return false;
250 }
initial.commitd7cae122008-07-26 21:49:38251 }
[email protected]54e3dfa22010-10-27 18:16:06252
bcwhite8ea07912016-11-09 23:38:26253 LogError(SUCCESS, ERROR_SUCCESS);
initial.commitd7cae122008-07-26 21:49:38254 return true;
255}
256
[email protected]b6413b49b2010-09-29 20:32:22257bool SharedMemory::Delete(const std::string& name) {
[email protected]9e51af92009-02-04 00:58:39258 // intentionally empty -- there is nothing for us to do on Windows.
259 return true;
260}
261
[email protected]b6413b49b2010-09-29 20:32:22262bool SharedMemory::Open(const std::string& name, bool read_only) {
erikchen3df1dd52017-05-03 22:53:40263 DCHECK(!shm_.IsValid());
forshaw0474abe2015-12-18 02:16:59264 DWORD access = FILE_MAP_READ | SECTION_QUERY;
265 if (!read_only)
266 access |= FILE_MAP_WRITE;
thestig8badc792014-12-04 22:14:22267 name_ = ASCIIToUTF16(name);
initial.commitd7cae122008-07-26 21:49:38268 read_only_ = read_only;
erikchen14525202017-05-06 19:16:51269
270 // This form of sharing shared memory is deprecated. https://crbug.com/345734.
271 // However, we can't get rid of it without a significant refactor because its
272 // used to communicate between two versions of the same service process, very
273 // early in the life cycle.
274 // Technically, we should also pass the GUID from the original shared memory
275 // region. We don't do that - this means that we will overcount this memory,
276 // which thankfully isn't relevant since Chrome only communicates with a
277 // single version of the service process.
erikchen9d6afd712017-05-18 17:49:06278 // We pass the size |0|, which is a dummy size and wrong, but otherwise
279 // harmless.
erikchen3df1dd52017-05-03 22:53:40280 shm_ = SharedMemoryHandle(
erikchen14525202017-05-06 19:16:51281 OpenFileMapping(access, false, name_.empty() ? nullptr : name_.c_str()),
erikchen9d6afd712017-05-18 17:49:06282 0u, UnguessableToken::Create());
erikchen3df1dd52017-05-03 22:53:40283 if (!shm_.IsValid())
forshaw0474abe2015-12-18 02:16:59284 return false;
285 // If a name specified assume it's an external section.
286 if (!name_.empty())
287 external_section_ = true;
288 // Note: size_ is not set in this case.
289 return true;
initial.commitd7cae122008-07-26 21:49:38290}
291
[email protected]e29e3f552013-01-16 09:02:34292bool SharedMemory::MapAt(off_t offset, size_t bytes) {
Zijie Heacbe01b2017-10-06 20:24:56293 if (!shm_.IsValid()) {
294 DLOG(ERROR) << "Invalid SharedMemoryHandle.";
initial.commitd7cae122008-07-26 21:49:38295 return false;
Zijie Heacbe01b2017-10-06 20:24:56296 }
initial.commitd7cae122008-07-26 21:49:38297
Zijie Heacbe01b2017-10-06 20:24:56298 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) {
299 DLOG(ERROR) << "Bytes required exceeds the 2G limitation.";
[email protected]374f1a82013-01-10 02:16:24300 return false;
Zijie Heacbe01b2017-10-06 20:24:56301 }
[email protected]374f1a82013-01-10 02:16:24302
Zijie Heacbe01b2017-10-06 20:24:56303 if (memory_) {
304 DLOG(ERROR) << "The SharedMemory has been mapped already.";
[email protected]421c1502014-03-18 22:33:28305 return false;
Zijie Heacbe01b2017-10-06 20:24:56306 }
[email protected]421c1502014-03-18 22:33:28307
Zijie Heacbe01b2017-10-06 20:24:56308 if (external_section_ && !IsSectionSafeToMap(shm_.GetHandle())) {
309 DLOG(ERROR) << "SharedMemoryHandle is not safe to be mapped.";
forshaw0474abe2015-12-18 02:16:59310 return false;
Zijie Heacbe01b2017-10-06 20:24:56311 }
forshaw0474abe2015-12-18 02:16:59312
avi9beac252015-12-24 08:44:47313 memory_ = MapViewOfFile(
erikchen3df1dd52017-05-03 22:53:40314 shm_.GetHandle(),
stanisc2660facb2016-06-30 03:47:47315 read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE,
avi9beac252015-12-24 08:44:47316 static_cast<uint64_t>(offset) >> 32, static_cast<DWORD>(offset), bytes);
initial.commitd7cae122008-07-26 21:49:38317 if (memory_ != NULL) {
[email protected]404a0582012-08-18 02:17:26318 DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
319 (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
[email protected]67ea5072013-03-28 02:02:18320 mapped_size_ = GetMemorySectionSize(memory_);
Hajime Hoshi5d644332017-07-13 14:50:22321 mapped_id_ = shm_.GetGUID();
hajimehoshie80c5762017-05-26 05:49:36322 SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this);
initial.commitd7cae122008-07-26 21:49:38323 return true;
324 }
Zijie Heacbe01b2017-10-06 20:24:56325 DPLOG(ERROR) << "Failed executing MapViewOfFile";
initial.commitd7cae122008-07-26 21:49:38326 return false;
327}
328
329bool SharedMemory::Unmap() {
330 if (memory_ == NULL)
331 return false;
332
hajimehoshie80c5762017-05-26 05:49:36333 SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this);
initial.commitd7cae122008-07-26 21:49:38334 UnmapViewOfFile(memory_);
335 memory_ = NULL;
Hajime Hoshi5d644332017-07-13 14:50:22336 mapped_id_ = UnguessableToken();
initial.commitd7cae122008-07-26 21:49:38337 return true;
338}
339
erikchenc87903e2017-05-02 19:05:01340SharedMemoryHandle SharedMemory::GetReadOnlyHandle() {
341 HANDLE result;
342 ProcessHandle process = GetCurrentProcess();
erikchen3df1dd52017-05-03 22:53:40343 if (!::DuplicateHandle(process, shm_.GetHandle(), process, &result,
erikchenc87903e2017-05-02 19:05:01344 FILE_MAP_READ | SECTION_QUERY, FALSE, 0)) {
345 return SharedMemoryHandle();
346 }
erikchen9d6afd712017-05-18 17:49:06347 SharedMemoryHandle handle =
348 SharedMemoryHandle(result, shm_.GetSize(), shm_.GetGUID());
erikchenc87903e2017-05-02 19:05:01349 handle.SetOwnershipPassesToIPC(true);
350 return handle;
351}
352
initial.commitd7cae122008-07-26 21:49:38353void SharedMemory::Close() {
erikchen3df1dd52017-05-03 22:53:40354 if (shm_.IsValid()) {
355 shm_.Close();
356 shm_ = SharedMemoryHandle();
357 }
initial.commitd7cae122008-07-26 21:49:38358}
359
[email protected]5fe733de2009-02-11 18:59:20360SharedMemoryHandle SharedMemory::handle() const {
erikchen3df1dd52017-05-03 22:53:40361 return shm_;
[email protected]5fe733de2009-02-11 18:59:20362}
363
sadrulf08f1e4a2016-11-15 00:40:02364SharedMemoryHandle SharedMemory::TakeHandle() {
erikchen3df1dd52017-05-03 22:53:40365 SharedMemoryHandle handle(shm_);
sadrulf08f1e4a2016-11-15 00:40:02366 handle.SetOwnershipPassesToIPC(true);
erikchen3df1dd52017-05-03 22:53:40367 shm_ = SharedMemoryHandle();
sadrulf08f1e4a2016-11-15 00:40:02368 memory_ = nullptr;
369 mapped_size_ = 0;
370 return handle;
371}
372
[email protected]176aa482008-11-14 03:25:15373} // namespace base