From c42e8d4e87a4158221806aa9f50c392845df2fbd Mon Sep 17 00:00:00 2001 From: Eden Chuang Date: Tue, 19 May 2020 12:50:36 +0000 Subject: [PATCH] Bug 1532287 - P1 Saving the loading document/worker's COEP in InternalRequest. r=dom-workers-and-storage-reviewers,perry Currently, the worker's COEP value is saved in WorkerPrivate and it is not respected for fetch/cache API in workers. This patch saving the COEP value which fetch/cache API should be respected when using in workers. Notice that for the dedicated workers, it is not only respected to worker's COEP but also its owner's. For fetch in workers, P2 will propagate the COEP value through nsILoadInfo to HttpChannels, such that COEP can be respected in parent process when calling ProcessCrossOriginResourcePolicyHeader() and ProcessCrossOriginEmbedderPolicyHeader(). For cache in workers. We handle it in bug 1603168. COEP will be propagated through CacheRequest to the parent process and respected in CacheOpParent::OnOpComplete(). Differential Revision: https://phabricator.services.mozilla.com/D73689 --- dom/fetch/InternalRequest.h | 10 ++++++++ dom/fetch/Request.cpp | 12 +++++++++ dom/workers/ScriptLoader.cpp | 5 ---- dom/workers/WorkerPrivate.cpp | 46 ++++++++++++++++++++--------------- dom/workers/WorkerPrivate.h | 14 ++++++++--- 5 files changed, 59 insertions(+), 28 deletions(-) diff --git a/dom/fetch/InternalRequest.h b/dom/fetch/InternalRequest.h index e0201cf8a447..aed771e90b8d 100644 --- a/dom/fetch/InternalRequest.h +++ b/dom/fetch/InternalRequest.h @@ -340,6 +340,14 @@ class InternalRequest final : public AtomicSafeRefCounted { InternalRequest(const InternalRequest& aOther) = delete; + void SetEmbedderPolicy(nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) { + mEmbedderPolicy = aPolicy; + } + + nsILoadInfo::CrossOriginEmbedderPolicy GetEmbedderPolicy() const { + return mEmbedderPolicy; + } + private: struct ConstructorGuard {}; @@ -412,6 +420,8 @@ class InternalRequest final : public AtomicSafeRefCounted { // It is illegal to pass such a Request object to a fetch() method unless // if the caller has chrome privileges. bool mContentPolicyTypeOverridden = false; + nsILoadInfo::CrossOriginEmbedderPolicy mEmbedderPolicy = + nsILoadInfo::EMBEDDER_POLICY_NULL; UniquePtr mPrincipalInfo; }; diff --git a/dom/fetch/Request.cpp b/dom/fetch/Request.cpp index 56da1efde8d8..5a8eb91b1427 100644 --- a/dom/fetch/Request.cpp +++ b/dom/fetch/Request.cpp @@ -429,6 +429,8 @@ SafeRefPtr Request::Constructor(nsIGlobalObject* aGlobal, } UniquePtr principalInfo; + nsILoadInfo::CrossOriginEmbedderPolicy coep = + nsILoadInfo::EMBEDDER_POLICY_NULL; if (NS_IsMainThread()) { nsCOMPtr window = do_QueryInterface(aGlobal); @@ -446,6 +448,9 @@ SafeRefPtr Request::Constructor(nsIGlobalObject* aGlobal, return nullptr; } } + if (window->GetWindowContext()) { + coep = window->GetWindowContext()->GetEmbedderPolicy(); + } } } else { WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); @@ -454,10 +459,17 @@ SafeRefPtr Request::Constructor(nsIGlobalObject* aGlobal, request->SetEnvironmentReferrerPolicy(worker->GetReferrerPolicy()); principalInfo = MakeUnique(worker->GetPrincipalInfo()); + coep = worker->GetEmbedderPolicy(); + // For dedicated worker, the response must respect the owner's COEP. + if (coep == nsILoadInfo::EMBEDDER_POLICY_NULL && + worker->IsDedicatedWorker()) { + coep = worker->GetOwnerEmbedderPolicy(); + } } } request->SetPrincipalInfo(std::move(principalInfo)); + request->SetEmbedderPolicy(coep); if (mode != RequestMode::EndGuard_) { request->SetMode(mode); diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index 0231de6c0918..171082759e86 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -593,9 +593,6 @@ class ScriptResponseHeaderProcessor final : public nsIRequestObserver { NS_IMETHOD OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) override { - MOZ_DIAGNOSTIC_ASSERT_IF(NS_SUCCEEDED(aStatusCode), - mWorkerPrivate->GetEmbedderPolicy().isSome()); - return NS_OK; } @@ -621,8 +618,6 @@ class ScriptResponseHeaderProcessor final : public nsIRequestObserver { ~ScriptResponseHeaderProcessor() = default; nsresult ProcessCrossOriginEmbedderPolicyHeader(nsIRequest* aRequest) { - MOZ_ASSERT_IF(!mIsMainScript, mWorkerPrivate->GetEmbedderPolicy().isSome()); - nsCOMPtr httpChannel = do_QueryInterface(aRequest); // NOTE: the spec doesn't say what to do with non-HTTP workers. diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index ef411f76cd4e..e3d94e2f4bf3 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -5232,20 +5232,19 @@ bool WorkerPrivate::CrossOriginIsolated() const { nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP; } -Maybe WorkerPrivate::GetEmbedderPolicy() +nsILoadInfo::CrossOriginEmbedderPolicy WorkerPrivate::GetEmbedderPolicy() const { - MOZ_ASSERT(NS_IsMainThread()); - if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) { - return Some(nsILoadInfo::EMBEDDER_POLICY_NULL); + return nsILoadInfo::EMBEDDER_POLICY_NULL; } - return mEmbedderPolicy; + return mEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL); } Result WorkerPrivate::SetEmbedderPolicy( nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mEmbedderPolicy.isNothing()); if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) { return Ok(); @@ -5255,10 +5254,10 @@ Result WorkerPrivate::SetEmbedderPolicy( // corp_reqired. But if owner's embedder policy is null, aPolicy needs not // match owner's value. // https://wicg.github.io/cross-origin-embedder-policy/#cascade-vs-require - auto ownerEmbedderPolicy = GetOwnerEmbedderPolicy(); - if (ownerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL) != + EnsureOwnerEmbedderPolicy(); + if (mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL) != nsILoadInfo::EMBEDDER_POLICY_NULL) { - if (ownerEmbedderPolicy.valueOr(aPolicy) != aPolicy) { + if (mOwnerEmbedderPolicy.valueOr(aPolicy) != aPolicy) { return Err(NS_ERROR_BLOCKED_BY_POLICY); } } @@ -5272,9 +5271,9 @@ void WorkerPrivate::InheritOwnerEmbedderPolicyOrNull(nsIRequest* aRequest) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aRequest); - auto coep = GetOwnerEmbedderPolicy(); + EnsureOwnerEmbedderPolicy(); - if (coep.isSome()) { + if (mOwnerEmbedderPolicy.isSome()) { nsCOMPtr channel = do_QueryInterface(aRequest); MOZ_ASSERT(channel); @@ -5289,7 +5288,8 @@ void WorkerPrivate::InheritOwnerEmbedderPolicyOrNull(nsIRequest* aRequest) { MOZ_RELEASE_ASSERT(isLocalScriptURI); } - mEmbedderPolicy.emplace(coep.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL)); + mEmbedderPolicy.emplace( + mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL)); } bool WorkerPrivate::MatchEmbedderPolicy( @@ -5303,19 +5303,25 @@ bool WorkerPrivate::MatchEmbedderPolicy( return mEmbedderPolicy.value() == aPolicy; } -Maybe -WorkerPrivate::GetOwnerEmbedderPolicy() const { +nsILoadInfo::CrossOriginEmbedderPolicy WorkerPrivate::GetOwnerEmbedderPolicy() + const { + if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) { + return nsILoadInfo::EMBEDDER_POLICY_NULL; + } + + return mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL); +} + +void WorkerPrivate::EnsureOwnerEmbedderPolicy() { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mOwnerEmbedderPolicy.isNothing()); if (GetParent()) { - return GetParent()->GetEmbedderPolicy(); + mOwnerEmbedderPolicy.emplace(GetParent()->GetEmbedderPolicy()); + } else if (GetWindow() && GetWindow()->GetWindowContext()) { + mOwnerEmbedderPolicy.emplace( + GetWindow()->GetWindowContext()->GetEmbedderPolicy()); } - - if (GetWindow() && GetWindow()->GetWindowContext()) { - return Some(GetWindow()->GetWindowContext()->GetEmbedderPolicy()); - } - - return Nothing(); } NS_IMPL_ADDREF(WorkerPrivate::EventTarget) diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index ea34dac09d0a..82d04ec3a72f 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -933,8 +933,7 @@ class WorkerPrivate : public RelativeTimeline { * will, depending on the return type, return a value that will avoid * assertion failures or a value that won't block loads. */ - - Maybe GetEmbedderPolicy() const; + nsILoadInfo::CrossOriginEmbedderPolicy GetEmbedderPolicy() const; // Fails if a policy has already been set or if `aPolicy` violates the owner's // policy, if an owner exists. @@ -954,6 +953,8 @@ class WorkerPrivate : public RelativeTimeline { bool MatchEmbedderPolicy( nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) const; + nsILoadInfo::CrossOriginEmbedderPolicy GetOwnerEmbedderPolicy() const; + private: WorkerPrivate( WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker, @@ -1059,7 +1060,13 @@ class WorkerPrivate : public RelativeTimeline { UniquePtr CreateClientSource(); - Maybe GetOwnerEmbedderPolicy() const; + // This method is called when corresponding script loader processes the COEP + // header for the worker. + // This method should be called only once in the main thread. + // After this method is called the COEP value owner(window/parent worker) is + // cached in mOwnerEmbedderPolicy such that it can be accessed in other + // threads, i.e. WorkerThread. + void EnsureOwnerEmbedderPolicy(); class EventTarget; friend class EventTarget; @@ -1308,6 +1315,7 @@ class WorkerPrivate : public RelativeTimeline { // there isn't a strong reason to store it on the global scope other than // better consistency with the COEP spec. Maybe mEmbedderPolicy; + Maybe mOwnerEmbedderPolicy; }; class AutoSyncLoopHolder {