diff --git a/dom/cache/CacheTypes.ipdlh b/dom/cache/CacheTypes.ipdlh index 7b4ca16bc482..4baf806defd3 100644 --- a/dom/cache/CacheTypes.ipdlh +++ b/dom/cache/CacheTypes.ipdlh @@ -86,6 +86,7 @@ struct CacheResponse CacheReadStreamOrVoid body; IPCChannelInfo channelInfo; OptionalPrincipalInfo principalInfo; + uint32_t paddingInfo; int64_t paddingSize; }; diff --git a/dom/cache/FileUtils.cpp b/dom/cache/FileUtils.cpp index c0220d666dca..dac9281a890f 100644 --- a/dom/cache/FileUtils.cpp +++ b/dom/cache/FileUtils.cpp @@ -31,6 +31,10 @@ 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, @@ -42,7 +46,16 @@ 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 @@ -254,7 +267,8 @@ BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId, // 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); @@ -277,7 +291,7 @@ BodyMaybeUpdatePaddingSize(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, MOZ_DIAGNOSTIC_ASSERT(fileSize >= 0); if (*aPaddingSizeOut == InternalResponse::UNKNOWN_PADDING_SIZE) { - *aPaddingSizeOut = BodyGeneratePadding(fileSize); + *aPaddingSizeOut = BodyGeneratePadding(fileSize, aPaddingInfo); } MOZ_DIAGNOSTIC_ASSERT(*aPaddingSizeOut >= 0); @@ -354,10 +368,24 @@ BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType, } 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(aPaddingInfo); + MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - aBodyFileSize >= randomSize); + randomSize += aBodyFileSize; + + return RoundUp(randomSize, kRoundUpNumber) - aBodyFileSize; } } // namespace diff --git a/dom/cache/Manager.cpp b/dom/cache/Manager.cpp index 93781d3c2224..baf750b895c5 100644 --- a/dom/cache/Manager.cpp +++ b/dom/cache/Manager.cpp @@ -785,6 +785,7 @@ private: // 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); diff --git a/dom/cache/TypeUtils.cpp b/dom/cache/TypeUtils.cpp index 419e235adcee..abe4bd4383ef 100644 --- a/dom/cache/TypeUtils.cpp +++ b/dom/cache/TypeUtils.cpp @@ -202,6 +202,7 @@ TypeUtils::ToCacheResponseWithoutBody(CacheResponse& aOut, aOut.principalInfo() = void_t(); } + aOut.paddingInfo() = aIn.GetPaddingInfo(); aOut.paddingSize() = aIn.GetPaddingSize(); } diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index ad420a9e13da..f33d8e837926 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -433,9 +433,12 @@ FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse, 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"); } @@ -614,6 +617,12 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest, // 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()) { diff --git a/dom/fetch/InternalResponse.cpp b/dom/fetch/InternalResponse.cpp index 94d5fe6838d8..96ec6cc0f4aa 100644 --- a/dom/fetch/InternalResponse.cpp +++ b/dom/fetch/InternalResponse.cpp @@ -11,12 +11,21 @@ #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) @@ -145,6 +154,7 @@ InternalResponse::Clone(CloneType aCloneType) clone->mHeaders = new InternalHeaders(*mHeaders); // Make sure the clone response will have the same padding size. + clone->mPaddingInfo = mPaddingInfo; clone->mPaddingSize = mPaddingSize; if (mWrappedResponse) { @@ -194,6 +204,52 @@ InternalResponse::CORSResponse() 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 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() { diff --git a/dom/fetch/InternalResponse.h b/dom/fetch/InternalResponse.h index a4b33749d7da..8478eac0b010 100644 --- a/dom/fetch/InternalResponse.h +++ b/dom/fetch/InternalResponse.h @@ -233,6 +233,12 @@ public: mBodySize = aBodySize; } + uint32_t + GetPaddingInfo(); + + nsresult + GeneratePaddingInfo(); + int64_t GetPaddingSize(); @@ -307,6 +313,9 @@ private: RefPtr mHeaders; nsCOMPtr 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 mPaddingInfo; int64_t mPaddingSize; public: static const int64_t UNKNOWN_BODY_SIZE = -1;