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