1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/memory/shared_memory.h"
6
7 #include <mach/mach_vm.h>
8
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_file.h"
11 #include "base/logging.h"
12 #include "base/mac/foundation_util.h"
13 #include "base/mac/mac_util.h"
14 #include "base/mac/scoped_mach_vm.h"
15 #include "base/metrics/field_trial.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/process/process_metrics.h"
18 #include "base/profiler/scoped_tracker.h"
19 #include "base/scoped_generic.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "build/build_config.h"
22
23 namespace base {
24
25 namespace {
26
27 // Returns whether the operation succeeded.
28 // |new_handle| is an output variable, populated on success. The caller takes
29 // ownership of the underlying memory object.
30 // |handle| is the handle to copy.
31 // If |handle| is already mapped, |mapped_addr| is its mapped location.
32 // Otherwise, |mapped_addr| should be |nullptr|.
MakeMachSharedMemoryHandleReadOnly(SharedMemoryHandle * new_handle,SharedMemoryHandle handle,void * mapped_addr)33 bool MakeMachSharedMemoryHandleReadOnly(SharedMemoryHandle* new_handle,
34 SharedMemoryHandle handle,
35 void* mapped_addr) {
36 if (!handle.IsValid())
37 return false;
38
39 size_t size;
40 CHECK(handle.GetSize(&size));
41
42 // Map if necessary.
43 void* temp_addr = mapped_addr;
44 base::mac::ScopedMachVM scoper;
45 if (!temp_addr) {
46 // Intentionally lower current prot and max prot to |VM_PROT_READ|.
47 kern_return_t kr = mach_vm_map(
48 mach_task_self(), reinterpret_cast<mach_vm_address_t*>(&temp_addr),
49 size, 0, VM_FLAGS_ANYWHERE, handle.GetMemoryObject(), 0, FALSE,
50 VM_PROT_READ, VM_PROT_READ, VM_INHERIT_NONE);
51 if (kr != KERN_SUCCESS)
52 return false;
53 scoper.reset(reinterpret_cast<vm_address_t>(temp_addr),
54 mach_vm_round_page(size));
55 }
56
57 // Make new memory object.
58 mach_port_t named_right;
59 kern_return_t kr = mach_make_memory_entry_64(
60 mach_task_self(), reinterpret_cast<memory_object_size_t*>(&size),
61 reinterpret_cast<memory_object_offset_t>(temp_addr), VM_PROT_READ,
62 &named_right, MACH_PORT_NULL);
63 if (kr != KERN_SUCCESS)
64 return false;
65
66 *new_handle = SharedMemoryHandle(named_right, size, base::GetCurrentProcId());
67 return true;
68 }
69
70 } // namespace
71
SharedMemoryCreateOptions()72 SharedMemoryCreateOptions::SharedMemoryCreateOptions()
73 : size(0),
74 executable(false),
75 share_read_only(false) {}
76
SharedMemory()77 SharedMemory::SharedMemory()
78 : mapped_size_(0), memory_(NULL), read_only_(false), requested_size_(0) {}
79
SharedMemory(const SharedMemoryHandle & handle,bool read_only)80 SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only)
81 : shm_(handle),
82 mapped_size_(0),
83 memory_(NULL),
84 read_only_(read_only),
85 requested_size_(0) {}
86
~SharedMemory()87 SharedMemory::~SharedMemory() {
88 Unmap();
89 Close();
90 }
91
92 // static
IsHandleValid(const SharedMemoryHandle & handle)93 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
94 return handle.IsValid();
95 }
96
97 // static
NULLHandle()98 SharedMemoryHandle SharedMemory::NULLHandle() {
99 return SharedMemoryHandle();
100 }
101
102 // static
CloseHandle(const SharedMemoryHandle & handle)103 void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
104 handle.Close();
105 }
106
107 // static
GetHandleLimit()108 size_t SharedMemory::GetHandleLimit() {
109 // This should be effectively unlimited on OS X.
110 return 10000;
111 }
112
113 // static
DuplicateHandle(const SharedMemoryHandle & handle)114 SharedMemoryHandle SharedMemory::DuplicateHandle(
115 const SharedMemoryHandle& handle) {
116 return handle.Duplicate();
117 }
118
CreateAndMapAnonymous(size_t size)119 bool SharedMemory::CreateAndMapAnonymous(size_t size) {
120 return CreateAnonymous(size) && Map(size);
121 }
122
123 // static
GetSizeFromSharedMemoryHandle(const SharedMemoryHandle & handle,size_t * size)124 bool SharedMemory::GetSizeFromSharedMemoryHandle(
125 const SharedMemoryHandle& handle,
126 size_t* size) {
127 return handle.GetSize(size);
128 }
129
130 // Chromium mostly only uses the unique/private shmem as specified by
131 // "name == L"". The exception is in the StatsTable.
Create(const SharedMemoryCreateOptions & options)132 bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
133 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
134 // is fixed.
135 tracked_objects::ScopedTracker tracking_profile1(
136 FROM_HERE_WITH_EXPLICIT_FUNCTION(
137 "466437 SharedMemory::Create::Start"));
138 DCHECK(!shm_.IsValid());
139 if (options.size == 0) return false;
140
141 if (options.size > static_cast<size_t>(std::numeric_limits<int>::max()))
142 return false;
143
144 shm_ = SharedMemoryHandle(options.size);
145 requested_size_ = options.size;
146 return shm_.IsValid();
147 }
148
MapAt(off_t offset,size_t bytes)149 bool SharedMemory::MapAt(off_t offset, size_t bytes) {
150 if (!shm_.IsValid())
151 return false;
152 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
153 return false;
154 if (memory_)
155 return false;
156
157 bool success = shm_.MapAt(offset, bytes, &memory_, read_only_);
158 if (success) {
159 mapped_size_ = bytes;
160 DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
161 (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
162 } else {
163 memory_ = NULL;
164 }
165
166 return success;
167 }
168
Unmap()169 bool SharedMemory::Unmap() {
170 if (memory_ == NULL)
171 return false;
172
173 mach_vm_deallocate(mach_task_self(),
174 reinterpret_cast<mach_vm_address_t>(memory_),
175 mapped_size_);
176 memory_ = NULL;
177 mapped_size_ = 0;
178 return true;
179 }
180
handle() const181 SharedMemoryHandle SharedMemory::handle() const {
182 return shm_;
183 }
184
Close()185 void SharedMemory::Close() {
186 shm_.Close();
187 shm_ = SharedMemoryHandle();
188 }
189
ShareToProcessCommon(ProcessHandle,SharedMemoryHandle * new_handle,bool close_self,ShareMode share_mode)190 bool SharedMemory::ShareToProcessCommon(ProcessHandle /*process*/,
191 SharedMemoryHandle* new_handle,
192 bool close_self,
193 ShareMode share_mode) {
194 DCHECK(shm_.IsValid());
195
196 bool success = false;
197 switch (share_mode) {
198 case SHARE_CURRENT_MODE:
199 *new_handle = shm_.Duplicate();
200 success = true;
201 break;
202 case SHARE_READONLY:
203 success = MakeMachSharedMemoryHandleReadOnly(new_handle, shm_, memory_);
204 break;
205 }
206
207 if (success)
208 new_handle->SetOwnershipPassesToIPC(true);
209
210 if (close_self) {
211 Unmap();
212 Close();
213 }
214
215 return success;
216 }
217
218 } // namespace base
219