Home | History | Annotate | Download | only in system
      1 // Copyright 2013 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 // TODO(vtl): I currently potentially overflow in doing index calculations.
      6 // E.g., |buffer_first_element_index_| and |buffer_current_num_elements_| fit
      7 // into a |uint32_t|, but their sum may not. This is bad and poses a security
      8 // risk. (We're currently saved by the limit on capacity -- the maximum size of
      9 // the buffer, checked in |DataPipe::Init()|, is currently sufficiently small.
     10 
     11 #include "mojo/system/local_data_pipe.h"
     12 
     13 #include <algorithm>
     14 
     15 #include "base/logging.h"
     16 #include "mojo/system/constants.h"
     17 
     18 namespace mojo {
     19 namespace system {
     20 
     21 LocalDataPipe::LocalDataPipe()
     22     : DataPipe(true, true),
     23       producer_open_(true),
     24       consumer_open_(true),
     25       buffer_first_element_index_(0),
     26       buffer_current_num_elements_(0),
     27       two_phase_max_elements_written_(0),
     28       two_phase_max_elements_read_(0) {
     29 }
     30 
     31 MojoResult LocalDataPipe::Init(const MojoCreateDataPipeOptions* options) {
     32   static const MojoCreateDataPipeOptions kDefaultOptions = {
     33     sizeof(MojoCreateDataPipeOptions),  // |struct_size|.
     34     MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
     35     1u,  // |element_size|.
     36     static_cast<uint32_t>(kDefaultDataPipeCapacityBytes)
     37   };
     38   if (!options)
     39     options = &kDefaultOptions;
     40 
     41   if (options->struct_size < sizeof(*options))
     42     return MOJO_RESULT_INVALID_ARGUMENT;
     43 
     44   // Note: lazily allocate memory, since a common case will be that one of the
     45   // handles is immediately passed off to another process.
     46   return DataPipe::Init(
     47       (options->flags & MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD),
     48       static_cast<size_t>(options->element_size),
     49       static_cast<size_t>(options->capacity_num_elements));
     50 }
     51 
     52 LocalDataPipe::~LocalDataPipe() {
     53   DCHECK(!producer_open_);
     54   DCHECK(!consumer_open_);
     55 }
     56 
     57 void LocalDataPipe::ProducerCloseImplNoLock() {
     58   DCHECK(producer_open_);
     59   producer_open_ = false;
     60   if (!buffer_current_num_elements_) {
     61     buffer_.reset();
     62     buffer_current_num_elements_ = 0;
     63   }
     64   AwakeConsumerWaitersForStateChangeNoLock();
     65 }
     66 
     67 MojoResult LocalDataPipe::ProducerBeginWriteDataImplNoLock(
     68     void** buffer,
     69     uint32_t* buffer_num_elements,
     70     MojoWriteDataFlags flags) {
     71   size_t max_elements_to_write = GetMaxElementsToWriteNoLock();
     72   // TODO(vtl): Consider this return value.
     73   if ((flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE) &&
     74       *buffer_num_elements < max_elements_to_write)
     75     return MOJO_RESULT_OUT_OF_RANGE;
     76 
     77   size_t next_index = (buffer_first_element_index_ +
     78                        buffer_current_num_elements_) % capacity_num_elements();
     79   EnsureBufferNoLock();
     80   *buffer = buffer_.get() + next_index * element_size();
     81   *buffer_num_elements = static_cast<uint32_t>(max_elements_to_write);
     82   two_phase_max_elements_written_ =
     83       static_cast<uint32_t>(max_elements_to_write);
     84   return MOJO_RESULT_OK;
     85 }
     86 
     87 MojoResult LocalDataPipe::ProducerEndWriteDataImplNoLock(
     88     uint32_t num_elements_written) {
     89   if (num_elements_written > two_phase_max_elements_written_) {
     90     // Note: The two-phase write ends here even on failure.
     91     two_phase_max_elements_written_ = 0;  // For safety.
     92     return MOJO_RESULT_INVALID_ARGUMENT;
     93   }
     94 
     95   buffer_current_num_elements_ += num_elements_written;
     96   DCHECK_LE(buffer_current_num_elements_, capacity_num_elements());
     97   two_phase_max_elements_written_ = 0;  // For safety.
     98   return MOJO_RESULT_OK;
     99 }
    100 
    101 MojoWaitFlags LocalDataPipe::ProducerSatisfiedFlagsNoLock() {
    102   MojoWaitFlags rv = MOJO_WAIT_FLAG_NONE;
    103   if (consumer_open_ && buffer_current_num_elements_ < capacity_num_elements())
    104     rv |= MOJO_WAIT_FLAG_WRITABLE;
    105   return rv;
    106 }
    107 
    108 MojoWaitFlags LocalDataPipe::ProducerSatisfiableFlagsNoLock() {
    109   MojoWaitFlags rv = MOJO_WAIT_FLAG_NONE;
    110   if (consumer_open_)
    111     rv |= MOJO_WAIT_FLAG_WRITABLE;
    112   return rv;
    113 }
    114 
    115 void LocalDataPipe::ConsumerCloseImplNoLock() {
    116   DCHECK(consumer_open_);
    117   consumer_open_ = false;
    118   buffer_.reset();
    119   buffer_current_num_elements_ = 0;
    120   AwakeProducerWaitersForStateChangeNoLock();
    121 }
    122 
    123 MojoResult LocalDataPipe::ConsumerDiscardDataNoLock(uint32_t* num_elements,
    124                                                     bool all_or_none) {
    125   // TODO(vtl): Think about the error code in this case.
    126   if (all_or_none && *num_elements > buffer_current_num_elements_)
    127     return MOJO_RESULT_OUT_OF_RANGE;
    128 
    129   size_t num_elements_to_discard =
    130       std::min(static_cast<size_t>(*num_elements),
    131                buffer_current_num_elements_);
    132   buffer_first_element_index_ =
    133       (buffer_first_element_index_ + num_elements_to_discard) %
    134           capacity_num_elements();
    135   buffer_current_num_elements_ -= num_elements_to_discard;
    136 
    137   AwakeProducerWaitersForStateChangeNoLock();
    138   AwakeConsumerWaitersForStateChangeNoLock();
    139 
    140   *num_elements = static_cast<uint32_t>(num_elements_to_discard);
    141   return MOJO_RESULT_OK;
    142 }
    143 
    144 MojoResult LocalDataPipe::ConsumerQueryDataNoLock(uint32_t* num_elements) {
    145   // Note: This cast is safe, since the capacity fits into a |uint32_t|.
    146   *num_elements = static_cast<uint32_t>(buffer_current_num_elements_);
    147   return MOJO_RESULT_OK;
    148 }
    149 
    150 MojoResult LocalDataPipe::ConsumerBeginReadDataImplNoLock(
    151     const void** buffer,
    152     uint32_t* buffer_num_elements,
    153     MojoReadDataFlags flags) {
    154   size_t max_elements_to_read = GetMaxElementsToReadNoLock();
    155   // TODO(vtl): Consider this return value.
    156   if ((flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE) &&
    157       *buffer_num_elements < max_elements_to_read)
    158     return MOJO_RESULT_OUT_OF_RANGE;
    159   // Note: This works even if |buffer_| is null.
    160   *buffer = buffer_.get() + buffer_first_element_index_ * element_size();
    161   *buffer_num_elements = static_cast<uint32_t>(max_elements_to_read);
    162   two_phase_max_elements_read_ = static_cast<uint32_t>(max_elements_to_read);
    163   return MOJO_RESULT_OK;
    164 }
    165 
    166 MojoResult LocalDataPipe::ConsumerEndReadDataImplNoLock(
    167     uint32_t num_elements_read) {
    168   if (num_elements_read > two_phase_max_elements_read_) {
    169     // Note: The two-phase read ends here even on failure.
    170     two_phase_max_elements_read_ = 0;  // For safety.
    171     return MOJO_RESULT_INVALID_ARGUMENT;
    172   }
    173 
    174   buffer_first_element_index_ += num_elements_read;
    175   DCHECK_LE(buffer_first_element_index_, capacity_num_elements());
    176   buffer_first_element_index_ %= capacity_num_elements();
    177   DCHECK_LE(num_elements_read, buffer_current_num_elements_);
    178   buffer_current_num_elements_ -= num_elements_read;
    179   two_phase_max_elements_read_ = 0;  // For safety.
    180   return MOJO_RESULT_OK;
    181 }
    182 
    183 MojoWaitFlags LocalDataPipe::ConsumerSatisfiedFlagsNoLock() {
    184   MojoWaitFlags rv = MOJO_WAIT_FLAG_NONE;
    185   if (buffer_current_num_elements_ > 0)
    186     rv |= MOJO_WAIT_FLAG_READABLE;
    187   return rv;
    188 }
    189 
    190 MojoWaitFlags LocalDataPipe::ConsumerSatisfiableFlagsNoLock() {
    191   MojoWaitFlags rv = MOJO_WAIT_FLAG_NONE;
    192   if (buffer_current_num_elements_ > 0 || producer_open_)
    193     rv |= MOJO_WAIT_FLAG_READABLE;
    194   return rv;
    195 }
    196 
    197 void LocalDataPipe::EnsureBufferNoLock() {
    198   DCHECK(producer_open_);
    199   if (buffer_.get())
    200     return;
    201   buffer_.reset(static_cast<char*>(
    202       base::AlignedAlloc(static_cast<size_t>(capacity_num_elements()) *
    203                              element_size(),
    204                          kDataPipeBufferAlignmentBytes)));
    205 }
    206 
    207 size_t LocalDataPipe::GetMaxElementsToWriteNoLock() {
    208   size_t next_index = buffer_first_element_index_ +
    209                       buffer_current_num_elements_;
    210   if (next_index >= capacity_num_elements()) {
    211     next_index %= capacity_num_elements();
    212     DCHECK_GE(buffer_first_element_index_, next_index);
    213     return buffer_first_element_index_ - next_index;
    214   }
    215   return capacity_num_elements() - next_index;
    216 }
    217 
    218 size_t LocalDataPipe::GetMaxElementsToReadNoLock() {
    219   if (buffer_first_element_index_ + buffer_current_num_elements_ >
    220           capacity_num_elements())
    221     return capacity_num_elements() - buffer_first_element_index_;
    222   return buffer_current_num_elements_;
    223 }
    224 
    225 }  // namespace system
    226 }  // namespace mojo
    227