Bug 1519074 - delay deserializing some CSP info until necessary, r=bzbarsky,ckerschb
☠☠ backed out by db283d60bcf1 ☠ ☠
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Fri, 18 Jan 2019 17:17:32 +0000
changeset 514452 a69c1a1dc6fe4dec9598bcdd3038d12bf681a045
parent 514451 edc879d89731b3ce0d849af3d78b60e7a6ce53a4
child 514453 6927759957ba288fffa5c32011b758cc7aeea5a4
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky, ckerschb
bugs1519074
milestone66.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 1519074 - delay deserializing some CSP info until necessary, r=bzbarsky,ckerschb Differential Revision: https://phabricator.services.mozilla.com/D16794
dom/security/nsCSPContext.cpp
dom/security/nsCSPContext.h
ipc/glue/BackgroundUtils.cpp
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -39,16 +39,17 @@
 #include "nsThreadUtils.h"
 #include "nsString.h"
 #include "nsScriptSecurityManager.h"
 #include "nsStringStream.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/CSPReportBinding.h"
 #include "mozilla/dom/CSPDictionariesBinding.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "nsINetworkInterceptController.h"
 #include "nsSandboxFlags.h"
 #include "nsIScriptElement.h"
 #include "nsIEventTarget.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/Element.h"
 #include "nsXULAppAPI.h"
@@ -269,71 +270,95 @@ nsresult nsCSPContext::InitFromOther(nsC
   NS_ENSURE_SUCCESS(rv, rv);
 
   for (auto policy : aOtherContext->mPolicies) {
     nsAutoString policyStr;
     policy->toString(policyStr);
     AppendPolicy(policyStr, policy->getReportOnlyFlag(),
                  policy->getDeliveredViaMetaTagFlag());
   }
+  mIPCPolicies = aOtherContext->mIPCPolicies;
   return NS_OK;
 }
 
+void nsCSPContext::SetIPCPolicies(
+    const nsTArray<mozilla::ipc::ContentSecurityPolicy>& aPolicies) {
+  mIPCPolicies = aPolicies;
+}
+
+void nsCSPContext::EnsureIPCPoliciesRead() {
+  if (mIPCPolicies.Length() > 0) {
+    nsresult rv;
+    for (auto& policy : mIPCPolicies) {
+      rv = AppendPolicy(policy.policy(), policy.reportOnlyFlag(),
+                        policy.deliveredViaMetaTagFlag());
+      NS_WARN_IF(NS_FAILED(rv));
+    }
+    mIPCPolicies.Clear();
+  }
+}
+
 NS_IMETHODIMP
 nsCSPContext::GetPolicyString(uint32_t aIndex, nsAString& outStr) {
   outStr.Truncate();
+  EnsureIPCPoliciesRead();
   if (aIndex >= mPolicies.Length()) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
   mPolicies[aIndex]->toString(outStr);
   return NS_OK;
 }
 
 const nsCSPPolicy* nsCSPContext::GetPolicy(uint32_t aIndex) {
+  EnsureIPCPoliciesRead();
   if (aIndex >= mPolicies.Length()) {
     return nullptr;
   }
   return mPolicies[aIndex];
 }
 
 NS_IMETHODIMP
 nsCSPContext::GetPolicyCount(uint32_t* outPolicyCount) {
+  EnsureIPCPoliciesRead();
   *outPolicyCount = mPolicies.Length();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSPContext::GetUpgradeInsecureRequests(bool* outUpgradeRequest) {
+  EnsureIPCPoliciesRead();
   *outUpgradeRequest = false;
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
     if (mPolicies[i]->hasDirective(
             nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
       *outUpgradeRequest = true;
       return NS_OK;
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSPContext::GetBlockAllMixedContent(bool* outBlockAllMixedContent) {
+  EnsureIPCPoliciesRead();
   *outBlockAllMixedContent = false;
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
     if (!mPolicies[i]->getReportOnlyFlag() &&
         mPolicies[i]->hasDirective(
             nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) {
       *outBlockAllMixedContent = true;
       return NS_OK;
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSPContext::GetEnforcesFrameAncestors(bool* outEnforcesFrameAncestors) {
+  EnsureIPCPoliciesRead();
   *outEnforcesFrameAncestors = false;
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
     if (!mPolicies[i]->getReportOnlyFlag() &&
         mPolicies[i]->hasDirective(
             nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE)) {
       *outEnforcesFrameAncestors = true;
       return NS_OK;
     }
@@ -368,16 +393,17 @@ nsCSPContext::AppendPolicy(const nsAStri
     mPolicies.AppendElement(policy);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSPContext::GetAllowsEval(bool* outShouldReportViolation,
                             bool* outAllowsEval) {
+  EnsureIPCPoliciesRead();
   *outShouldReportViolation = false;
   *outAllowsEval = true;
 
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
     if (!mPolicies[i]->allows(nsIContentPolicy::TYPE_SCRIPT, CSP_UNSAFE_EVAL,
                               EmptyString(), false)) {
       // policy is violated: must report the violation and allow the inline
       // script if the policy is report-only.
@@ -448,16 +474,17 @@ nsCSPContext::GetAllowsInline(nsContentP
       "We should only see external content policy types here.");
 
   if (aContentType != nsIContentPolicy::TYPE_SCRIPT &&
       aContentType != nsIContentPolicy::TYPE_STYLESHEET) {
     MOZ_ASSERT(false, "can only allow inline for script or style");
     return NS_OK;
   }
 
+  EnsureIPCPoliciesRead();
   nsAutoString content(EmptyString());
 
   // always iterate all policies, otherwise we might not send out all reports
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
     bool allowed =
         mPolicies[i]->allows(aContentType, CSP_UNSAFE_INLINE, EmptyString(),
                              aParserCreated) ||
         mPolicies[i]->allows(aContentType, CSP_NONCE, aNonce, aParserCreated);
@@ -580,16 +607,17 @@ nsCSPContext::GetAllowsInline(nsContentP
  *     reports.
  */
 NS_IMETHODIMP
 nsCSPContext::LogViolationDetails(
     uint16_t aViolationType, Element* aTriggeringElement,
     nsICSPEventListener* aCSPEventListener, const nsAString& aSourceFile,
     const nsAString& aScriptSample, int32_t aLineNum, int32_t aColumnNum,
     const nsAString& aNonce, const nsAString& aContent) {
+  EnsureIPCPoliciesRead();
   for (uint32_t p = 0; p < mPolicies.Length(); p++) {
     NS_ASSERTION(mPolicies[p], "null pointer in nsTArray<nsCSPPolicy>");
 
     BlockedContentSource blockedContentSource = BlockedContentSource::eUnknown;
     if (aViolationType == nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL) {
       blockedContentSource = BlockedContentSource::eEval;
     } else if (aViolationType ==
                    nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT ||
@@ -794,16 +822,17 @@ void StripURIForReporting(nsIURI* aURI, 
 }
 
 nsresult nsCSPContext::GatherSecurityPolicyViolationEventData(
     nsIURI* aBlockedURI, const nsACString& aBlockedString, nsIURI* aOriginalURI,
     nsAString& aViolatedDirective, uint32_t aViolatedPolicyIndex,
     nsAString& aSourceFile, nsAString& aScriptSample, uint32_t aLineNum,
     uint32_t aColumnNum,
     mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit) {
+  EnsureIPCPoliciesRead();
   NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
 
   MOZ_ASSERT(ValidateDirectiveName(aViolatedDirective),
              "Invalid directive name");
 
   nsresult rv;
 
   // document-uri
@@ -899,16 +928,17 @@ nsresult nsCSPContext::GatherSecurityPol
   aViolationEventInit.mComposed = true;
 
   return NS_OK;
 }
 
 nsresult nsCSPContext::SendReports(
     const mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit,
     uint32_t aViolatedPolicyIndex) {
+  EnsureIPCPoliciesRead();
   NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
 
   dom::CSPReport report;
 
   // blocked-uri
   report.mCsp_report.mBlocked_uri = aViolationEventInit.mBlockedURI;
 
   // document-uri
@@ -1312,16 +1342,17 @@ class CSPReportSenderRunnable final : pu
  */
 nsresult nsCSPContext::AsyncReportViolation(
     Element* aTriggeringElement, nsICSPEventListener* aCSPEventListener,
     nsIURI* aBlockedURI, BlockedContentSource aBlockedContentSource,
     nsIURI* aOriginalURI, const nsAString& aViolatedDirective,
     uint32_t aViolatedPolicyIndex, const nsAString& aObserverSubject,
     const nsAString& aSourceFile, const nsAString& aScriptSample,
     uint32_t aLineNum, uint32_t aColumnNum) {
+  EnsureIPCPoliciesRead();
   NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
 
   nsCOMPtr<nsIRunnable> task = new CSPReportSenderRunnable(
       aTriggeringElement, aCSPEventListener, aBlockedURI, aBlockedContentSource,
       aOriginalURI, aViolatedPolicyIndex,
       mPolicies[aViolatedPolicyIndex]->getReportOnlyFlag(), aViolatedDirective,
       aObserverSubject, aSourceFile, aScriptSample, aLineNum, aColumnNum, this);
 
@@ -1334,16 +1365,17 @@ nsresult nsCSPContext::AsyncReportViolat
 
   NS_DispatchToMainThread(task.forget());
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSPContext::RequireSRIForType(nsContentPolicyType aContentType,
                                 bool* outRequiresSRIForType) {
+  EnsureIPCPoliciesRead();
   *outRequiresSRIForType = false;
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
     if (mPolicies[i]->hasDirective(REQUIRE_SRI_FOR)) {
       if (mPolicies[i]->requireSRIForType(aContentType)) {
         *outRequiresSRIForType = true;
         return NS_OK;
       }
     }
@@ -1490,16 +1522,17 @@ nsCSPContext::Permits(Element* aTriggeri
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSPContext::ToJSON(nsAString& outCSPinJSON) {
   outCSPinJSON.Truncate();
   dom::CSPPolicies jsonPolicies;
   jsonPolicies.mCsp_policies.Construct();
+  EnsureIPCPoliciesRead();
 
   for (uint32_t p = 0; p < mPolicies.Length(); p++) {
     dom::CSP jsonCSP;
     mPolicies[p]->toDomCSPStruct(jsonCSP);
     jsonPolicies.mCsp_policies.Value().AppendElement(jsonCSP, fallible);
   }
 
   // convert the gathered information to JSON
@@ -1511,16 +1544,17 @@ nsCSPContext::ToJSON(nsAString& outCSPin
 
 NS_IMETHODIMP
 nsCSPContext::GetCSPSandboxFlags(uint32_t* aOutSandboxFlags) {
   if (!aOutSandboxFlags) {
     return NS_ERROR_FAILURE;
   }
   *aOutSandboxFlags = SANDBOXED_NONE;
 
+  EnsureIPCPoliciesRead();
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
     uint32_t flags = mPolicies[i]->getSandboxFlags();
 
     // current policy doesn't have sandbox flag, check next policy
     if (!flags) {
       continue;
     }
 
@@ -1677,42 +1711,39 @@ nsCSPContext::Read(nsIObjectInputStream*
 
     bool reportOnly = false;
     rv = aStream->ReadBoolean(&reportOnly);
     NS_ENSURE_SUCCESS(rv, rv);
 
     bool deliveredViaMetaTag = false;
     rv = aStream->ReadBoolean(&deliveredViaMetaTag);
     NS_ENSURE_SUCCESS(rv, rv);
-
-    // @param deliveredViaMetaTag:
-    // when parsing the CSP policy string initially we already remove directives
-    // that should not be processed when delivered via the meta tag. Such
-    // directives will not be present at this point anymore.
-    nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(
-        policyString, mSelfURI, reportOnly, this, deliveredViaMetaTag);
-    if (policy) {
-      mPolicies.AppendElement(policy);
-    }
+    mIPCPolicies.AppendElement(mozilla::ipc::ContentSecurityPolicy(
+        policyString, reportOnly, deliveredViaMetaTag));
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSPContext::Write(nsIObjectOutputStream* aStream) {
   nsresult rv = NS_WriteOptionalCompoundObject(aStream, mSelfURI,
                                                NS_GET_IID(nsIURI), true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Serialize all the policies.
-  aStream->Write32(mPolicies.Length());
+  aStream->Write32(mPolicies.Length() + mIPCPolicies.Length());
 
   nsAutoString polStr;
   for (uint32_t p = 0; p < mPolicies.Length(); p++) {
     polStr.Truncate();
     mPolicies[p]->toString(polStr);
     aStream->WriteWStringZ(polStr.get());
     aStream->WriteBoolean(mPolicies[p]->getReportOnlyFlag());
     aStream->WriteBoolean(mPolicies[p]->getDeliveredViaMetaTagFlag());
   }
+  for (auto& policy : mIPCPolicies) {
+    aStream->WriteWStringZ(policy.policy().get());
+    aStream->WriteBoolean(policy.reportOnlyFlag());
+    aStream->WriteBoolean(policy.deliveredViaMetaTagFlag());
+  }
   return NS_OK;
 }
--- a/dom/security/nsCSPContext.h
+++ b/dom/security/nsCSPContext.h
@@ -32,16 +32,19 @@
 class nsINetworkInterceptController;
 class nsIEventTarget;
 struct ConsoleMsgQueueElem;
 
 namespace mozilla {
 namespace dom {
 class Element;
 }
+namespace ipc {
+class ContentSecurityPolicy;
+}
 }  // namespace mozilla
 
 class nsCSPContext : public nsIContentSecurityPolicy {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICONTENTSECURITYPOLICY
   NS_DECL_NSISERIALIZABLE
 
@@ -50,16 +53,19 @@ class nsCSPContext : public nsIContentSe
 
  public:
   nsCSPContext();
 
   nsresult InitFromOther(nsCSPContext* otherContext,
                          mozilla::dom::Document* aDoc,
                          nsIPrincipal* aPrincipal);
 
+  void SetIPCPolicies(
+      const nsTArray<mozilla::ipc::ContentSecurityPolicy>& policies);
+
   /**
    * SetRequestContext() needs to be called before the innerWindowID
    * is initialized on the document. Use this function to call back to
    * flush queued up console messages and initalize the innerWindowID.
    */
   void flushConsoleMessages();
 
   void logToConsole(const char* aName, const char16_t** aParams,
@@ -128,16 +134,18 @@ class nsCSPContext : public nsIContentSe
 
   static uint32_t ScriptSampleMaxLength() {
     return std::max(
         mozilla::StaticPrefs::security_csp_reporting_script_sample_max_length(),
         0);
   }
 
  private:
+  void EnsureIPCPoliciesRead();
+
   bool permitsInternal(CSPDirective aDir,
                        mozilla::dom::Element* aTriggeringElement,
                        nsICSPEventListener* aCSPEventListener,
                        nsIURI* aContentLocation, nsIURI* aOriginalURIIfRedirect,
                        const nsAString& aNonce, bool aIsPreload, bool aSpecific,
                        bool aSendViolationReports,
                        bool aSendContentLocationInViolationReports,
                        bool aParserCreated);
@@ -148,16 +156,22 @@ class nsCSPContext : public nsIContentSe
                              nsICSPEventListener* aCSPEventListener,
                              const nsAString& aNonce, const nsAString& aContent,
                              const nsAString& aViolatedDirective,
                              uint32_t aViolatedPolicyIndex,
                              uint32_t aLineNumber, uint32_t aColumnNumber);
 
   nsString mReferrer;
   uint64_t mInnerWindowID;  // used for web console logging
+  // When deserializing an nsCSPContext instance, we initially just keep the
+  // policies unparsed. We will only reconstruct actual CSP policy instances
+  // when there's an attempt to use the CSP. Given a better way to serialize/
+  // deserialize individual nsCSPPolicy objects, this performance
+  // optimization could go away.
+  nsTArray<mozilla::ipc::ContentSecurityPolicy> mIPCPolicies;
   nsTArray<nsCSPPolicy*> mPolicies;
   nsCOMPtr<nsIURI> mSelfURI;
   nsCOMPtr<nsILoadGroup> mCallingChannelLoadGroup;
   nsWeakPtr mLoadingContext;
   // The CSP hangs off the principal, so let's store a raw pointer of the
   // principal to avoid memory leaks. Within the destructor of the principal we
   // explicitly set mLoadingPrincipal to null.
   nsIPrincipal* mLoadingPrincipal;
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -110,25 +110,18 @@ already_AddRefed<nsIPrincipal> Principal
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return nullptr;
         }
 
         rv = csp->SetRequestContext(nullptr, principal);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return nullptr;
         }
-
-        for (auto policy : info.securityPolicies()) {
-          rv = csp->AppendPolicy(policy.policy(), policy.reportOnlyFlag(),
-                                 policy.deliveredViaMetaTagFlag());
-          if (NS_WARN_IF(NS_FAILED(rv))) {
-            return nullptr;
-          }
-        }
-
+        static_cast<nsCSPContext*>(csp.get())->SetIPCPolicies(
+            info.securityPolicies());
         principal->SetCsp(csp);
       }
 
       return principal.forget();
     }
 
     case PrincipalInfo::TExpandedPrincipalInfo: {
       const ExpandedPrincipalInfo& info =