Home | History | Annotate | Download | only in embedder
      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|.
     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
     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
     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
     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
     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 
    109 size_t PlatformSharedBuffer::GetNumBytes() const {
    110   return num_bytes_;
    111 }
    112 
    113 bool PlatformSharedBuffer::IsReadOnly() const {
    114   return read_only_;
    115 }
    116 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    216 PlatformSharedBuffer::PlatformSharedBuffer(size_t num_bytes, bool read_only)
    217     : num_bytes_(num_bytes), read_only_(read_only) {}
    218 
    219 PlatformSharedBuffer::~PlatformSharedBuffer() {}
    220 
    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 
    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 
    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 
    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 
    287 PlatformSharedBufferMapping::~PlatformSharedBufferMapping() {
    288   Unmap();
    289 }
    290 
    291 void* PlatformSharedBufferMapping::GetBase() const {
    292   return base_;
    293 }
    294 
    295 size_t PlatformSharedBufferMapping::GetLength() const {
    296   return length_;
    297 }
    298 
    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 
    320 void PlatformSharedBufferMapping::Unmap() {
    321   shared_memory_.Unmap();
    322 }
    323 
    324 }  // namespace edk
    325 }  // namespace mojo
    326