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