Home | History | Annotate | Download | only in core
      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/core/shared_buffer_dispatcher.h"
      6 
      7 #include <stddef.h>
      8 #include <stdint.h>
      9 
     10 #include <limits>
     11 #include <memory>
     12 #include <utility>
     13 
     14 #include "base/logging.h"
     15 #include "base/memory/ptr_util.h"
     16 #include "build/build_config.h"
     17 #include "mojo/core/configuration.h"
     18 #include "mojo/core/node_controller.h"
     19 #include "mojo/core/options_validation.h"
     20 #include "mojo/core/platform_handle_utils.h"
     21 #include "mojo/core/platform_shared_memory_mapping.h"
     22 #include "mojo/public/c/system/platform_handle.h"
     23 
     24 namespace mojo {
     25 namespace core {
     26 
     27 namespace {
     28 
     29 #pragma pack(push, 1)
     30 
     31 struct SerializedState {
     32   uint64_t num_bytes;
     33   uint32_t access_mode;
     34   uint64_t guid_high;
     35   uint64_t guid_low;
     36   uint32_t padding;
     37 };
     38 
     39 #pragma pack(pop)
     40 
     41 static_assert(sizeof(SerializedState) % 8 == 0,
     42               "Invalid SerializedState size.");
     43 
     44 }  // namespace
     45 
     46 // static
     47 const MojoCreateSharedBufferOptions
     48     SharedBufferDispatcher::kDefaultCreateOptions = {
     49         static_cast<uint32_t>(sizeof(MojoCreateSharedBufferOptions)),
     50         MOJO_CREATE_SHARED_BUFFER_FLAG_NONE};
     51 
     52 // static
     53 MojoResult SharedBufferDispatcher::ValidateCreateOptions(
     54     const MojoCreateSharedBufferOptions* in_options,
     55     MojoCreateSharedBufferOptions* out_options) {
     56   const MojoCreateSharedBufferFlags kKnownFlags =
     57       MOJO_CREATE_SHARED_BUFFER_FLAG_NONE;
     58 
     59   *out_options = kDefaultCreateOptions;
     60   if (!in_options)
     61     return MOJO_RESULT_OK;
     62 
     63   UserOptionsReader<MojoCreateSharedBufferOptions> reader(in_options);
     64   if (!reader.is_valid())
     65     return MOJO_RESULT_INVALID_ARGUMENT;
     66 
     67   if (!OPTIONS_STRUCT_HAS_MEMBER(MojoCreateSharedBufferOptions, flags, reader))
     68     return MOJO_RESULT_OK;
     69   if ((reader.options().flags & ~kKnownFlags))
     70     return MOJO_RESULT_UNIMPLEMENTED;
     71   out_options->flags = reader.options().flags;
     72 
     73   // Checks for fields beyond |flags|:
     74 
     75   // (Nothing here yet.)
     76 
     77   return MOJO_RESULT_OK;
     78 }
     79 
     80 // static
     81 MojoResult SharedBufferDispatcher::Create(
     82     const MojoCreateSharedBufferOptions& /*validated_options*/,
     83     NodeController* node_controller,
     84     uint64_t num_bytes,
     85     scoped_refptr<SharedBufferDispatcher>* result) {
     86   if (!num_bytes)
     87     return MOJO_RESULT_INVALID_ARGUMENT;
     88   if (num_bytes > GetConfiguration().max_shared_memory_num_bytes)
     89     return MOJO_RESULT_RESOURCE_EXHAUSTED;
     90 
     91   base::WritableSharedMemoryRegion writable_region;
     92   if (node_controller) {
     93     writable_region =
     94         node_controller->CreateSharedBuffer(static_cast<size_t>(num_bytes));
     95   } else {
     96     writable_region = base::WritableSharedMemoryRegion::Create(
     97         static_cast<size_t>(num_bytes));
     98   }
     99   if (!writable_region.IsValid())
    100     return MOJO_RESULT_RESOURCE_EXHAUSTED;
    101 
    102   *result = CreateInternal(
    103       base::WritableSharedMemoryRegion::TakeHandleForSerialization(
    104           std::move(writable_region)));
    105   return MOJO_RESULT_OK;
    106 }
    107 
    108 // static
    109 MojoResult SharedBufferDispatcher::CreateFromPlatformSharedMemoryRegion(
    110     base::subtle::PlatformSharedMemoryRegion region,
    111     scoped_refptr<SharedBufferDispatcher>* result) {
    112   if (!region.IsValid())
    113     return MOJO_RESULT_INVALID_ARGUMENT;
    114 
    115   *result = CreateInternal(std::move(region));
    116   return MOJO_RESULT_OK;
    117 }
    118 
    119 // static
    120 scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::Deserialize(
    121     const void* bytes,
    122     size_t num_bytes,
    123     const ports::PortName* ports,
    124     size_t num_ports,
    125     PlatformHandle* platform_handles,
    126     size_t num_platform_handles) {
    127   if (num_bytes != sizeof(SerializedState)) {
    128     LOG(ERROR) << "Invalid serialized shared buffer dispatcher (bad size)";
    129     return nullptr;
    130   }
    131 
    132   const SerializedState* serialized_state =
    133       static_cast<const SerializedState*>(bytes);
    134   if (!serialized_state->num_bytes) {
    135     LOG(ERROR)
    136         << "Invalid serialized shared buffer dispatcher (invalid num_bytes)";
    137     return nullptr;
    138   }
    139 
    140   if (num_ports)
    141     return nullptr;
    142 
    143   PlatformHandle handles[2];
    144 #if defined(OS_POSIX) && !defined(OS_ANDROID) && \
    145     (!defined(OS_MACOSX) || defined(OS_IOS))
    146   if (serialized_state->access_mode ==
    147       MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE) {
    148     if (num_platform_handles != 2)
    149       return nullptr;
    150     handles[1] = std::move(platform_handles[1]);
    151   } else {
    152     if (num_platform_handles != 1)
    153       return nullptr;
    154   }
    155 #else
    156   if (num_platform_handles != 1)
    157     return nullptr;
    158 #endif
    159   handles[0] = std::move(platform_handles[0]);
    160 
    161   base::UnguessableToken guid = base::UnguessableToken::Deserialize(
    162       serialized_state->guid_high, serialized_state->guid_low);
    163 
    164   base::subtle::PlatformSharedMemoryRegion::Mode mode;
    165   switch (serialized_state->access_mode) {
    166     case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY:
    167       mode = base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly;
    168       break;
    169     case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE:
    170       mode = base::subtle::PlatformSharedMemoryRegion::Mode::kWritable;
    171       break;
    172     case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE:
    173       mode = base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe;
    174       break;
    175     default:
    176       LOG(ERROR) << "Invalid serialized shared buffer access mode.";
    177       return nullptr;
    178   }
    179 
    180   auto region = base::subtle::PlatformSharedMemoryRegion::Take(
    181       CreateSharedMemoryRegionHandleFromPlatformHandles(std::move(handles[0]),
    182                                                         std::move(handles[1])),
    183       mode, static_cast<size_t>(serialized_state->num_bytes), guid);
    184   if (!region.IsValid()) {
    185     LOG(ERROR)
    186         << "Invalid serialized shared buffer dispatcher (invalid num_bytes?)";
    187     return nullptr;
    188   }
    189 
    190   return CreateInternal(std::move(region));
    191 }
    192 
    193 base::subtle::PlatformSharedMemoryRegion
    194 SharedBufferDispatcher::PassPlatformSharedMemoryRegion() {
    195   base::AutoLock lock(lock_);
    196   if (!region_.IsValid() || in_transit_)
    197     return base::subtle::PlatformSharedMemoryRegion();
    198 
    199   return std::move(region_);
    200 }
    201 
    202 Dispatcher::Type SharedBufferDispatcher::GetType() const {
    203   return Type::SHARED_BUFFER;
    204 }
    205 
    206 MojoResult SharedBufferDispatcher::Close() {
    207   base::AutoLock lock(lock_);
    208   if (in_transit_)
    209     return MOJO_RESULT_INVALID_ARGUMENT;
    210 
    211   region_ = base::subtle::PlatformSharedMemoryRegion();
    212   return MOJO_RESULT_OK;
    213 }
    214 
    215 MojoResult SharedBufferDispatcher::DuplicateBufferHandle(
    216     const MojoDuplicateBufferHandleOptions* options,
    217     scoped_refptr<Dispatcher>* new_dispatcher) {
    218   MojoDuplicateBufferHandleOptions validated_options;
    219   MojoResult result = ValidateDuplicateOptions(options, &validated_options);
    220   if (result != MOJO_RESULT_OK)
    221     return result;
    222 
    223   base::AutoLock lock(lock_);
    224   if (in_transit_)
    225     return MOJO_RESULT_INVALID_ARGUMENT;
    226 
    227   if ((validated_options.flags & MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_READ_ONLY)) {
    228     // If a read-only duplicate is requested and this handle is not already
    229     // read-only, we need to make it read-only before duplicating. If it's
    230     // unsafe it can't be made read-only, and we must fail instead.
    231     if (region_.GetMode() ==
    232         base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe) {
    233       return MOJO_RESULT_FAILED_PRECONDITION;
    234     } else if (region_.GetMode() ==
    235                base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
    236       region_ = base::ReadOnlySharedMemoryRegion::TakeHandleForSerialization(
    237           base::WritableSharedMemoryRegion::ConvertToReadOnly(
    238               base::WritableSharedMemoryRegion::Deserialize(
    239                   std::move(region_))));
    240     }
    241 
    242     DCHECK_EQ(region_.GetMode(),
    243               base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly);
    244   } else {
    245     // A writable duplicate was requested. If this is already a read-only handle
    246     // we have to reject. Otherwise we have to convert to unsafe to ensure that
    247     // no future read-only duplication requests can succeed.
    248     if (region_.GetMode() ==
    249         base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly) {
    250       return MOJO_RESULT_FAILED_PRECONDITION;
    251     } else if (region_.GetMode() ==
    252                base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
    253       auto handle = region_.PassPlatformHandle();
    254 #if defined(OS_POSIX) && !defined(OS_ANDROID) && \
    255     (!defined(OS_MACOSX) || defined(OS_IOS))
    256       // On POSIX systems excluding Android, Fuchsia, and OSX, we explicitly
    257       // wipe out the secondary (read-only) FD from the platform handle to
    258       // repurpose it for exclusive unsafe usage.
    259       handle.readonly_fd.reset();
    260 #endif
    261       region_ = base::subtle::PlatformSharedMemoryRegion::Take(
    262           std::move(handle),
    263           base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
    264           region_.GetSize(), region_.GetGUID());
    265     }
    266   }
    267 
    268   *new_dispatcher = CreateInternal(region_.Duplicate());
    269   return MOJO_RESULT_OK;
    270 }
    271 
    272 MojoResult SharedBufferDispatcher::MapBuffer(
    273     uint64_t offset,
    274     uint64_t num_bytes,
    275     std::unique_ptr<PlatformSharedMemoryMapping>* mapping) {
    276   if (offset > static_cast<uint64_t>(std::numeric_limits<size_t>::max()))
    277     return MOJO_RESULT_INVALID_ARGUMENT;
    278   if (num_bytes > static_cast<uint64_t>(std::numeric_limits<size_t>::max()))
    279     return MOJO_RESULT_INVALID_ARGUMENT;
    280 
    281   base::AutoLock lock(lock_);
    282   DCHECK(region_.IsValid());
    283   if (in_transit_ || num_bytes == 0 ||
    284       static_cast<size_t>(offset + num_bytes) > region_.GetSize()) {
    285     return MOJO_RESULT_INVALID_ARGUMENT;
    286   }
    287 
    288   DCHECK(mapping);
    289   *mapping = std::make_unique<PlatformSharedMemoryMapping>(
    290       &region_, static_cast<size_t>(offset), static_cast<size_t>(num_bytes));
    291   if (!(*mapping)->IsValid()) {
    292     LOG(ERROR) << "Failed to map shared memory region.";
    293     return MOJO_RESULT_RESOURCE_EXHAUSTED;
    294   }
    295 
    296   return MOJO_RESULT_OK;
    297 }
    298 
    299 MojoResult SharedBufferDispatcher::GetBufferInfo(MojoSharedBufferInfo* info) {
    300   if (!info)
    301     return MOJO_RESULT_INVALID_ARGUMENT;
    302 
    303   base::AutoLock lock(lock_);
    304   info->struct_size = sizeof(*info);
    305   info->size = region_.GetSize();
    306   return MOJO_RESULT_OK;
    307 }
    308 
    309 void SharedBufferDispatcher::StartSerialize(uint32_t* num_bytes,
    310                                             uint32_t* num_ports,
    311                                             uint32_t* num_platform_handles) {
    312   *num_bytes = sizeof(SerializedState);
    313   *num_ports = 0;
    314   *num_platform_handles = 1;
    315 #if defined(OS_POSIX) && !defined(OS_ANDROID) && \
    316     (!defined(OS_MACOSX) || defined(OS_IOS))
    317   if (region_.GetMode() ==
    318       base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
    319     *num_platform_handles = 2;
    320   }
    321 #endif
    322 }
    323 
    324 bool SharedBufferDispatcher::EndSerialize(void* destination,
    325                                           ports::PortName* ports,
    326                                           PlatformHandle* handles) {
    327   SerializedState* serialized_state =
    328       static_cast<SerializedState*>(destination);
    329   base::AutoLock lock(lock_);
    330   serialized_state->num_bytes = region_.GetSize();
    331   switch (region_.GetMode()) {
    332     case base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly:
    333       serialized_state->access_mode =
    334           MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY;
    335       break;
    336     case base::subtle::PlatformSharedMemoryRegion::Mode::kWritable:
    337       serialized_state->access_mode =
    338           MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE;
    339       break;
    340     case base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe:
    341       serialized_state->access_mode =
    342           MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE;
    343       break;
    344     default:
    345       NOTREACHED();
    346       return false;
    347   }
    348 
    349   const base::UnguessableToken& guid = region_.GetGUID();
    350   serialized_state->guid_high = guid.GetHighForSerialization();
    351   serialized_state->guid_low = guid.GetLowForSerialization();
    352   serialized_state->padding = 0;
    353 
    354   auto region = std::move(region_);
    355 #if defined(OS_POSIX) && !defined(OS_ANDROID) && \
    356     (!defined(OS_MACOSX) || defined(OS_IOS))
    357   if (region.GetMode() ==
    358       base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
    359     PlatformHandle platform_handles[2];
    360     ExtractPlatformHandlesFromSharedMemoryRegionHandle(
    361         region.PassPlatformHandle(), &platform_handles[0],
    362         &platform_handles[1]);
    363     handles[0] = std::move(platform_handles[0]);
    364     handles[1] = std::move(platform_handles[1]);
    365     return true;
    366   }
    367 #endif
    368 
    369   PlatformHandle platform_handle;
    370   PlatformHandle ignored_handle;
    371   ExtractPlatformHandlesFromSharedMemoryRegionHandle(
    372       region.PassPlatformHandle(), &platform_handle, &ignored_handle);
    373   handles[0] = std::move(platform_handle);
    374   return true;
    375 }
    376 
    377 bool SharedBufferDispatcher::BeginTransit() {
    378   base::AutoLock lock(lock_);
    379   if (in_transit_)
    380     return false;
    381   in_transit_ = region_.IsValid();
    382   return in_transit_;
    383 }
    384 
    385 void SharedBufferDispatcher::CompleteTransitAndClose() {
    386   base::AutoLock lock(lock_);
    387   in_transit_ = false;
    388   region_ = base::subtle::PlatformSharedMemoryRegion();
    389 }
    390 
    391 void SharedBufferDispatcher::CancelTransit() {
    392   base::AutoLock lock(lock_);
    393   in_transit_ = false;
    394 }
    395 
    396 SharedBufferDispatcher::SharedBufferDispatcher(
    397     base::subtle::PlatformSharedMemoryRegion region)
    398     : region_(std::move(region)) {
    399   DCHECK(region_.IsValid());
    400 }
    401 
    402 SharedBufferDispatcher::~SharedBufferDispatcher() {
    403   DCHECK(!region_.IsValid() && !in_transit_);
    404 }
    405 
    406 // static
    407 scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::CreateInternal(
    408     base::subtle::PlatformSharedMemoryRegion region) {
    409   return base::WrapRefCounted(new SharedBufferDispatcher(std::move(region)));
    410 }
    411 
    412 // static
    413 MojoResult SharedBufferDispatcher::ValidateDuplicateOptions(
    414     const MojoDuplicateBufferHandleOptions* in_options,
    415     MojoDuplicateBufferHandleOptions* out_options) {
    416   const MojoDuplicateBufferHandleFlags kKnownFlags =
    417       MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_READ_ONLY;
    418   static const MojoDuplicateBufferHandleOptions kDefaultOptions = {
    419       static_cast<uint32_t>(sizeof(MojoDuplicateBufferHandleOptions)),
    420       MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_NONE};
    421 
    422   *out_options = kDefaultOptions;
    423   if (!in_options)
    424     return MOJO_RESULT_OK;
    425 
    426   UserOptionsReader<MojoDuplicateBufferHandleOptions> reader(in_options);
    427   if (!reader.is_valid())
    428     return MOJO_RESULT_INVALID_ARGUMENT;
    429 
    430   if (!OPTIONS_STRUCT_HAS_MEMBER(MojoDuplicateBufferHandleOptions, flags,
    431                                  reader))
    432     return MOJO_RESULT_OK;
    433   if ((reader.options().flags & ~kKnownFlags))
    434     return MOJO_RESULT_UNIMPLEMENTED;
    435   out_options->flags = reader.options().flags;
    436 
    437   // Checks for fields beyond |flags|:
    438 
    439   // (Nothing here yet.)
    440 
    441   return MOJO_RESULT_OK;
    442 }
    443 
    444 }  // namespace core
    445 }  // namespace mojo
    446