xpcom/io/nsInputStreamTee.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include <stdlib.h>
     7 #include "prlog.h"
     9 #include "mozilla/Mutex.h"
    10 #include "mozilla/Attributes.h"
    11 #include "nsIInputStreamTee.h"
    12 #include "nsIInputStream.h"
    13 #include "nsIOutputStream.h"
    14 #include "nsCOMPtr.h"
    15 #include "nsAutoPtr.h"
    16 #include "nsIEventTarget.h"
    17 #include "nsThreadUtils.h"
    19 using namespace mozilla;
    21 #ifdef LOG
    22 #undef LOG
    23 #endif
    24 #ifdef PR_LOGGING
    25 static PRLogModuleInfo*
    26 GetTeeLog()
    27 {
    28     static PRLogModuleInfo *sLog;
    29     if (!sLog)
    30         sLog = PR_NewLogModule("nsInputStreamTee");
    31     return sLog;
    32 }
    33 #define LOG(args) PR_LOG(GetTeeLog(), PR_LOG_DEBUG, args)
    34 #else
    35 #define LOG(args)
    36 #endif
    38 class nsInputStreamTee MOZ_FINAL : public nsIInputStreamTee
    39 {
    40 public:
    41     NS_DECL_THREADSAFE_ISUPPORTS
    42     NS_DECL_NSIINPUTSTREAM
    43     NS_DECL_NSIINPUTSTREAMTEE
    45     nsInputStreamTee();
    46     bool SinkIsValid();
    47     void InvalidateSink();
    49 private:
    50     ~nsInputStreamTee() {}
    52     nsresult TeeSegment(const char *buf, uint32_t count);
    54     static NS_METHOD WriteSegmentFun(nsIInputStream *, void *, const char *,
    55                                      uint32_t, uint32_t, uint32_t *);
    57 private:
    58     nsCOMPtr<nsIInputStream>  mSource;
    59     nsCOMPtr<nsIOutputStream> mSink;
    60     nsCOMPtr<nsIEventTarget>  mEventTarget;
    61     nsWriteSegmentFun         mWriter;  // for implementing ReadSegments
    62     void                      *mClosure; // for implementing ReadSegments
    63     nsAutoPtr<Mutex>          mLock; // synchronize access to mSinkIsValid
    64     bool                      mSinkIsValid; // False if TeeWriteEvent fails 
    65 };
    67 class nsInputStreamTeeWriteEvent : public nsRunnable {
    68 public:
    69     // aTee's lock is held across construction of this object
    70     nsInputStreamTeeWriteEvent(const char *aBuf, uint32_t aCount,
    71                                nsIOutputStream  *aSink, 
    72                                nsInputStreamTee *aTee)
    73     {
    74         // copy the buffer - will be free'd by dtor
    75         mBuf = (char *)malloc(aCount);
    76         if (mBuf) memcpy(mBuf, (char *)aBuf, aCount);
    77         mCount = aCount;
    78         mSink = aSink;
    79         bool isNonBlocking;
    80         mSink->IsNonBlocking(&isNonBlocking);
    81         NS_ASSERTION(isNonBlocking == false, "mSink is nonblocking");
    82         mTee = aTee;
    83     }
    85     NS_IMETHOD Run()
    86     {
    87         if (!mBuf) {
    88             NS_WARNING("nsInputStreamTeeWriteEvent::Run() "
    89                        "memory not allocated\n");
    90             return NS_OK;
    91         }
    92         NS_ABORT_IF_FALSE(mSink, "mSink is null!");
    94         //  The output stream could have been invalidated between when 
    95         //  this event was dispatched and now, so check before writing.
    96         if (!mTee->SinkIsValid()) {
    97             return NS_OK; 
    98         }
   100         LOG(("nsInputStreamTeeWriteEvent::Run() [%p]"
   101              "will write %u bytes to %p\n",
   102               this, mCount, mSink.get()));
   104         uint32_t totalBytesWritten = 0;
   105         while (mCount) {
   106             nsresult rv;
   107             uint32_t bytesWritten = 0;
   108             rv = mSink->Write(mBuf + totalBytesWritten, mCount, &bytesWritten);
   109             if (NS_FAILED(rv)) {
   110                 LOG(("nsInputStreamTeeWriteEvent::Run[%p] error %x in writing",
   111                      this,rv));
   112                 mTee->InvalidateSink();
   113                 break;
   114             }
   115             totalBytesWritten += bytesWritten;
   116             NS_ASSERTION(bytesWritten <= mCount, "wrote too much");
   117             mCount -= bytesWritten;
   118         }
   119         return NS_OK;
   120     }
   122 protected:
   123     virtual ~nsInputStreamTeeWriteEvent()
   124     {
   125         if (mBuf) free(mBuf);
   126         mBuf = nullptr;
   127     }
   129 private:
   130     char *mBuf;
   131     uint32_t mCount;
   132     nsCOMPtr<nsIOutputStream> mSink;
   133     // back pointer to the tee that created this runnable
   134     nsRefPtr<nsInputStreamTee> mTee;
   135 };
   137 nsInputStreamTee::nsInputStreamTee(): mLock(nullptr)
   138                                     , mSinkIsValid(true)
   139 {
   140 }
   142 bool
   143 nsInputStreamTee::SinkIsValid()
   144 {
   145     MutexAutoLock lock(*mLock); 
   146     return mSinkIsValid; 
   147 }
   149 void
   150 nsInputStreamTee::InvalidateSink()
   151 {
   152     MutexAutoLock lock(*mLock);
   153     mSinkIsValid = false;
   154 }
   156 nsresult
   157 nsInputStreamTee::TeeSegment(const char *buf, uint32_t count)
   158 {
   159     if (!mSink) return NS_OK; // nothing to do
   160     if (mLock) { // asynchronous case
   161         NS_ASSERTION(mEventTarget, "mEventTarget is null, mLock is not null.");
   162         if (!SinkIsValid()) {
   163             return NS_OK; // nothing to do
   164         }
   165         nsRefPtr<nsIRunnable> event =
   166             new nsInputStreamTeeWriteEvent(buf, count, mSink, this);
   167         LOG(("nsInputStreamTee::TeeSegment [%p] dispatching write %u bytes\n",
   168               this, count));
   169         return mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
   170     } else { // synchronous case
   171         NS_ASSERTION(!mEventTarget, "mEventTarget is not null, mLock is null.");
   172         nsresult rv;
   173         uint32_t totalBytesWritten = 0;
   174         while (count) {
   175             uint32_t bytesWritten = 0;
   176             rv = mSink->Write(buf + totalBytesWritten, count, &bytesWritten);
   177             if (NS_FAILED(rv)) {
   178                 // ok, this is not a fatal error... just drop our reference to mSink
   179                 // and continue on as if nothing happened.
   180                 NS_WARNING("Write failed (non-fatal)");
   181                 // catch possible misuse of the input stream tee
   182                 NS_ASSERTION(rv != NS_BASE_STREAM_WOULD_BLOCK, "sink must be a blocking stream");
   183                 mSink = 0;
   184                 break;
   185             }
   186             totalBytesWritten += bytesWritten;
   187             NS_ASSERTION(bytesWritten <= count, "wrote too much");
   188             count -= bytesWritten;
   189         }
   190         return NS_OK;
   191     }
   192 }
   194 NS_METHOD
   195 nsInputStreamTee::WriteSegmentFun(nsIInputStream *in, void *closure, const char *fromSegment,
   196                                   uint32_t offset, uint32_t count, uint32_t *writeCount)
   197 {
   198     nsInputStreamTee *tee = reinterpret_cast<nsInputStreamTee *>(closure);
   200     nsresult rv = tee->mWriter(in, tee->mClosure, fromSegment, offset, count, writeCount);
   201     if (NS_FAILED(rv) || (*writeCount == 0)) {
   202         NS_ASSERTION((NS_FAILED(rv) ? (*writeCount == 0) : true),
   203                 "writer returned an error with non-zero writeCount");
   204         return rv;
   205     }
   207     return tee->TeeSegment(fromSegment, *writeCount);
   208 }
   210 NS_IMPL_ISUPPORTS(nsInputStreamTee,
   211                   nsIInputStreamTee,
   212                   nsIInputStream)
   213 NS_IMETHODIMP
   214 nsInputStreamTee::Close()
   215 {
   216     if (NS_WARN_IF(!mSource))
   217         return NS_ERROR_NOT_INITIALIZED;
   218     nsresult rv = mSource->Close();
   219     mSource = 0;
   220     mSink = 0;
   221     return rv;
   222 }
   224 NS_IMETHODIMP
   225 nsInputStreamTee::Available(uint64_t *avail)
   226 {
   227     if (NS_WARN_IF(!mSource))
   228         return NS_ERROR_NOT_INITIALIZED;
   229     return mSource->Available(avail);
   230 }
   232 NS_IMETHODIMP
   233 nsInputStreamTee::Read(char *buf, uint32_t count, uint32_t *bytesRead)
   234 {
   235     if (NS_WARN_IF(!mSource))
   236         return NS_ERROR_NOT_INITIALIZED;
   238     nsresult rv = mSource->Read(buf, count, bytesRead);
   239     if (NS_FAILED(rv) || (*bytesRead == 0))
   240         return rv;
   242     return TeeSegment(buf, *bytesRead);
   243 }
   245 NS_IMETHODIMP
   246 nsInputStreamTee::ReadSegments(nsWriteSegmentFun writer, 
   247                                void *closure,
   248                                uint32_t count,
   249                                uint32_t *bytesRead)
   250 {
   251     if (NS_WARN_IF(!mSource))
   252         return NS_ERROR_NOT_INITIALIZED;
   254     mWriter = writer;
   255     mClosure = closure;
   257     return mSource->ReadSegments(WriteSegmentFun, this, count, bytesRead);
   258 }
   260 NS_IMETHODIMP
   261 nsInputStreamTee::IsNonBlocking(bool *result)
   262 {
   263     if (NS_WARN_IF(!mSource))
   264         return NS_ERROR_NOT_INITIALIZED;
   265     return mSource->IsNonBlocking(result);
   266 }
   268 NS_IMETHODIMP
   269 nsInputStreamTee::SetSource(nsIInputStream *source)
   270 {
   271     mSource = source;
   272     return NS_OK;
   273 }
   275 NS_IMETHODIMP
   276 nsInputStreamTee::GetSource(nsIInputStream **source)
   277 {
   278     NS_IF_ADDREF(*source = mSource);
   279     return NS_OK;
   280 }
   282 NS_IMETHODIMP
   283 nsInputStreamTee::SetSink(nsIOutputStream *sink)
   284 {
   285 #ifdef DEBUG
   286     if (sink) {
   287         bool nonBlocking;
   288         nsresult rv = sink->IsNonBlocking(&nonBlocking);
   289         if (NS_FAILED(rv) || nonBlocking)
   290             NS_ERROR("sink should be a blocking stream");
   291     }
   292 #endif
   293     mSink = sink;
   294     return NS_OK;
   295 }
   297 NS_IMETHODIMP
   298 nsInputStreamTee::GetSink(nsIOutputStream **sink)
   299 {
   300     NS_IF_ADDREF(*sink = mSink);
   301     return NS_OK;
   302 }
   304 NS_IMETHODIMP
   305 nsInputStreamTee::SetEventTarget(nsIEventTarget *anEventTarget)
   306 {
   307     mEventTarget = anEventTarget;
   308     if (mEventTarget) {
   309         // Only need synchronization if this is an async tee
   310         mLock = new Mutex("nsInputStreamTee.mLock");
   311     }
   312     return NS_OK;
   313 }
   315 NS_IMETHODIMP
   316 nsInputStreamTee::GetEventTarget(nsIEventTarget **anEventTarget)
   317 {
   318     NS_IF_ADDREF(*anEventTarget = mEventTarget);
   319     return NS_OK;
   320 }
   323 nsresult
   324 NS_NewInputStreamTeeAsync(nsIInputStream **result,
   325                           nsIInputStream *source,
   326                           nsIOutputStream *sink,
   327                           nsIEventTarget *anEventTarget)
   328 {
   329     nsresult rv;
   331     nsCOMPtr<nsIInputStreamTee> tee = new nsInputStreamTee();
   332     if (!tee)
   333         return NS_ERROR_OUT_OF_MEMORY;
   335     rv = tee->SetSource(source);
   336     if (NS_FAILED(rv)) return rv;
   338     rv = tee->SetSink(sink);
   339     if (NS_FAILED(rv)) return rv;
   341     rv = tee->SetEventTarget(anEventTarget);
   342     if (NS_FAILED(rv)) return rv;
   344     NS_ADDREF(*result = tee);
   345     return rv;
   346 }
   348 nsresult
   349 NS_NewInputStreamTee(nsIInputStream **result,
   350                      nsIInputStream *source,
   351                      nsIOutputStream *sink)
   352 {
   353     return NS_NewInputStreamTeeAsync(result, source, sink, nullptr);
   354 }
   356 #undef LOG

mercurial