
The canonical bug format is TODO(crbug.com/<id>). TODOs of the following forms will all be migrated to the new format: - TODO(crbug.com/<old id>) - TODO(https://crbug.com/<old id>) - TODO(crbug/<old id>) - TODO(crbug/monorail/<old id>) - TODO(<old id>) - TODO(issues.chromium.org/<old id>) - TODO(https://issues.chromium.org/<old id>) - TODO(https://issues.chromium.org/u/1/issues/<old id>) - TODO(bugs.chromium.org/<old id>) Bug id mapping is sourced from go/chrome-on-buganizer-prod-issues. See go/crbug-todo-migration for details. #crbug-todo-migration Bug: b/321899722 Change-Id: Ibc66b8c440e4bcdef414e77fef4d9874d2ea9951 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5493800 Auto-Submit: Alison Gale <agale@chromium.org> Commit-Queue: Alison Gale <agale@chromium.org> Reviewed-by: Peter Boström <pbos@chromium.org> Owners-Override: Alison Gale <agale@chromium.org> Cr-Commit-Position: refs/heads/main@{#1293330}
195 lines
7.0 KiB
C++
195 lines
7.0 KiB
C++
// Copyright 2020 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "content/browser/mojo_binder_policy_applier.h"
|
|
|
|
#include <string_view>
|
|
|
|
#include "base/containers/contains.h"
|
|
#include "base/containers/fixed_flat_set.h"
|
|
#include "content/public/browser/mojo_binder_policy_map.h"
|
|
#include "mojo/public/cpp/bindings/message.h"
|
|
|
|
namespace {
|
|
|
|
// TODO(crbug.com/40196368): It is not sustainable to maintain a list.
|
|
// An ideal solution should:
|
|
// 1. Show a pre-submit warning if a frame-scoped interface is specified with
|
|
// kDefer but declares synchronous methods.
|
|
// 2. When an interface that can make sync IPC is registered with BinderMap,
|
|
// change its policy to kCancel by default.
|
|
// 3. Bind these receivers to a generic implementation, and terminate the
|
|
// execution context if it receives a synchronous message.
|
|
// Stores the list of interface names that declare sync methods.
|
|
constexpr auto kSyncMethodInterfaces = base::MakeFixedFlatSet<std::string_view>(
|
|
{"blink.mojom.NotificationService"});
|
|
|
|
} // namespace
|
|
|
|
namespace content {
|
|
|
|
MojoBinderPolicyApplier::MojoBinderPolicyApplier(
|
|
const MojoBinderPolicyMapImpl* policy_map,
|
|
base::OnceCallback<void(const std::string& interface_name)> cancel_callback)
|
|
: policy_map_(*policy_map), cancel_callback_(std::move(cancel_callback)) {}
|
|
|
|
MojoBinderPolicyApplier::~MojoBinderPolicyApplier() = default;
|
|
|
|
// static
|
|
std::unique_ptr<MojoBinderPolicyApplier>
|
|
MojoBinderPolicyApplier::CreateForSameOriginPrerendering(
|
|
base::OnceCallback<void(const std::string& interface_name)>
|
|
cancel_callback) {
|
|
return std::make_unique<MojoBinderPolicyApplier>(
|
|
MojoBinderPolicyMapImpl::GetInstanceForSameOriginPrerendering(),
|
|
std::move(cancel_callback));
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<MojoBinderPolicyApplier>
|
|
MojoBinderPolicyApplier::CreateForPreview(
|
|
base::OnceCallback<void(const std::string& interface_name)>
|
|
cancel_callback) {
|
|
return std::make_unique<MojoBinderPolicyApplier>(
|
|
MojoBinderPolicyMapImpl::GetInstanceForPreview(),
|
|
std::move(cancel_callback));
|
|
}
|
|
|
|
void MojoBinderPolicyApplier::ApplyPolicyToNonAssociatedBinder(
|
|
const std::string& interface_name,
|
|
base::OnceClosure binder_callback) {
|
|
if (mode_ == Mode::kGrantAll) {
|
|
std::move(binder_callback).Run();
|
|
return;
|
|
}
|
|
const MojoBinderNonAssociatedPolicy policy =
|
|
GetNonAssociatedMojoBinderPolicy(interface_name);
|
|
|
|
// Run in the kPrepareToGrantAll mode before the renderer sends back a
|
|
// DidCommitActivation. In this mode, MojoBinderPolicyApplier loosens
|
|
// policies, but still defers binders to ensure that the renderer does not
|
|
// receive unexpected messages before CommitActivation arrives.
|
|
if (mode_ == Mode::kPrepareToGrantAll) {
|
|
switch (policy) {
|
|
case MojoBinderNonAssociatedPolicy::kGrant:
|
|
// Grant these two kinds of interfaces because:
|
|
// - kCancel and kUnexpected interfaces may have sync methods, so grant
|
|
// them to avoid deadlocks.
|
|
// - Renderer might request these interfaces during the prerenderingchange
|
|
// event, because from the page's point of view it is no longer
|
|
// prerendering.
|
|
case MojoBinderNonAssociatedPolicy::kCancel:
|
|
case MojoBinderNonAssociatedPolicy::kUnexpected:
|
|
std::move(binder_callback).Run();
|
|
break;
|
|
case MojoBinderNonAssociatedPolicy::kDefer:
|
|
if (base::Contains(kSyncMethodInterfaces, interface_name)) {
|
|
std::move(binder_callback).Run();
|
|
} else {
|
|
deferred_binders_.push_back(std::move(binder_callback));
|
|
}
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
DCHECK_EQ(mode_, Mode::kEnforce);
|
|
switch (policy) {
|
|
case MojoBinderNonAssociatedPolicy::kGrant:
|
|
std::move(binder_callback).Run();
|
|
break;
|
|
case MojoBinderNonAssociatedPolicy::kCancel:
|
|
if (cancel_callback_) {
|
|
std::move(cancel_callback_).Run(interface_name);
|
|
}
|
|
break;
|
|
case MojoBinderNonAssociatedPolicy::kDefer:
|
|
if (base::Contains(kSyncMethodInterfaces, interface_name)) {
|
|
deferred_sync_binders_.push_back(std::move(binder_callback));
|
|
} else {
|
|
deferred_binders_.push_back(std::move(binder_callback));
|
|
}
|
|
break;
|
|
case MojoBinderNonAssociatedPolicy::kUnexpected:
|
|
mojo::ReportBadMessage("MBPA_BAD_INTERFACE: " + interface_name);
|
|
if (cancel_callback_) {
|
|
std::move(cancel_callback_).Run(interface_name);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool MojoBinderPolicyApplier::ApplyPolicyToAssociatedBinder(
|
|
const std::string& interface_name) {
|
|
MojoBinderAssociatedPolicy policy = MojoBinderAssociatedPolicy::kCancel;
|
|
switch (mode_) {
|
|
// Always allow binders to run.
|
|
case Mode::kGrantAll:
|
|
case Mode::kPrepareToGrantAll:
|
|
return true;
|
|
case Mode::kEnforce:
|
|
policy = policy_map_->GetAssociatedMojoBinderPolicy(
|
|
interface_name, MojoBinderAssociatedPolicy::kCancel);
|
|
if (policy != MojoBinderAssociatedPolicy::kGrant) {
|
|
if (cancel_callback_)
|
|
std::move(cancel_callback_).Run(interface_name);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void MojoBinderPolicyApplier::PrepareToGrantAll() {
|
|
DCHECK_EQ(mode_, Mode::kEnforce);
|
|
|
|
// The remote side would think its status has changed after the browser
|
|
// executes this method, so it is safe to send some synchronous method, so the
|
|
// browser side should make the IPC pipeline ready.
|
|
for (auto& deferred_binder : deferred_sync_binders_) {
|
|
std::move(deferred_binder).Run();
|
|
}
|
|
deferred_sync_binders_.clear();
|
|
|
|
mode_ = Mode::kPrepareToGrantAll;
|
|
}
|
|
|
|
void MojoBinderPolicyApplier::GrantAll() {
|
|
DCHECK_NE(mode_, Mode::kGrantAll);
|
|
|
|
// Check that we are in a Mojo message dispatch, since the deferred binders
|
|
// might call mojo::ReportBadMessage().
|
|
//
|
|
// TODO(crbug.com/40185437): Give the deferred_binders_ a
|
|
// BadMessageCallback and forbid them from using mojo::ReportBadMessage()
|
|
// directly. We are currently in the message stack of one of the PageBroadcast
|
|
// Mojo callbacks handled by RenderViewHost, so if a binder calls
|
|
// mojo::ReportBadMessage() it kills possibly the wrong renderer. Even if we
|
|
// only run the binders associated with the RVH for each message per-RVH,
|
|
// there are still subtle problems with running all these callbacks at once:
|
|
// for example, mojo::GetMessageCallback()/mojo::ReportBadMessage() can only
|
|
// be called once per message dispatch.
|
|
DCHECK(mojo::IsInMessageDispatch());
|
|
|
|
mode_ = Mode::kGrantAll;
|
|
|
|
// It's safe to iterate over `deferred_binders_` because no more callbacks
|
|
// will be added to it once `grant_all_` is true."
|
|
for (auto& deferred_binder : deferred_binders_)
|
|
std::move(deferred_binder).Run();
|
|
deferred_binders_.clear();
|
|
}
|
|
|
|
void MojoBinderPolicyApplier::DropDeferredBinders() {
|
|
deferred_binders_.clear();
|
|
}
|
|
|
|
MojoBinderNonAssociatedPolicy
|
|
MojoBinderPolicyApplier::GetNonAssociatedMojoBinderPolicy(
|
|
const std::string& interface_name) const {
|
|
return policy_map_->GetNonAssociatedMojoBinderPolicy(interface_name,
|
|
default_policy_);
|
|
}
|
|
|
|
} // namespace content
|