1 // Copyright 2014 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 "mojo/edk/embedder/platform_shared_buffer.h"
6
7 #include <stddef.h>
8
9 #include <utility>
10
11 #include "base/logging.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/shared_memory.h"
14 #include "base/process/process_handle.h"
15 #include "base/sys_info.h"
16 #include "mojo/edk/embedder/platform_handle_utils.h"
17
18 #if defined(OS_NACL)
19 // For getpagesize() on NaCl.
20 #include <unistd.h>
21 #endif
22
23 namespace mojo {
24 namespace edk {
25
26 namespace {
27
28 // Takes ownership of |memory_handle|.
SharedMemoryToPlatformHandle(base::SharedMemoryHandle memory_handle)29 ScopedPlatformHandle SharedMemoryToPlatformHandle(
30 base::SharedMemoryHandle memory_handle) {
31 #if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
32 return ScopedPlatformHandle(PlatformHandle(memory_handle.fd));
33 #elif defined(OS_WIN)
34 return ScopedPlatformHandle(PlatformHandle(memory_handle.GetHandle()));
35 #else
36 return ScopedPlatformHandle(PlatformHandle(memory_handle.GetMemoryObject()));
37 #endif
38 }
39
40 } // namespace
41
42 // static
Create(size_t num_bytes)43 PlatformSharedBuffer* PlatformSharedBuffer::Create(size_t num_bytes) {
44 DCHECK_GT(num_bytes, 0u);
45
46 PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, false);
47 if (!rv->Init()) {
48 // We can't just delete it directly, due to the "in destructor" (debug)
49 // check.
50 scoped_refptr<PlatformSharedBuffer> deleter(rv);
51 return nullptr;
52 }
53
54 return rv;
55 }
56
57 // static
CreateFromPlatformHandle(size_t num_bytes,bool read_only,ScopedPlatformHandle platform_handle)58 PlatformSharedBuffer* PlatformSharedBuffer::CreateFromPlatformHandle(
59 size_t num_bytes,
60 bool read_only,
61 ScopedPlatformHandle platform_handle) {
62 DCHECK_GT(num_bytes, 0u);
63
64 PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, read_only);
65 if (!rv->InitFromPlatformHandle(std::move(platform_handle))) {
66 // We can't just delete it directly, due to the "in destructor" (debug)
67 // check.
68 scoped_refptr<PlatformSharedBuffer> deleter(rv);
69 return nullptr;
70 }
71
72 return rv;
73 }
74
75 // static
CreateFromPlatformHandlePair(size_t num_bytes,ScopedPlatformHandle rw_platform_handle,ScopedPlatformHandle ro_platform_handle)76 PlatformSharedBuffer* PlatformSharedBuffer::CreateFromPlatformHandlePair(
77 size_t num_bytes,
78 ScopedPlatformHandle rw_platform_handle,
79 ScopedPlatformHandle ro_platform_handle) {
80 DCHECK_GT(num_bytes, 0u);
81 DCHECK(rw_platform_handle.is_valid());
82 DCHECK(ro_platform_handle.is_valid());
83
84 PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, false);
85 if (!rv->InitFromPlatformHandlePair(std::move(rw_platform_handle),
86 std::move(ro_platform_handle))) {
87 // We can't just delete it directly, due to the "in destructor" (debug)
88 // check.
89 scoped_refptr<PlatformSharedBuffer> deleter(rv);
90 return nullptr;
91 }
92
93 return rv;
94 }
95
96 // static
CreateFromSharedMemoryHandle(size_t num_bytes,bool read_only,base::SharedMemoryHandle handle)97 PlatformSharedBuffer* PlatformSharedBuffer::CreateFromSharedMemoryHandle(
98 size_t num_bytes,
99 bool read_only,
100 base::SharedMemoryHandle handle) {
101 DCHECK_GT(num_bytes, 0u);
102
103 PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, read_only);
104 rv->InitFromSharedMemoryHandle(handle);
105
106 return rv;
107 }
108
GetNumBytes() const109 size_t PlatformSharedBuffer::GetNumBytes() const {
110 return num_bytes_;
111 }
112
IsReadOnly() const113 bool PlatformSharedBuffer::IsReadOnly() const {
114 return read_only_;
115 }
116
Map(size_t offset,size_t length)117 std::unique_ptr<PlatformSharedBufferMapping> PlatformSharedBuffer::Map(
118 size_t offset,
119 size_t length) {
120 if (!IsValidMap(offset, length))
121 return nullptr;
122
123 return MapNoCheck(offset, length);
124 }
125
IsValidMap(size_t offset,size_t length)126 bool PlatformSharedBuffer::IsValidMap(size_t offset, size_t length) {
127 if (offset > num_bytes_ || length == 0)
128 return false;
129
130 // Note: This is an overflow-safe check of |offset + length > num_bytes_|
131 // (that |num_bytes >= offset| is verified above).
132 if (length > num_bytes_ - offset)
133 return false;
134
135 return true;
136 }
137
MapNoCheck(size_t offset,size_t length)138 std::unique_ptr<PlatformSharedBufferMapping> PlatformSharedBuffer::MapNoCheck(
139 size_t offset,
140 size_t length) {
141 DCHECK(IsValidMap(offset, length));
142 DCHECK(shared_memory_);
143 base::SharedMemoryHandle handle;
144 {
145 base::AutoLock locker(lock_);
146 handle = base::SharedMemory::DuplicateHandle(shared_memory_->handle());
147 }
148 if (handle == base::SharedMemory::NULLHandle())
149 return nullptr;
150
151 std::unique_ptr<PlatformSharedBufferMapping> mapping(
152 new PlatformSharedBufferMapping(handle, read_only_, offset, length));
153 if (mapping->Map())
154 return base::WrapUnique(mapping.release());
155
156 return nullptr;
157 }
158
DuplicatePlatformHandle()159 ScopedPlatformHandle PlatformSharedBuffer::DuplicatePlatformHandle() {
160 DCHECK(shared_memory_);
161 base::SharedMemoryHandle handle;
162 {
163 base::AutoLock locker(lock_);
164 handle = base::SharedMemory::DuplicateHandle(shared_memory_->handle());
165 }
166 if (handle == base::SharedMemory::NULLHandle())
167 return ScopedPlatformHandle();
168
169 return SharedMemoryToPlatformHandle(handle);
170 }
171
PassPlatformHandle()172 ScopedPlatformHandle PlatformSharedBuffer::PassPlatformHandle() {
173 DCHECK(HasOneRef());
174
175 // The only way to pass a handle from base::SharedMemory is to duplicate it
176 // and close the original.
177 ScopedPlatformHandle handle = DuplicatePlatformHandle();
178
179 base::AutoLock locker(lock_);
180 shared_memory_->Close();
181 return handle;
182 }
183
DuplicateSharedMemoryHandle()184 base::SharedMemoryHandle PlatformSharedBuffer::DuplicateSharedMemoryHandle() {
185 DCHECK(shared_memory_);
186
187 base::AutoLock locker(lock_);
188 return base::SharedMemory::DuplicateHandle(shared_memory_->handle());
189 }
190
CreateReadOnlyDuplicate()191 PlatformSharedBuffer* PlatformSharedBuffer::CreateReadOnlyDuplicate() {
192 DCHECK(shared_memory_);
193
194 if (ro_shared_memory_) {
195 base::AutoLock locker(lock_);
196 base::SharedMemoryHandle handle;
197 handle = base::SharedMemory::DuplicateHandle(ro_shared_memory_->handle());
198 if (handle == base::SharedMemory::NULLHandle())
199 return nullptr;
200 return CreateFromSharedMemoryHandle(num_bytes_, true, handle);
201 }
202
203 base::SharedMemoryHandle handle;
204 bool success;
205 {
206 base::AutoLock locker(lock_);
207 success = shared_memory_->ShareReadOnlyToProcess(
208 base::GetCurrentProcessHandle(), &handle);
209 }
210 if (!success || handle == base::SharedMemory::NULLHandle())
211 return nullptr;
212
213 return CreateFromSharedMemoryHandle(num_bytes_, true, handle);
214 }
215
PlatformSharedBuffer(size_t num_bytes,bool read_only)216 PlatformSharedBuffer::PlatformSharedBuffer(size_t num_bytes, bool read_only)
217 : num_bytes_(num_bytes), read_only_(read_only) {}
218
~PlatformSharedBuffer()219 PlatformSharedBuffer::~PlatformSharedBuffer() {}
220
Init()221 bool PlatformSharedBuffer::Init() {
222 DCHECK(!shared_memory_);
223 DCHECK(!read_only_);
224
225 base::SharedMemoryCreateOptions options;
226 options.size = num_bytes_;
227 // By default, we can share as read-only.
228 options.share_read_only = true;
229
230 shared_memory_.reset(new base::SharedMemory);
231 return shared_memory_->Create(options);
232 }
233
InitFromPlatformHandle(ScopedPlatformHandle platform_handle)234 bool PlatformSharedBuffer::InitFromPlatformHandle(
235 ScopedPlatformHandle platform_handle) {
236 DCHECK(!shared_memory_);
237
238 #if defined(OS_WIN)
239 base::SharedMemoryHandle handle(platform_handle.release().handle,
240 base::GetCurrentProcId());
241 #elif defined(OS_MACOSX) && !defined(OS_IOS)
242 base::SharedMemoryHandle handle;
243 handle = base::SharedMemoryHandle(platform_handle.release().port, num_bytes_,
244 base::GetCurrentProcId());
245 #else
246 base::SharedMemoryHandle handle(platform_handle.release().handle, false);
247 #endif
248
249 shared_memory_.reset(new base::SharedMemory(handle, read_only_));
250 return true;
251 }
252
InitFromPlatformHandlePair(ScopedPlatformHandle rw_platform_handle,ScopedPlatformHandle ro_platform_handle)253 bool PlatformSharedBuffer::InitFromPlatformHandlePair(
254 ScopedPlatformHandle rw_platform_handle,
255 ScopedPlatformHandle ro_platform_handle) {
256 #if defined(OS_MACOSX)
257 NOTREACHED();
258 return false;
259 #else // defined(OS_MACOSX)
260
261 #if defined(OS_WIN)
262 base::SharedMemoryHandle handle(rw_platform_handle.release().handle,
263 base::GetCurrentProcId());
264 base::SharedMemoryHandle ro_handle(ro_platform_handle.release().handle,
265 base::GetCurrentProcId());
266 #else // defined(OS_WIN)
267 base::SharedMemoryHandle handle(rw_platform_handle.release().handle, false);
268 base::SharedMemoryHandle ro_handle(ro_platform_handle.release().handle,
269 false);
270 #endif // defined(OS_WIN)
271
272 DCHECK(!shared_memory_);
273 shared_memory_.reset(new base::SharedMemory(handle, false));
274 ro_shared_memory_.reset(new base::SharedMemory(ro_handle, true));
275 return true;
276
277 #endif // defined(OS_MACOSX)
278 }
279
InitFromSharedMemoryHandle(base::SharedMemoryHandle handle)280 void PlatformSharedBuffer::InitFromSharedMemoryHandle(
281 base::SharedMemoryHandle handle) {
282 DCHECK(!shared_memory_);
283
284 shared_memory_.reset(new base::SharedMemory(handle, read_only_));
285 }
286
~PlatformSharedBufferMapping()287 PlatformSharedBufferMapping::~PlatformSharedBufferMapping() {
288 Unmap();
289 }
290
GetBase() const291 void* PlatformSharedBufferMapping::GetBase() const {
292 return base_;
293 }
294
GetLength() const295 size_t PlatformSharedBufferMapping::GetLength() const {
296 return length_;
297 }
298
Map()299 bool PlatformSharedBufferMapping::Map() {
300 // Mojo shared buffers can be mapped at any offset. However,
301 // base::SharedMemory must be mapped at a page boundary. So calculate what the
302 // nearest whole page offset is, and build a mapping that's offset from that.
303 #if defined(OS_NACL)
304 // base::SysInfo isn't available under NaCl.
305 size_t page_size = getpagesize();
306 #else
307 size_t page_size = base::SysInfo::VMAllocationGranularity();
308 #endif
309 size_t offset_rounding = offset_ % page_size;
310 size_t real_offset = offset_ - offset_rounding;
311 size_t real_length = length_ + offset_rounding;
312
313 if (!shared_memory_.MapAt(static_cast<off_t>(real_offset), real_length))
314 return false;
315
316 base_ = static_cast<char*>(shared_memory_.memory()) + offset_rounding;
317 return true;
318 }
319
Unmap()320 void PlatformSharedBufferMapping::Unmap() {
321 shared_memory_.Unmap();
322 }
323
324 } // namespace edk
325 } // namespace mojo
326