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_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 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 280 PlatformSharedBufferMapping::~PlatformSharedBufferMapping() { 281 Unmap(); 282 } 283 284 void* PlatformSharedBufferMapping::GetBase() const { 285 return base_; 286 } 287 288 size_t PlatformSharedBufferMapping::GetLength() const { 289 return length_; 290 } 291 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 313 void PlatformSharedBufferMapping::Unmap() { 314 shared_memory_.Unmap(); 315 } 316 317 } // namespace edk 318 } // namespace mojo 319