• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
LocalDataPipe(const MojoCreateDataPipeOptions & options)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 
~LocalDataPipe()29 LocalDataPipe::~LocalDataPipe() {
30 }
31 
ProducerCloseImplNoLock()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 
ProducerWriteDataImplNoLock(UserPointer<const void> elements,UserPointer<uint32_t> num_bytes,uint32_t max_num_bytes_to_write,uint32_t min_num_bytes_to_write)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 
ProducerBeginWriteDataImplNoLock(UserPointer<void * > buffer,UserPointer<uint32_t> buffer_num_bytes,uint32_t min_num_bytes_to_write)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 
ProducerEndWriteDataImplNoLock(uint32_t num_bytes_written)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 
ProducerGetHandleSignalsStateImplNoLock() const155 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 
ConsumerCloseImplNoLock()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 
ConsumerReadDataImplNoLock(UserPointer<void> elements,UserPointer<uint32_t> num_bytes,uint32_t max_num_bytes_to_read,uint32_t min_num_bytes_to_read)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 
ConsumerDiscardDataImplNoLock(UserPointer<uint32_t> num_bytes,uint32_t max_num_bytes_to_discard,uint32_t min_num_bytes_to_discard)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 
ConsumerQueryDataImplNoLock(UserPointer<uint32_t> num_bytes)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 
ConsumerBeginReadDataImplNoLock(UserPointer<const void * > buffer,UserPointer<uint32_t> buffer_num_bytes,uint32_t min_num_bytes_to_read)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 
ConsumerEndReadDataImplNoLock(uint32_t num_bytes_read)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 
ConsumerGetHandleSignalsStateImplNoLock() const284 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 
EnsureBufferNoLock()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 
DestroyBufferNoLock()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 
GetMaxNumBytesToWriteNoLock()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 
GetMaxNumBytesToReadNoLock()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 
MarkDataAsConsumedNoLock(size_t num_bytes)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