• 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., |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 
LocalDataPipe()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 
Init(const MojoCreateDataPipeOptions * options)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 
~LocalDataPipe()52 LocalDataPipe::~LocalDataPipe() {
53   DCHECK(!producer_open_);
54   DCHECK(!consumer_open_);
55 }
56 
ProducerCloseImplNoLock()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 
ProducerBeginWriteDataImplNoLock(void ** buffer,uint32_t * buffer_num_elements,MojoWriteDataFlags flags)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 
ProducerEndWriteDataImplNoLock(uint32_t num_elements_written)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 
ProducerSatisfiedFlagsNoLock()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 
ProducerSatisfiableFlagsNoLock()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 
ConsumerCloseImplNoLock()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 
ConsumerDiscardDataNoLock(uint32_t * num_elements,bool all_or_none)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 
ConsumerQueryDataNoLock(uint32_t * num_elements)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 
ConsumerBeginReadDataImplNoLock(const void ** buffer,uint32_t * buffer_num_elements,MojoReadDataFlags flags)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 
ConsumerEndReadDataImplNoLock(uint32_t num_elements_read)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 
ConsumerSatisfiedFlagsNoLock()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 
ConsumerSatisfiableFlagsNoLock()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 
EnsureBufferNoLock()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 
GetMaxElementsToWriteNoLock()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 
GetMaxElementsToReadNoLock()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