Bug 1290481 - P5: Implement a function to generate padding size. r=bkelly
authorTom Tung <shes050117@gmail.com>
Mon, 10 Jul 2017 17:03:24 +0800
changeset 379541 bafa89b7f292ed24d69c57c5dcc041723b386e32
parent 379540 8c60914e10e9595831071bcb6e02de39a90b1e96
child 379542 a3844ec447b4d85e8abff9b3fad06dda5b7f17fb
push id32456
push userarchaeopteryx@coole-files.de
push dateThu, 07 Sep 2017 22:00:40 +0000
treeherdermozilla-central@b4c1ad9565ee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1290481
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1290481 - P5: Implement a function to generate padding size. r=bkelly MozReview-Commit-ID: 6poDeyErBjc
dom/cache/CacheTypes.ipdlh
dom/cache/FileUtils.cpp
dom/cache/Manager.cpp
dom/cache/TypeUtils.cpp
dom/fetch/FetchDriver.cpp
dom/fetch/InternalResponse.cpp
dom/fetch/InternalResponse.h
--- a/dom/cache/CacheTypes.ipdlh
+++ b/dom/cache/CacheTypes.ipdlh
@@ -81,16 +81,17 @@ struct CacheResponse
   nsCString[] urlList;
   uint32_t status;
   nsCString statusText;
   HeadersEntry[] headers;
   HeadersGuardEnum headersGuard;
   CacheReadStreamOrVoid body;
   IPCChannelInfo channelInfo;
   OptionalPrincipalInfo principalInfo;
+  uint32_t paddingInfo;
   int64_t paddingSize;
 };
 
 union CacheResponseOrVoid
 {
   void_t;
   CacheResponse;
 };
--- a/dom/cache/FileUtils.cpp
+++ b/dom/cache/FileUtils.cpp
@@ -26,28 +26,41 @@ namespace cache {
 using mozilla::dom::quota::FileInputStream;
 using mozilla::dom::quota::FileOutputStream;
 using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
 using mozilla::dom::quota::QuotaManager;
 using mozilla::dom::quota::QuotaObject;
 
 namespace {
 
+// Const variable for generate padding size.
+// XXX This will be tweaked to something more meaningful in Bug 1383656.
+const int64_t kRoundUpNumber = 20480;
+
 enum BodyFileType
 {
   BODY_FILE_FINAL,
   BODY_FILE_TMP
 };
 
 nsresult
 BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
              nsIFile** aBodyFileOut);
 
 int64_t
-BodyGeneratePadding(const int64_t aBodyFileSize);
+RoundUp(const int64_t aX, const int64_t aY);
+
+// The alogrithm for generating padding refers to the mitigation approach in
+// https://github.com/whatwg/storage/issues/31.
+// First, generate a random number between 0 and 100kB.
+// Next, round up the sum of random number and response size to the nearest
+// 20kB.
+// Finally, the virtual padding size will be the result minus the response size.
+int64_t
+BodyGeneratePadding(const int64_t aBodyFileSize, const uint32_t aPaddingInfo);
 
 } // namespace
 
 // static
 nsresult
 BodyCreateDir(nsIFile* aBaseDir)
 {
   MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
@@ -249,17 +262,18 @@ BodyOpen(const QuotaInfo& aQuotaInfo, ns
   fileStream.forget(aStreamOut);
 
   return rv;
 }
 
 // static
 nsresult
 BodyMaybeUpdatePaddingSize(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
-                           const nsID& aId, int64_t* aPaddingSizeOut)
+                           const nsID& aId, const uint32_t aPaddingInfo,
+                           int64_t* aPaddingSizeOut)
 {
   MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
   MOZ_DIAGNOSTIC_ASSERT(aPaddingSizeOut);
 
   nsCOMPtr<nsIFile> bodyFile;
   nsresult rv =
     BodyIdToFile(aBaseDir, aId, BODY_FILE_TMP, getter_AddRefs(bodyFile));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -272,17 +286,17 @@ BodyMaybeUpdatePaddingSize(const QuotaIn
   int64_t fileSize = 0;
   RefPtr<QuotaObject> quotaObject =
     quotaManager->GetQuotaObject(PERSISTENCE_TYPE_DEFAULT, aQuotaInfo.mGroup,
                                  aQuotaInfo.mOrigin, bodyFile, &fileSize);
   MOZ_DIAGNOSTIC_ASSERT(quotaObject);
   MOZ_DIAGNOSTIC_ASSERT(fileSize >= 0);
 
   if (*aPaddingSizeOut == InternalResponse::UNKNOWN_PADDING_SIZE) {
-    *aPaddingSizeOut = BodyGeneratePadding(fileSize);
+    *aPaddingSizeOut = BodyGeneratePadding(fileSize, aPaddingInfo);
   }
 
   MOZ_DIAGNOSTIC_ASSERT(*aPaddingSizeOut >= 0);
 
   if (!quotaObject->IncreaseSize(*aPaddingSizeOut)) {
     return NS_ERROR_FILE_NO_DEVICE_SPACE;
   }
 
@@ -349,20 +363,34 @@ BodyIdToFile(nsIFile* aBaseDir, const ns
 
   rv = (*aBodyFileOut)->Append(fileName);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 int64_t
-BodyGeneratePadding(const int64_t aBodyFileSize)
+RoundUp(const int64_t aX, const int64_t aY)
 {
-  // XXXtt: Will deal with it in the next patch.
-  return 0;
+  MOZ_DIAGNOSTIC_ASSERT(aX >= 0);
+  MOZ_DIAGNOSTIC_ASSERT(aY > 0);
+
+  MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - ((aX - 1) / aY) * aY >= aY);
+  return aY + ((aX - 1) / aY) * aY;
+}
+
+int64_t
+BodyGeneratePadding(const int64_t aBodyFileSize, const uint32_t aPaddingInfo)
+{
+  // Generate padding
+  int64_t randomSize = static_cast<int64_t>(aPaddingInfo);
+  MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - aBodyFileSize >= randomSize);
+  randomSize += aBodyFileSize;
+
+  return RoundUp(randomSize, kRoundUpNumber) - aBodyFileSize;
 }
 
 } // namespace
 
 nsresult
 BodyDeleteOrphanedFiles(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
                         nsTArray<nsID>& aKnownBodyIdList)
 {
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -780,16 +780,17 @@ private:
         }
       }
       if (e.mResponseStream) {
         // Gerenate padding size for opaque response if needed.
         if (e.mResponse.type() == ResponseType::Opaque) {
           // It'll generate padding if we've not set it yet.
           rv = BodyMaybeUpdatePaddingSize(mQuotaInfo.ref(), mDBDir,
                                           e.mResponseBodyId,
+                                          e.mResponse.paddingInfo(),
                                           &e.mResponse.paddingSize());
           if (NS_WARN_IF(NS_FAILED(rv))) {
             DoResolve(rv);
             return;
           }
 
           MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - e.mResponse.paddingSize() >=
                                 mUpdatedPaddingSize);
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -197,16 +197,17 @@ TypeUtils::ToCacheResponseWithoutBody(Ca
   aOut.headersGuard() = headers->Guard();
   aOut.channelInfo() = aIn.GetChannelInfo().AsIPCChannelInfo();
   if (aIn.GetPrincipalInfo()) {
     aOut.principalInfo() = *aIn.GetPrincipalInfo();
   } else {
     aOut.principalInfo() = void_t();
   }
 
+  aOut.paddingInfo() = aIn.GetPaddingInfo();
   aOut.paddingSize() = aIn.GetPaddingSize();
 }
 
 void
 TypeUtils::ToCacheResponse(JSContext* aCx, CacheResponse& aOut, Response& aIn,
                            nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
                            ErrorResult& aRv)
 {
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -428,19 +428,22 @@ FetchDriver::BeginAndGetFilteredResponse
   } else {
     switch (mRequest->GetResponseTainting()) {
       case LoadTainting::Basic:
         filteredResponse = aResponse->BasicResponse();
         break;
       case LoadTainting::CORS:
         filteredResponse = aResponse->CORSResponse();
         break;
-      case LoadTainting::Opaque:
+      case LoadTainting::Opaque: {
         filteredResponse = aResponse->OpaqueResponse();
+        nsresult rv = filteredResponse->GeneratePaddingInfo();
+        if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; }
         break;
+      }
       default:
         MOZ_CRASH("Unexpected case");
     }
   }
 
   MOZ_ASSERT(filteredResponse);
   MOZ_ASSERT(mObserver);
   if (!ShouldCheckSRI(mRequest, filteredResponse)) {
@@ -609,16 +612,22 @@ FetchDriver::OnStartRequest(nsIRequest* 
   // the outer fetch().  In gecko, however, we serialize the Response through
   // the channel and must regenerate the tainting from the channel in the
   // interception case.
   mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());
 
   // Resolves fetch() promise which may trigger code running in a worker.  Make
   // sure the Response is fully initialized before calling this.
   mResponse = BeginAndGetFilteredResponse(response, foundOpaqueRedirect);
+  if (NS_WARN_IF(!mResponse)) {
+    // Fail to generate a paddingInfo for opaque response.
+    MOZ_DIAGNOSTIC_ASSERT(mResponse->Type() == ResponseType::Opaque);
+    FailWithNetworkError();
+    return rv;
+  }
 
   // From "Main Fetch" step 19: SRI-part1.
   if (ShouldCheckSRI(mRequest, mResponse) && mSRIMetadata.IsEmpty()) {
     nsIConsoleReportCollector* reporter = nullptr;
     if (mObserver) {
       reporter = mObserver->GetReporter();
     }
 
--- a/dom/fetch/InternalResponse.cpp
+++ b/dom/fetch/InternalResponse.cpp
@@ -6,22 +6,31 @@
 
 #include "InternalResponse.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/InternalHeaders.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
+#include "nsIRandomGenerator.h"
 #include "nsIURI.h"
 #include "nsStreamUtils.h"
 
 namespace mozilla {
 namespace dom {
 
+namespace {
+
+// Const variable for generate padding size
+// XXX This will be tweaked to something more meaningful in Bug 1383656.
+const uint32_t kMaxRandomNumber = 102400;
+
+} // namespace
+
 InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusText)
   : mType(ResponseType::Default)
   , mStatus(aStatus)
   , mStatusText(aStatusText)
   , mHeaders(new InternalHeaders(HeadersGuardEnum::Response))
   , mBodySize(UNKNOWN_BODY_SIZE)
   , mPaddingSize(UNKNOWN_PADDING_SIZE)
 {
@@ -140,16 +149,17 @@ InternalResponse::ToIPC(IPCInternalRespo
 already_AddRefed<InternalResponse>
 InternalResponse::Clone(CloneType aCloneType)
 {
   RefPtr<InternalResponse> clone = CreateIncompleteCopy();
 
   clone->mHeaders = new InternalHeaders(*mHeaders);
 
   // Make sure the clone response will have the same padding size.
+  clone->mPaddingInfo = mPaddingInfo;
   clone->mPaddingSize = mPaddingSize;
 
   if (mWrappedResponse) {
     clone->mWrappedResponse = mWrappedResponse->Clone(aCloneType);
     MOZ_ASSERT(!mBody);
     return clone.forget();
   }
 
@@ -189,16 +199,62 @@ InternalResponse::CORSResponse()
   MOZ_ASSERT(!mWrappedResponse, "Can't CORSResponse a already wrapped response");
   RefPtr<InternalResponse> cors = CreateIncompleteCopy();
   cors->mType = ResponseType::Cors;
   cors->mHeaders = InternalHeaders::CORSHeaders(Headers());
   cors->mWrappedResponse = this;
   return cors.forget();
 }
 
+uint32_t
+InternalResponse::GetPaddingInfo()
+{
+  // If it's an opaque response, the paddingInfo should be generated only when
+  // paddingSize is unknown size.
+  // If it's not, the paddingInfo should be nothing and the paddingSize should
+  // be unknown size.
+  MOZ_DIAGNOSTIC_ASSERT((mType == ResponseType::Opaque &&
+                         mPaddingSize == UNKNOWN_PADDING_SIZE &&
+                         mPaddingInfo.isSome()) ||
+                        (mType == ResponseType::Opaque &&
+                         mPaddingSize != UNKNOWN_PADDING_SIZE &&
+                         mPaddingInfo.isNothing()) ||
+                        (mType != ResponseType::Opaque &&
+                         mPaddingSize == UNKNOWN_PADDING_SIZE &&
+                         mPaddingInfo.isNothing()));
+  return mPaddingInfo.isSome() ? mPaddingInfo.ref() : 0;
+}
+
+nsresult
+InternalResponse::GeneratePaddingInfo()
+{
+  MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque);
+  MOZ_DIAGNOSTIC_ASSERT(mPaddingSize == UNKNOWN_PADDING_SIZE);
+
+  // Utilize random generator to generator a random number
+  nsresult rv;
+  uint32_t randomNumber = 0;
+  nsCOMPtr<nsIRandomGenerator> randomGenerator =
+    do_GetService("@mozilla.org/security/random-generator;1", &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  MOZ_DIAGNOSTIC_ASSERT(randomGenerator);
+
+  uint8_t* buffer;
+  rv = randomGenerator->GenerateRandomBytes(sizeof(randomNumber), &buffer);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  memcpy(&randomNumber, buffer, sizeof(randomNumber));
+  free(buffer);
+
+  mPaddingInfo.emplace(randomNumber % kMaxRandomNumber);
+
+  return rv;
+}
+
 int64_t
 InternalResponse::GetPaddingSize()
 {
   // We initialize padding size to an unknown size (-1). After cached, we only
   // pad opaque response. Opaque response's padding size might be unknown before
   // cached.
   MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque ||
                         mPaddingSize == UNKNOWN_PADDING_SIZE);
--- a/dom/fetch/InternalResponse.h
+++ b/dom/fetch/InternalResponse.h
@@ -228,16 +228,22 @@ public:
     MOZ_ASSERT(aBodySize == UNKNOWN_BODY_SIZE || aBodySize >= 0);
     // If body is not given, then size must be unknown.
     MOZ_ASSERT_IF(!aBody, aBodySize == UNKNOWN_BODY_SIZE);
 
     mBody = aBody;
     mBodySize = aBodySize;
   }
 
+  uint32_t
+  GetPaddingInfo();
+
+  nsresult
+  GeneratePaddingInfo();
+
   int64_t
   GetPaddingSize();
 
   void
   SetPaddingSize(int64_t aPaddingSize);
 
   void
   InitChannelInfo(nsIChannel* aChannel)
@@ -302,16 +308,19 @@ private:
   // Unless stated otherwise, it is the empty list. The current url is the last
   // element in mURLlist
   nsTArray<nsCString> mURLList;
   const uint16_t mStatus;
   const nsCString mStatusText;
   RefPtr<InternalHeaders> mHeaders;
   nsCOMPtr<nsIInputStream> mBody;
   int64_t mBodySize;
+  // It's used to passed to the CacheResponse to generate padding size. Once, we
+  // generate the padding size for resposne, we don't need it anymore.
+  Maybe<uint32_t> mPaddingInfo;
   int64_t mPaddingSize;
 public:
   static const int64_t UNKNOWN_BODY_SIZE = -1;
   static const int64_t UNKNOWN_PADDING_SIZE = -1;
 private:
   ChannelInfo mChannelInfo;
   UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;