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