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., |start_index_| and |current_num_bytes_| fit into a |uint32_t|, but
      7 // their sum may not. This is bad and poses a security risk. (We're currently
      8 // saved by the limit on capacity -- the maximum size of the buffer, checked in
      9 // |DataPipe::ValidateOptions()|, is currently sufficiently small.)
     10 
     11 #include "mojo/system/local_data_pipe.h"
     12 
     13 #include <string.h>
     14 
     15 #include <algorithm>
     16 
     17 #include "base/logging.h"
     18 #include "mojo/system/constants.h"
     19 
     20 namespace mojo {
     21 namespace system {
     22 
     23 LocalDataPipe::LocalDataPipe(const MojoCreateDataPipeOptions& options)
     24     : DataPipe(true, true, options), start_index_(0), current_num_bytes_(0) {
     25   // Note: |buffer_| is lazily allocated, since a common case will be that one
     26   // of the handles is immediately passed off to another process.
     27 }
     28 
     29 LocalDataPipe::~LocalDataPipe() {
     30 }
     31 
     32 void LocalDataPipe::ProducerCloseImplNoLock() {
     33   // If the consumer is still open and we still have data, we have to keep the
     34   // buffer around. Currently, we won't free it even if it empties later. (We
     35   // could do this -- requiring a check on every read -- but that seems to be
     36   // optimizing for the uncommon case.)
     37   if (!consumer_open_no_lock() || !current_num_bytes_) {
     38     // Note: There can only be a two-phase *read* (by the consumer) if we still
     39     // have data.
     40     DCHECK(!consumer_in_two_phase_read_no_lock());
     41     DestroyBufferNoLock();
     42   }
     43 }
     44 
     45 MojoResult LocalDataPipe::ProducerWriteDataImplNoLock(
     46     UserPointer<const void> elements,
     47     UserPointer<uint32_t> num_bytes,
     48     uint32_t max_num_bytes_to_write,
     49     uint32_t min_num_bytes_to_write) {
     50   DCHECK_EQ(max_num_bytes_to_write % element_num_bytes(), 0u);
     51   DCHECK_EQ(min_num_bytes_to_write % element_num_bytes(), 0u);
     52   DCHECK_GT(max_num_bytes_to_write, 0u);
     53   DCHECK(consumer_open_no_lock());
     54 
     55   size_t num_bytes_to_write = 0;
     56   if (may_discard()) {
     57     if (min_num_bytes_to_write > capacity_num_bytes())
     58       return MOJO_RESULT_OUT_OF_RANGE;
     59 
     60     num_bytes_to_write = std::min(static_cast<size_t>(max_num_bytes_to_write),
     61                                   capacity_num_bytes());
     62     if (num_bytes_to_write > capacity_num_bytes() - current_num_bytes_) {
     63       // Discard as much as needed (discard oldest first).
     64       MarkDataAsConsumedNoLock(num_bytes_to_write -
     65                                (capacity_num_bytes() - current_num_bytes_));
     66       // No need to wake up write waiters, since we're definitely going to leave
     67       // the buffer full.
     68     }
     69   } else {
     70     if (min_num_bytes_to_write > capacity_num_bytes() - current_num_bytes_) {
     71       // Don't return "should wait" since you can't wait for a specified amount
     72       // of data.
     73       return MOJO_RESULT_OUT_OF_RANGE;
     74     }
     75 
     76     num_bytes_to_write = std::min(static_cast<size_t>(max_num_bytes_to_write),
     77                                   capacity_num_bytes() - current_num_bytes_);
     78   }
     79   if (num_bytes_to_write == 0)
     80     return MOJO_RESULT_SHOULD_WAIT;
     81 
     82   // The amount we can write in our first |memcpy()|.
     83   size_t num_bytes_to_write_first =
     84       std::min(num_bytes_to_write, GetMaxNumBytesToWriteNoLock());
     85   // Do the first (and possibly only) |memcpy()|.
     86   size_t first_write_index =
     87       (start_index_ + current_num_bytes_) % capacity_num_bytes();
     88   EnsureBufferNoLock();
     89   elements.GetArray(buffer_.get() + first_write_index,
     90                     num_bytes_to_write_first);
     91 
     92   if (num_bytes_to_write_first < num_bytes_to_write) {
     93     // The "second write index" is zero.
     94     elements.At(num_bytes_to_write_first)
     95         .GetArray(buffer_.get(), num_bytes_to_write - num_bytes_to_write_first);
     96   }
     97 
     98   current_num_bytes_ += num_bytes_to_write;
     99   DCHECK_LE(current_num_bytes_, capacity_num_bytes());
    100   num_bytes.Put(static_cast<uint32_t>(num_bytes_to_write));
    101   return MOJO_RESULT_OK;
    102 }
    103 
    104 MojoResult LocalDataPipe::ProducerBeginWriteDataImplNoLock(
    105     UserPointer<void*> buffer,
    106     UserPointer<uint32_t> buffer_num_bytes,
    107     uint32_t min_num_bytes_to_write) {
    108   DCHECK(consumer_open_no_lock());
    109 
    110   // The index we need to start writing at.
    111   size_t write_index =
    112       (start_index_ + current_num_bytes_) % capacity_num_bytes();
    113 
    114   size_t max_num_bytes_to_write = GetMaxNumBytesToWriteNoLock();
    115   if (min_num_bytes_to_write > max_num_bytes_to_write) {
    116     // In "may discard" mode, we can always write from the write index to the
    117     // end of the buffer.
    118     if (may_discard() &&
    119         min_num_bytes_to_write <= capacity_num_bytes() - write_index) {
    120       // To do so, we need to discard an appropriate amount of data.
    121       // We should only reach here if the start index is after the write index!
    122       DCHECK_GE(start_index_, write_index);
    123       DCHECK_GT(min_num_bytes_to_write - max_num_bytes_to_write, 0u);
    124       MarkDataAsConsumedNoLock(min_num_bytes_to_write - max_num_bytes_to_write);
    125       max_num_bytes_to_write = min_num_bytes_to_write;
    126     } else {
    127       // Don't return "should wait" since you can't wait for a specified amount
    128       // of data.
    129       return MOJO_RESULT_OUT_OF_RANGE;
    130     }
    131   }
    132 
    133   // Don't go into a two-phase write if there's no room.
    134   if (max_num_bytes_to_write == 0)
    135     return MOJO_RESULT_SHOULD_WAIT;
    136 
    137   EnsureBufferNoLock();
    138   buffer.Put(buffer_.get() + write_index);
    139   buffer_num_bytes.Put(static_cast<uint32_t>(max_num_bytes_to_write));
    140   set_producer_two_phase_max_num_bytes_written_no_lock(
    141       static_cast<uint32_t>(max_num_bytes_to_write));
    142   return MOJO_RESULT_OK;
    143 }
    144 
    145 MojoResult LocalDataPipe::ProducerEndWriteDataImplNoLock(
    146     uint32_t num_bytes_written) {
    147   DCHECK_LE(num_bytes_written,
    148             producer_two_phase_max_num_bytes_written_no_lock());
    149   current_num_bytes_ += num_bytes_written;
    150   DCHECK_LE(current_num_bytes_, capacity_num_bytes());
    151   set_producer_two_phase_max_num_bytes_written_no_lock(0);
    152   return MOJO_RESULT_OK;
    153 }
    154 
    155 HandleSignalsState LocalDataPipe::ProducerGetHandleSignalsStateImplNoLock()
    156     const {
    157   HandleSignalsState rv;
    158   if (consumer_open_no_lock()) {
    159     if ((may_discard() || current_num_bytes_ < capacity_num_bytes()) &&
    160         !producer_in_two_phase_write_no_lock())
    161       rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
    162     rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
    163   }
    164   return rv;
    165 }
    166 
    167 void LocalDataPipe::ConsumerCloseImplNoLock() {
    168   // If the producer is around and in a two-phase write, we have to keep the
    169   // buffer around. (We then don't free it until the producer is closed. This
    170   // could be rectified, but again seems like optimizing for the uncommon case.)
    171   if (!producer_open_no_lock() || !producer_in_two_phase_write_no_lock())
    172     DestroyBufferNoLock();
    173   current_num_bytes_ = 0;
    174 }
    175 
    176 MojoResult LocalDataPipe::ConsumerReadDataImplNoLock(
    177     UserPointer<void> elements,
    178     UserPointer<uint32_t> num_bytes,
    179     uint32_t max_num_bytes_to_read,
    180     uint32_t min_num_bytes_to_read) {
    181   DCHECK_EQ(max_num_bytes_to_read % element_num_bytes(), 0u);
    182   DCHECK_EQ(min_num_bytes_to_read % element_num_bytes(), 0u);
    183   DCHECK_GT(max_num_bytes_to_read, 0u);
    184 
    185   if (min_num_bytes_to_read > current_num_bytes_) {
    186     // Don't return "should wait" since you can't wait for a specified amount of
    187     // data.
    188     return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE
    189                                    : MOJO_RESULT_FAILED_PRECONDITION;
    190   }
    191 
    192   size_t num_bytes_to_read =
    193       std::min(static_cast<size_t>(max_num_bytes_to_read), current_num_bytes_);
    194   if (num_bytes_to_read == 0) {
    195     return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT
    196                                    : MOJO_RESULT_FAILED_PRECONDITION;
    197   }
    198 
    199   // The amount we can read in our first |memcpy()|.
    200   size_t num_bytes_to_read_first =
    201       std::min(num_bytes_to_read, GetMaxNumBytesToReadNoLock());
    202   elements.PutArray(buffer_.get() + start_index_, num_bytes_to_read_first);
    203 
    204   if (num_bytes_to_read_first < num_bytes_to_read) {
    205     // The "second read index" is zero.
    206     elements.At(num_bytes_to_read_first)
    207         .PutArray(buffer_.get(), num_bytes_to_read - num_bytes_to_read_first);
    208   }
    209 
    210   MarkDataAsConsumedNoLock(num_bytes_to_read);
    211   num_bytes.Put(static_cast<uint32_t>(num_bytes_to_read));
    212   return MOJO_RESULT_OK;
    213 }
    214 
    215 MojoResult LocalDataPipe::ConsumerDiscardDataImplNoLock(
    216     UserPointer<uint32_t> num_bytes,
    217     uint32_t max_num_bytes_to_discard,
    218     uint32_t min_num_bytes_to_discard) {
    219   DCHECK_EQ(max_num_bytes_to_discard % element_num_bytes(), 0u);
    220   DCHECK_EQ(min_num_bytes_to_discard % element_num_bytes(), 0u);
    221   DCHECK_GT(max_num_bytes_to_discard, 0u);
    222 
    223   if (min_num_bytes_to_discard > current_num_bytes_) {
    224     // Don't return "should wait" since you can't wait for a specified amount of
    225     // data.
    226     return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE
    227                                    : MOJO_RESULT_FAILED_PRECONDITION;
    228   }
    229 
    230   // Be consistent with other operations; error if no data available.
    231   if (current_num_bytes_ == 0) {
    232     return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT
    233                                    : MOJO_RESULT_FAILED_PRECONDITION;
    234   }
    235 
    236   size_t num_bytes_to_discard = std::min(
    237       static_cast<size_t>(max_num_bytes_to_discard), current_num_bytes_);
    238   MarkDataAsConsumedNoLock(num_bytes_to_discard);
    239   num_bytes.Put(static_cast<uint32_t>(num_bytes_to_discard));
    240   return MOJO_RESULT_OK;
    241 }
    242 
    243 MojoResult LocalDataPipe::ConsumerQueryDataImplNoLock(
    244     UserPointer<uint32_t> num_bytes) {
    245   // Note: This cast is safe, since the capacity fits into a |uint32_t|.
    246   num_bytes.Put(static_cast<uint32_t>(current_num_bytes_));
    247   return MOJO_RESULT_OK;
    248 }
    249 
    250 MojoResult LocalDataPipe::ConsumerBeginReadDataImplNoLock(
    251     UserPointer<const void*> buffer,
    252     UserPointer<uint32_t> buffer_num_bytes,
    253     uint32_t min_num_bytes_to_read) {
    254   size_t max_num_bytes_to_read = GetMaxNumBytesToReadNoLock();
    255   if (min_num_bytes_to_read > max_num_bytes_to_read) {
    256     // Don't return "should wait" since you can't wait for a specified amount of
    257     // data.
    258     return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE
    259                                    : MOJO_RESULT_FAILED_PRECONDITION;
    260   }
    261 
    262   // Don't go into a two-phase read if there's no data.
    263   if (max_num_bytes_to_read == 0) {
    264     return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT
    265                                    : MOJO_RESULT_FAILED_PRECONDITION;
    266   }
    267 
    268   buffer.Put(buffer_.get() + start_index_);
    269   buffer_num_bytes.Put(static_cast<uint32_t>(max_num_bytes_to_read));
    270   set_consumer_two_phase_max_num_bytes_read_no_lock(
    271       static_cast<uint32_t>(max_num_bytes_to_read));
    272   return MOJO_RESULT_OK;
    273 }
    274 
    275 MojoResult LocalDataPipe::ConsumerEndReadDataImplNoLock(
    276     uint32_t num_bytes_read) {
    277   DCHECK_LE(num_bytes_read, consumer_two_phase_max_num_bytes_read_no_lock());
    278   DCHECK_LE(start_index_ + num_bytes_read, capacity_num_bytes());
    279   MarkDataAsConsumedNoLock(num_bytes_read);
    280   set_consumer_two_phase_max_num_bytes_read_no_lock(0);
    281   return MOJO_RESULT_OK;
    282 }
    283 
    284 HandleSignalsState LocalDataPipe::ConsumerGetHandleSignalsStateImplNoLock()
    285     const {
    286   HandleSignalsState rv;
    287   if (current_num_bytes_ > 0) {
    288     if (!consumer_in_two_phase_read_no_lock())
    289       rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE;
    290     rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
    291   } else if (producer_open_no_lock()) {
    292     rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
    293   }
    294   return rv;
    295 }
    296 
    297 void LocalDataPipe::EnsureBufferNoLock() {
    298   DCHECK(producer_open_no_lock());
    299   if (buffer_)
    300     return;
    301   buffer_.reset(static_cast<char*>(
    302       base::AlignedAlloc(capacity_num_bytes(), kDataPipeBufferAlignmentBytes)));
    303 }
    304 
    305 void LocalDataPipe::DestroyBufferNoLock() {
    306 #ifndef NDEBUG
    307   // Scribble on the buffer to help detect use-after-frees. (This also helps the
    308   // unit test detect certain bugs without needing ASAN or similar.)
    309   if (buffer_)
    310     memset(buffer_.get(), 0xcd, capacity_num_bytes());
    311 #endif
    312   buffer_.reset();
    313 }
    314 
    315 size_t LocalDataPipe::GetMaxNumBytesToWriteNoLock() {
    316   size_t next_index = start_index_ + current_num_bytes_;
    317   if (next_index >= capacity_num_bytes()) {
    318     next_index %= capacity_num_bytes();
    319     DCHECK_GE(start_index_, next_index);
    320     DCHECK_EQ(start_index_ - next_index,
    321               capacity_num_bytes() - current_num_bytes_);
    322     return start_index_ - next_index;
    323   }
    324   return capacity_num_bytes() - next_index;
    325 }
    326 
    327 size_t LocalDataPipe::GetMaxNumBytesToReadNoLock() {
    328   if (start_index_ + current_num_bytes_ > capacity_num_bytes())
    329     return capacity_num_bytes() - start_index_;
    330   return current_num_bytes_;
    331 }
    332 
    333 void LocalDataPipe::MarkDataAsConsumedNoLock(size_t num_bytes) {
    334   DCHECK_LE(num_bytes, current_num_bytes_);
    335   start_index_ += num_bytes;
    336   start_index_ %= capacity_num_bytes();
    337   current_num_bytes_ -= num_bytes;
    338 }
    339 
    340 }  // namespace system
    341 }  // namespace mojo
    342