1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "InputStreamUtils.h"
8
9 #include "nsIIPCSerializableInputStream.h"
10
11 #include "mozilla/Assertions.h"
12 #include "mozilla/dom/File.h"
13 #include "mozilla/dom/quota/DecryptingInputStream_impl.h"
14 #include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
15 #include "mozilla/ipc/DataPipe.h"
16 #include "mozilla/ipc/IPCStreamDestination.h"
17 #include "mozilla/ipc/IPCStreamSource.h"
18 #include "mozilla/InputStreamLengthHelper.h"
19 #include "mozilla/RemoteLazyInputStream.h"
20 #include "mozilla/RemoteLazyInputStreamChild.h"
21 #include "mozilla/RemoteLazyInputStreamStorage.h"
22 #include "mozilla/SlicedInputStream.h"
23 #include "mozilla/InputStreamLengthWrapper.h"
24 #include "nsBufferedStreams.h"
25 #include "nsComponentManagerUtils.h"
26 #include "nsDebug.h"
27 #include "nsFileStreams.h"
28 #include "nsIAsyncInputStream.h"
29 #include "nsIAsyncOutputStream.h"
30 #include "nsID.h"
31 #include "nsIMIMEInputStream.h"
32 #include "nsIMultiplexInputStream.h"
33 #include "nsIPipe.h"
34 #include "nsMIMEInputStream.h"
35 #include "nsMultiplexInputStream.h"
36 #include "nsNetCID.h"
37 #include "nsStreamUtils.h"
38 #include "nsStringStream.h"
39 #include "nsXULAppAPI.h"
40
41 using namespace mozilla;
42 using namespace mozilla::dom;
43
44 namespace mozilla {
45 namespace ipc {
46
47 namespace {
48
49 template <typename M>
SerializeInputStreamInternal(nsIInputStream * aInputStream,InputStreamParams & aParams,nsTArray<FileDescriptor> & aFileDescriptors,bool aDelayedStart,uint32_t aMaxSize,uint32_t * aSizeUsed,M * aManager)50 void SerializeInputStreamInternal(nsIInputStream* aInputStream,
51 InputStreamParams& aParams,
52 nsTArray<FileDescriptor>& aFileDescriptors,
53 bool aDelayedStart, uint32_t aMaxSize,
54 uint32_t* aSizeUsed, M* aManager) {
55 MOZ_ASSERT(aInputStream);
56 MOZ_ASSERT(aManager);
57
58 nsCOMPtr<nsIIPCSerializableInputStream> serializable =
59 do_QueryInterface(aInputStream);
60 if (!serializable) {
61 MOZ_CRASH("Input stream is not serializable!");
62 }
63
64 serializable->Serialize(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
65 aSizeUsed, aManager);
66
67 if (aParams.type() == InputStreamParams::T__None) {
68 MOZ_CRASH("Serialize failed!");
69 }
70 }
71
72 template <typename M>
SerializeInputStreamAsPipeInternal(nsIInputStream * aInputStream,InputStreamParams & aParams,bool aDelayedStart,M * aManager)73 void SerializeInputStreamAsPipeInternal(nsIInputStream* aInputStream,
74 InputStreamParams& aParams,
75 bool aDelayedStart, M* aManager) {
76 MOZ_ASSERT(aInputStream);
77 MOZ_ASSERT(aManager);
78
79 // Let's try to take the length using InputStreamLengthHelper. If the length
80 // cannot be taken synchronously, and its length is needed, the stream needs
81 // to be fully copied in memory on the deserialization side.
82 int64_t length;
83 if (!InputStreamLengthHelper::GetSyncLength(aInputStream, &length)) {
84 length = -1;
85 }
86
87 nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aInputStream);
88
89 // As a fallback, attempt to stream the data across using a IPCStream
90 // actor. For blocking streams, create a nonblocking pipe instead,
91 bool nonBlocking = false;
92 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aInputStream->IsNonBlocking(&nonBlocking)));
93 if (!nonBlocking || !asyncStream) {
94 const uint32_t kBufferSize = 32768; // matches IPCStream buffer size.
95 nsCOMPtr<nsIAsyncOutputStream> sink;
96 nsresult rv = NS_NewPipe2(getter_AddRefs(asyncStream), getter_AddRefs(sink),
97 true, false, kBufferSize, UINT32_MAX);
98 if (NS_WARN_IF(NS_FAILED(rv))) {
99 return;
100 }
101
102 nsCOMPtr<nsIEventTarget> target =
103 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
104
105 // Since the source stream could be used by others, let's not close it when
106 // the copy is done.
107 rv = NS_AsyncCopy(aInputStream, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS,
108 kBufferSize, nullptr, nullptr, false);
109 if (NS_WARN_IF(NS_FAILED(rv))) {
110 return;
111 }
112 }
113 MOZ_DIAGNOSTIC_ASSERT(asyncStream);
114
115 auto* streamSource = IPCStreamSource::Create(asyncStream, aManager);
116 if (NS_WARN_IF(!streamSource)) {
117 // Failed to create IPCStreamSource, which would cause a failure should we
118 // attempt to serialize it later. So abort now.
119 return;
120 }
121
122 aParams = IPCRemoteStreamParams(aDelayedStart, streamSource, length);
123 }
124
125 } // namespace
126
SerializeInputStream(nsIInputStream * aInputStream,InputStreamParams & aParams,nsTArray<FileDescriptor> & aFileDescriptors,bool aDelayedStart,uint32_t aMaxSize,uint32_t * aSizeUsed,ParentToChildStreamActorManager * aManager)127 void InputStreamHelper::SerializeInputStream(
128 nsIInputStream* aInputStream, InputStreamParams& aParams,
129 nsTArray<FileDescriptor>& aFileDescriptors, bool aDelayedStart,
130 uint32_t aMaxSize, uint32_t* aSizeUsed,
131 ParentToChildStreamActorManager* aManager) {
132 SerializeInputStreamInternal(aInputStream, aParams, aFileDescriptors,
133 aDelayedStart, aMaxSize, aSizeUsed, aManager);
134 }
135
SerializeInputStream(nsIInputStream * aInputStream,InputStreamParams & aParams,nsTArray<FileDescriptor> & aFileDescriptors,bool aDelayedStart,uint32_t aMaxSize,uint32_t * aSizeUsed,ChildToParentStreamActorManager * aManager)136 void InputStreamHelper::SerializeInputStream(
137 nsIInputStream* aInputStream, InputStreamParams& aParams,
138 nsTArray<FileDescriptor>& aFileDescriptors, bool aDelayedStart,
139 uint32_t aMaxSize, uint32_t* aSizeUsed,
140 ChildToParentStreamActorManager* aManager) {
141 SerializeInputStreamInternal(aInputStream, aParams, aFileDescriptors,
142 aDelayedStart, aMaxSize, aSizeUsed, aManager);
143 }
144
SerializeInputStreamAsPipe(nsIInputStream * aInputStream,InputStreamParams & aParams,bool aDelayedStart,ParentToChildStreamActorManager * aManager)145 void InputStreamHelper::SerializeInputStreamAsPipe(
146 nsIInputStream* aInputStream, InputStreamParams& aParams,
147 bool aDelayedStart, ParentToChildStreamActorManager* aManager) {
148 SerializeInputStreamAsPipeInternal(aInputStream, aParams, aDelayedStart,
149 aManager);
150 }
151
SerializeInputStreamAsPipe(nsIInputStream * aInputStream,InputStreamParams & aParams,bool aDelayedStart,ChildToParentStreamActorManager * aManager)152 void InputStreamHelper::SerializeInputStreamAsPipe(
153 nsIInputStream* aInputStream, InputStreamParams& aParams,
154 bool aDelayedStart, ChildToParentStreamActorManager* aManager) {
155 SerializeInputStreamAsPipeInternal(aInputStream, aParams, aDelayedStart,
156 aManager);
157 }
158
PostSerializationActivation(InputStreamParams & aParams,bool aConsumedByIPC,bool aDelayedStart)159 void InputStreamHelper::PostSerializationActivation(InputStreamParams& aParams,
160 bool aConsumedByIPC,
161 bool aDelayedStart) {
162 switch (aParams.type()) {
163 case InputStreamParams::TBufferedInputStreamParams: {
164 BufferedInputStreamParams& params =
165 aParams.get_BufferedInputStreamParams();
166 InputStreamHelper::PostSerializationActivation(
167 params.optionalStream(), aConsumedByIPC, aDelayedStart);
168 return;
169 }
170
171 case InputStreamParams::TMIMEInputStreamParams: {
172 MIMEInputStreamParams& params = aParams.get_MIMEInputStreamParams();
173 InputStreamHelper::PostSerializationActivation(
174 params.optionalStream(), aConsumedByIPC, aDelayedStart);
175 return;
176 }
177
178 case InputStreamParams::TMultiplexInputStreamParams: {
179 MultiplexInputStreamParams& params =
180 aParams.get_MultiplexInputStreamParams();
181 for (InputStreamParams& subParams : params.streams()) {
182 InputStreamHelper::PostSerializationActivation(
183 subParams, aConsumedByIPC, aDelayedStart);
184 }
185 return;
186 }
187
188 case InputStreamParams::TSlicedInputStreamParams: {
189 SlicedInputStreamParams& params = aParams.get_SlicedInputStreamParams();
190 InputStreamHelper::PostSerializationActivation(
191 params.stream(), aConsumedByIPC, aDelayedStart);
192 return;
193 }
194
195 case InputStreamParams::TInputStreamLengthWrapperParams: {
196 InputStreamLengthWrapperParams& params =
197 aParams.get_InputStreamLengthWrapperParams();
198 InputStreamHelper::PostSerializationActivation(
199 params.stream(), aConsumedByIPC, aDelayedStart);
200 return;
201 }
202
203 case InputStreamParams::TIPCRemoteStreamParams: {
204 IPCRemoteStreamType& remoteInputStream =
205 aParams.get_IPCRemoteStreamParams().stream();
206
207 IPCStreamSource* source = nullptr;
208 if (remoteInputStream.type() ==
209 IPCRemoteStreamType::TPChildToParentStreamChild) {
210 source = IPCStreamSource::Cast(
211 remoteInputStream.get_PChildToParentStreamChild());
212 } else {
213 MOZ_ASSERT(remoteInputStream.type() ==
214 IPCRemoteStreamType::TPParentToChildStreamParent);
215 source = IPCStreamSource::Cast(
216 remoteInputStream.get_PParentToChildStreamParent());
217 }
218
219 MOZ_ASSERT(source);
220
221 // If the source stream has not been taken to be sent to the other side,
222 // we can destroy it.
223 if (!aConsumedByIPC) {
224 source->StartDestroy();
225 return;
226 }
227
228 if (!aDelayedStart) {
229 // If we don't need to do a delayedStart, we start it now. Otherwise,
230 // the Start() will be called at the first use by the
231 // IPCStreamDestination::DelayedStartInputStream.
232 source->Start();
233 }
234
235 return;
236 }
237
238 case InputStreamParams::TDataPipeReceiverStreamParams:
239 break;
240
241 case InputStreamParams::TStringInputStreamParams:
242 break;
243
244 case InputStreamParams::TFileInputStreamParams:
245 break;
246
247 case InputStreamParams::TRemoteLazyInputStreamParams:
248 break;
249
250 case InputStreamParams::TEncryptedFileInputStreamParams:
251 break;
252
253 default:
254 MOZ_CRASH(
255 "A new stream? Should decide if it must be processed recursively or "
256 "not.");
257 }
258 }
259
PostSerializationActivation(Maybe<InputStreamParams> & aParams,bool aConsumedByIPC,bool aDelayedStart)260 void InputStreamHelper::PostSerializationActivation(
261 Maybe<InputStreamParams>& aParams, bool aConsumedByIPC,
262 bool aDelayedStart) {
263 if (aParams.isSome()) {
264 InputStreamHelper::PostSerializationActivation(
265 aParams.ref(), aConsumedByIPC, aDelayedStart);
266 }
267 }
268
DeserializeInputStream(const InputStreamParams & aParams,const nsTArray<FileDescriptor> & aFileDescriptors)269 already_AddRefed<nsIInputStream> InputStreamHelper::DeserializeInputStream(
270 const InputStreamParams& aParams,
271 const nsTArray<FileDescriptor>& aFileDescriptors) {
272 if (aParams.type() == InputStreamParams::TRemoteLazyInputStreamParams) {
273 const RemoteLazyInputStreamParams& params =
274 aParams.get_RemoteLazyInputStreamParams();
275
276 // RemoteLazyInputStreamRefs are not deserializable on the parent side,
277 // because the parent is the only one that has a copy of the original stream
278 // in the RemoteLazyInputStreamStorage.
279 if (params.type() ==
280 RemoteLazyInputStreamParams::TRemoteLazyInputStreamRef) {
281 MOZ_ASSERT(XRE_IsParentProcess());
282 const RemoteLazyInputStreamRef& ref =
283 params.get_RemoteLazyInputStreamRef();
284
285 auto storage = RemoteLazyInputStreamStorage::Get().unwrapOr(nullptr);
286 MOZ_ASSERT(storage);
287 nsCOMPtr<nsIInputStream> stream;
288 storage->GetStream(ref.id(), ref.start(), ref.length(),
289 getter_AddRefs(stream));
290 return stream.forget();
291 }
292
293 // parent -> child serializations receive an RemoteLazyInputStream actor.
294 MOZ_ASSERT(params.type() ==
295 RemoteLazyInputStreamParams::TPRemoteLazyInputStreamChild);
296 RemoteLazyInputStreamChild* actor =
297 static_cast<RemoteLazyInputStreamChild*>(
298 params.get_PRemoteLazyInputStreamChild());
299 nsCOMPtr<nsIInputStream> stream = actor->CreateStream();
300 return stream.forget();
301 }
302
303 if (aParams.type() == InputStreamParams::TIPCRemoteStreamParams) {
304 const IPCRemoteStreamParams& remoteStream =
305 aParams.get_IPCRemoteStreamParams();
306 const IPCRemoteStreamType& remoteStreamType = remoteStream.stream();
307 IPCStreamDestination* destinationStream;
308
309 if (remoteStreamType.type() ==
310 IPCRemoteStreamType::TPChildToParentStreamParent) {
311 destinationStream = IPCStreamDestination::Cast(
312 remoteStreamType.get_PChildToParentStreamParent());
313 } else {
314 MOZ_ASSERT(remoteStreamType.type() ==
315 IPCRemoteStreamType::TPParentToChildStreamChild);
316 destinationStream = IPCStreamDestination::Cast(
317 remoteStreamType.get_PParentToChildStreamChild());
318 }
319
320 destinationStream->SetDelayedStart(remoteStream.delayedStart());
321 destinationStream->SetLength(remoteStream.length());
322 return destinationStream->TakeReader();
323 }
324
325 if (aParams.type() == InputStreamParams::TDataPipeReceiverStreamParams) {
326 const DataPipeReceiverStreamParams& pipeParams =
327 aParams.get_DataPipeReceiverStreamParams();
328 return do_AddRef(pipeParams.pipe());
329 }
330
331 nsCOMPtr<nsIIPCSerializableInputStream> serializable;
332
333 switch (aParams.type()) {
334 case InputStreamParams::TStringInputStreamParams: {
335 nsCOMPtr<nsIInputStream> stream;
336 NS_NewCStringInputStream(getter_AddRefs(stream), ""_ns);
337 serializable = do_QueryInterface(stream);
338 } break;
339
340 case InputStreamParams::TFileInputStreamParams: {
341 nsCOMPtr<nsIFileInputStream> stream;
342 nsFileInputStream::Create(nullptr, NS_GET_IID(nsIFileInputStream),
343 getter_AddRefs(stream));
344 serializable = do_QueryInterface(stream);
345 } break;
346
347 case InputStreamParams::TBufferedInputStreamParams: {
348 nsCOMPtr<nsIBufferedInputStream> stream;
349 nsBufferedInputStream::Create(nullptr, NS_GET_IID(nsIBufferedInputStream),
350 getter_AddRefs(stream));
351 serializable = do_QueryInterface(stream);
352 } break;
353
354 case InputStreamParams::TMIMEInputStreamParams: {
355 nsCOMPtr<nsIMIMEInputStream> stream;
356 nsMIMEInputStreamConstructor(nullptr, NS_GET_IID(nsIMIMEInputStream),
357 getter_AddRefs(stream));
358 serializable = do_QueryInterface(stream);
359 } break;
360
361 case InputStreamParams::TMultiplexInputStreamParams: {
362 nsCOMPtr<nsIMultiplexInputStream> stream;
363 nsMultiplexInputStreamConstructor(
364 nullptr, NS_GET_IID(nsIMultiplexInputStream), getter_AddRefs(stream));
365 serializable = do_QueryInterface(stream);
366 } break;
367
368 case InputStreamParams::TSlicedInputStreamParams:
369 serializable = new SlicedInputStream();
370 break;
371
372 case InputStreamParams::TInputStreamLengthWrapperParams:
373 serializable = new InputStreamLengthWrapper();
374 break;
375
376 case InputStreamParams::TEncryptedFileInputStreamParams:
377 serializable = new dom::quota::DecryptingInputStream<
378 dom::quota::IPCStreamCipherStrategy>();
379 break;
380
381 default:
382 MOZ_ASSERT(false, "Unknown params!");
383 return nullptr;
384 }
385
386 MOZ_ASSERT(serializable);
387
388 if (!serializable->Deserialize(aParams, aFileDescriptors)) {
389 MOZ_ASSERT(false, "Deserialize failed!");
390 return nullptr;
391 }
392
393 nsCOMPtr<nsIInputStream> stream = do_QueryInterface(serializable);
394 MOZ_ASSERT(stream);
395
396 return stream.forget();
397 }
398
399 } // namespace ipc
400 } // namespace mozilla
401