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