• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_WIN) || defined(OS_MACOSX)
257   NOTREACHED();
258   return false;
259 #else
260   DCHECK(!shared_memory_);
261 
262   base::SharedMemoryHandle handle(rw_platform_handle.release().handle, false);
263   shared_memory_.reset(new base::SharedMemory(handle, false));
264 
265   base::SharedMemoryHandle ro_handle(ro_platform_handle.release().handle,
266                                      false);
267   ro_shared_memory_.reset(new base::SharedMemory(ro_handle, true));
268 
269   return true;
270 #endif
271 }
272 
InitFromSharedMemoryHandle(base::SharedMemoryHandle handle)273 void PlatformSharedBuffer::InitFromSharedMemoryHandle(
274     base::SharedMemoryHandle handle) {
275   DCHECK(!shared_memory_);
276 
277   shared_memory_.reset(new base::SharedMemory(handle, read_only_));
278 }
279 
~PlatformSharedBufferMapping()280 PlatformSharedBufferMapping::~PlatformSharedBufferMapping() {
281   Unmap();
282 }
283 
GetBase() const284 void* PlatformSharedBufferMapping::GetBase() const {
285   return base_;
286 }
287 
GetLength() const288 size_t PlatformSharedBufferMapping::GetLength() const {
289   return length_;
290 }
291 
Map()292 bool PlatformSharedBufferMapping::Map() {
293   // Mojo shared buffers can be mapped at any offset. However,
294   // base::SharedMemory must be mapped at a page boundary. So calculate what the
295   // nearest whole page offset is, and build a mapping that's offset from that.
296 #if defined(OS_NACL)
297   // base::SysInfo isn't available under NaCl.
298   size_t page_size = getpagesize();
299 #else
300   size_t page_size = base::SysInfo::VMAllocationGranularity();
301 #endif
302   size_t offset_rounding = offset_ % page_size;
303   size_t real_offset = offset_ - offset_rounding;
304   size_t real_length = length_ + offset_rounding;
305 
306   if (!shared_memory_.MapAt(static_cast<off_t>(real_offset), real_length))
307     return false;
308 
309   base_ = static_cast<char*>(shared_memory_.memory()) + offset_rounding;
310   return true;
311 }
312 
Unmap()313 void PlatformSharedBufferMapping::Unmap() {
314   shared_memory_.Unmap();
315 }
316 
317 }  // namespace edk
318 }  // namespace mojo
319