| // 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 "base/functional/callback.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/test/bind.h" |
| #include "base/test/task_environment.h" |
| #include "content/browser/mojo_binder_policy_map_impl.h" |
| #include "content/public/test/mojo_capability_control_test_interfaces.mojom.h" |
| #include "mojo/public/cpp/bindings/generic_pending_receiver.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace content { |
| |
| // A test class that implements test interfaces and provides verification |
| // methods. |
| class TestReceiverCollector : public mojom::TestInterfaceForDefer, |
| public mojom::TestInterfaceForGrant, |
| public mojom::TestInterfaceForCancel, |
| public mojom::TestInterfaceForUnexpected { |
| public: |
| TestReceiverCollector() = default; |
| |
| ~TestReceiverCollector() override = default; |
| |
| // Deletes copy and move operations. |
| TestReceiverCollector(const TestReceiverCollector& other) = delete; |
| TestReceiverCollector& operator=(const TestReceiverCollector& other) = delete; |
| TestReceiverCollector(TestReceiverCollector&&) = delete; |
| TestReceiverCollector& operator=(TestReceiverCollector&&) = delete; |
| |
| void BindDeferInterface( |
| mojo::PendingReceiver<mojom::TestInterfaceForDefer> receiver) { |
| ASSERT_FALSE(defer_receiver_.is_bound()); |
| defer_receiver_.Bind(std::move(receiver)); |
| } |
| |
| void BindGrantInterface( |
| mojo::PendingReceiver<mojom::TestInterfaceForGrant> receiver) { |
| ASSERT_FALSE(grant_receiver_.is_bound()); |
| grant_receiver_.Bind(std::move(receiver)); |
| } |
| |
| void BindCancelInterface( |
| mojo::PendingReceiver<mojom::TestInterfaceForCancel> receiver) { |
| ASSERT_FALSE(cancel_receiver_.is_bound()); |
| cancel_receiver_.Bind(std::move(receiver)); |
| } |
| |
| void BindUnexpectedInterface( |
| mojo::PendingReceiver<mojom::TestInterfaceForUnexpected> receiver) { |
| ASSERT_FALSE(unexpected_receiver_.is_bound()); |
| unexpected_receiver_.Bind(std::move(receiver)); |
| } |
| |
| // mojom::TestInterfaceForDefer implementation. |
| void Ping(PingCallback callback) override { NOTREACHED(); } |
| |
| // Will be called when MojoBinderPolicyApplier::ApplyPolicyToBinder() |
| // handles a kCancel binding request. |
| void Cancel(const std::string& interface_name) { |
| is_cancelled_ = true; |
| cancelled_interface_ = interface_name; |
| } |
| |
| // Used to check if the cancel_closure of MojoBinderPolicyApplier was |
| // executed. |
| bool IsCancelled() { return is_cancelled_; } |
| |
| const std::string& cancelled_interface() const { |
| return cancelled_interface_; |
| } |
| |
| bool IsDeferReceiverBound() const { return defer_receiver_.is_bound(); } |
| |
| bool IsGrantReceiverBound() const { return grant_receiver_.is_bound(); } |
| |
| bool IsCancelReceiverBound() const { return cancel_receiver_.is_bound(); } |
| |
| bool IsUnexpectedReceiverBound() const { |
| return unexpected_receiver_.is_bound(); |
| } |
| |
| private: |
| mojo::Receiver<mojom::TestInterfaceForDefer> defer_receiver_{this}; |
| mojo::Receiver<mojom::TestInterfaceForGrant> grant_receiver_{this}; |
| mojo::Receiver<mojom::TestInterfaceForCancel> cancel_receiver_{this}; |
| mojo::Receiver<mojom::TestInterfaceForUnexpected> unexpected_receiver_{this}; |
| bool is_cancelled_ = false; |
| std::string cancelled_interface_; |
| }; |
| |
| class MojoBinderPolicyApplierTest : public testing::Test, |
| mojom::MojoContextProvider { |
| public: |
| MojoBinderPolicyApplierTest() = default; |
| |
| // mojom::MojoContextProvider |
| void GrantAll() override { policy_applier_.GrantAll(); } |
| |
| protected: |
| std::vector<base::OnceClosure>& deferred_binders() { |
| return policy_applier_.deferred_binders_; |
| } |
| |
| // Calls MojoBinderPolicyApplier::GrantAll() inside a Mojo message dispatch |
| // stack. |
| void RunGrantAll() { |
| DCHECK(!receiver_.is_bound()); |
| receiver_.Bind(remote_.BindNewPipeAndPassReceiver()); |
| remote_->GrantAll(); |
| remote_.FlushForTesting(); |
| } |
| |
| const MojoBinderPolicyMapImpl policy_map_{ |
| {{"content.mojom.TestInterfaceForDefer", |
| MojoBinderNonAssociatedPolicy::kDefer}, |
| {"content.mojom.TestInterfaceForGrant", |
| MojoBinderNonAssociatedPolicy::kGrant}, |
| {"content.mojom.TestInterfaceForCancel", |
| MojoBinderNonAssociatedPolicy::kCancel}, |
| {"content.mojom.TestInterfaceForUnexpected", |
| MojoBinderNonAssociatedPolicy::kUnexpected}}}; |
| TestReceiverCollector collector_{}; |
| MojoBinderPolicyApplier policy_applier_{ |
| &policy_map_, base::BindOnce(&TestReceiverCollector::Cancel, |
| base::Unretained(&collector_))}; |
| |
| private: |
| base::test::TaskEnvironment task_environment_; |
| mojo::Remote<mojom::MojoContextProvider> remote_; |
| mojo::Receiver<mojom::MojoContextProvider> receiver_{this}; |
| }; |
| |
| // Verifies that interfaces whose policies are kGrant can be bound immediately. |
| TEST_F(MojoBinderPolicyApplierTest, GrantInEnforce) { |
| // Initialize Mojo interfaces. |
| mojo::Remote<mojom::TestInterfaceForGrant> grant_remote; |
| mojo::GenericPendingReceiver grant_receiver( |
| grant_remote.BindNewPipeAndPassReceiver()); |
| |
| // Bind the interface immediately if the policy is kGrant. |
| const std::string interface_name = grant_receiver.interface_name().value(); |
| EXPECT_FALSE(collector_.IsCancelled()); |
| EXPECT_FALSE(collector_.IsGrantReceiverBound()); |
| policy_applier_.ApplyPolicyToNonAssociatedBinder( |
| interface_name, |
| base::BindOnce(&TestReceiverCollector::BindGrantInterface, |
| base::Unretained(&collector_), |
| grant_receiver.As<mojom::TestInterfaceForGrant>())); |
| EXPECT_TRUE(collector_.IsGrantReceiverBound()); |
| EXPECT_FALSE(collector_.IsCancelled()); |
| } |
| |
| // Verifies that interfaces whose policies are kDefer cannot be bound until |
| // GrantAll() is called. |
| TEST_F(MojoBinderPolicyApplierTest, DeferInEnforce) { |
| // Initialize Mojo interfaces. |
| mojo::Remote<mojom::TestInterfaceForDefer> defer_remote; |
| mojo::GenericPendingReceiver defer_receiver( |
| defer_remote.BindNewPipeAndPassReceiver()); |
| |
| // Delay binding the interface until GrantAll() is called. |
| const std::string interface_name = defer_receiver.interface_name().value(); |
| EXPECT_FALSE(collector_.IsCancelled()); |
| policy_applier_.ApplyPolicyToNonAssociatedBinder( |
| interface_name, |
| base::BindOnce(&TestReceiverCollector::BindDeferInterface, |
| base::Unretained(&collector_), |
| defer_receiver.As<mojom::TestInterfaceForDefer>())); |
| EXPECT_FALSE(collector_.IsDeferReceiverBound()); |
| EXPECT_EQ(1U, deferred_binders().size()); |
| |
| RunGrantAll(); |
| EXPECT_EQ(0U, deferred_binders().size()); |
| EXPECT_TRUE(collector_.IsDeferReceiverBound()); |
| EXPECT_FALSE(collector_.IsCancelled()); |
| } |
| |
| // Verifies that MojoBinderPolicyApplier will run `cancel_closure` when running |
| // in the kEnforce mode and receiving an interface whose policy is kCancel, |
| TEST_F(MojoBinderPolicyApplierTest, CancelInEnforce) { |
| // Initialize Mojo interfaces. |
| mojo::Remote<mojom::TestInterfaceForCancel> cancel_remote; |
| mojo::GenericPendingReceiver cancel_receiver( |
| cancel_remote.BindNewPipeAndPassReceiver()); |
| |
| const std::string interface_name = cancel_receiver.interface_name().value(); |
| EXPECT_FALSE(collector_.IsCancelled()); |
| EXPECT_FALSE(collector_.IsCancelReceiverBound()); |
| policy_applier_.ApplyPolicyToNonAssociatedBinder( |
| interface_name, |
| base::BindOnce(&TestReceiverCollector::BindCancelInterface, |
| base::Unretained(&collector_), |
| cancel_receiver.As<mojom::TestInterfaceForCancel>())); |
| EXPECT_TRUE(collector_.IsCancelled()); |
| EXPECT_EQ(collector_.cancelled_interface(), |
| "content.mojom.TestInterfaceForCancel"); |
| EXPECT_FALSE(collector_.IsCancelReceiverBound()); |
| } |
| |
| // When MojoBinderPolicyApplier runs in the kPrepareToGrantAll mode, verifies it |
| // applies kGrant for kGrant interfaces. |
| TEST_F(MojoBinderPolicyApplierTest, GrantInPrepareToGrantAll) { |
| // Initialize Mojo interfaces. |
| mojo::Remote<mojom::TestInterfaceForGrant> grant_remote; |
| mojo::GenericPendingReceiver grant_receiver( |
| grant_remote.BindNewPipeAndPassReceiver()); |
| |
| policy_applier_.PrepareToGrantAll(); |
| const std::string grant_interface_name = |
| grant_receiver.interface_name().value(); |
| policy_applier_.ApplyPolicyToNonAssociatedBinder( |
| grant_interface_name, |
| base::BindOnce(&TestReceiverCollector::BindGrantInterface, |
| base::Unretained(&collector_), |
| grant_receiver.As<mojom::TestInterfaceForGrant>())); |
| EXPECT_TRUE(collector_.IsGrantReceiverBound()); |
| } |
| |
| // When MojoBinderPolicyApplier runs in the kPrepareToGrantAll mode, verifies it |
| // applies kDefer for kDefer interfaces. |
| TEST_F(MojoBinderPolicyApplierTest, DeferInPrepareToGrantAll) { |
| // Initialize Mojo interfaces. |
| mojo::Remote<mojom::TestInterfaceForDefer> defer_remote; |
| mojo::GenericPendingReceiver defer_receiver( |
| defer_remote.BindNewPipeAndPassReceiver()); |
| |
| policy_applier_.PrepareToGrantAll(); |
| const std::string defer_interface_name = |
| defer_receiver.interface_name().value(); |
| policy_applier_.ApplyPolicyToNonAssociatedBinder( |
| defer_interface_name, |
| base::BindOnce(&TestReceiverCollector::BindDeferInterface, |
| base::Unretained(&collector_), |
| defer_receiver.As<mojom::TestInterfaceForDefer>())); |
| EXPECT_FALSE(collector_.IsDeferReceiverBound()); |
| EXPECT_EQ(1U, deferred_binders().size()); |
| |
| RunGrantAll(); |
| EXPECT_TRUE(collector_.IsDeferReceiverBound()); |
| EXPECT_EQ(0U, deferred_binders().size()); |
| } |
| |
| // When MojoBinderPolicyApplier runs in the kPrepareToGrantAll mode, verifies it |
| // applies kGrant rather than kCancel policy when receiving a kCancel interface |
| // binding request. |
| TEST_F(MojoBinderPolicyApplierTest, CancelInPrepareToGrantAll) { |
| // Initialize Mojo interfaces. |
| mojo::Remote<mojom::TestInterfaceForCancel> cancel_remote; |
| mojo::GenericPendingReceiver cancel_receiver( |
| cancel_remote.BindNewPipeAndPassReceiver()); |
| |
| policy_applier_.PrepareToGrantAll(); |
| const std::string cancel_interface_name = |
| cancel_receiver.interface_name().value(); |
| policy_applier_.ApplyPolicyToNonAssociatedBinder( |
| cancel_interface_name, |
| base::BindOnce(&TestReceiverCollector::BindCancelInterface, |
| base::Unretained(&collector_), |
| cancel_receiver.As<mojom::TestInterfaceForCancel>())); |
| EXPECT_FALSE(collector_.IsCancelled()); |
| EXPECT_TRUE(collector_.IsCancelReceiverBound()); |
| } |
| |
| TEST_F(MojoBinderPolicyApplierTest, UnexpectedInPrepareToGrantAll) { |
| // Initialize Mojo interfaces. |
| mojo::Remote<mojom::TestInterfaceForUnexpected> unexpected_remote; |
| mojo::GenericPendingReceiver unexpected_receiver( |
| unexpected_remote.BindNewPipeAndPassReceiver()); |
| |
| policy_applier_.PrepareToGrantAll(); |
| const std::string interface_name = |
| unexpected_receiver.interface_name().value(); |
| policy_applier_.ApplyPolicyToNonAssociatedBinder( |
| interface_name, |
| base::BindOnce( |
| &TestReceiverCollector::BindUnexpectedInterface, |
| base::Unretained(&collector_), |
| unexpected_receiver.As<mojom::TestInterfaceForUnexpected>())); |
| EXPECT_FALSE(collector_.IsCancelled()); |
| EXPECT_TRUE(collector_.IsUnexpectedReceiverBound()); |
| } |
| |
| // Verifies that all interfaces are bound immediately if GrantAll() is called, |
| // regardless of policies. |
| TEST_F(MojoBinderPolicyApplierTest, BindInterfacesAfterResolving) { |
| // Initialize Mojo interfaces. |
| mojo::Remote<mojom::TestInterfaceForDefer> defer_remote; |
| mojo::GenericPendingReceiver defer_receiver( |
| defer_remote.BindNewPipeAndPassReceiver()); |
| mojo::Remote<mojom::TestInterfaceForGrant> grant_remote; |
| mojo::GenericPendingReceiver grant_receiver( |
| grant_remote.BindNewPipeAndPassReceiver()); |
| mojo::Remote<mojom::TestInterfaceForCancel> cancel_remote; |
| mojo::GenericPendingReceiver cancel_receiver( |
| cancel_remote.BindNewPipeAndPassReceiver()); |
| mojo::Remote<mojom::TestInterfaceForUnexpected> unexpected_remote; |
| mojo::GenericPendingReceiver unexpected_receiver( |
| unexpected_remote.BindNewPipeAndPassReceiver()); |
| |
| RunGrantAll(); |
| |
| // All interfaces should be bound immediately. |
| const std::string defer_interface_name = |
| defer_receiver.interface_name().value(); |
| const std::string grant_interface_name = |
| grant_receiver.interface_name().value(); |
| const std::string cancel_interface_name = |
| cancel_receiver.interface_name().value(); |
| const std::string unexpected_interface_name = |
| unexpected_receiver.interface_name().value(); |
| EXPECT_FALSE(collector_.IsCancelled()); |
| EXPECT_FALSE(collector_.IsGrantReceiverBound()); |
| EXPECT_FALSE(collector_.IsDeferReceiverBound()); |
| EXPECT_FALSE(collector_.IsCancelReceiverBound()); |
| EXPECT_FALSE(collector_.IsUnexpectedReceiverBound()); |
| policy_applier_.ApplyPolicyToNonAssociatedBinder( |
| defer_interface_name, |
| base::BindOnce(&TestReceiverCollector::BindDeferInterface, |
| base::Unretained(&collector_), |
| defer_receiver.As<mojom::TestInterfaceForDefer>())); |
| policy_applier_.ApplyPolicyToNonAssociatedBinder( |
| grant_interface_name, |
| base::BindOnce(&TestReceiverCollector::BindGrantInterface, |
| base::Unretained(&collector_), |
| grant_receiver.As<mojom::TestInterfaceForGrant>())); |
| policy_applier_.ApplyPolicyToNonAssociatedBinder( |
| cancel_interface_name, |
| base::BindOnce(&TestReceiverCollector::BindCancelInterface, |
| base::Unretained(&collector_), |
| cancel_receiver.As<mojom::TestInterfaceForCancel>())); |
| policy_applier_.ApplyPolicyToNonAssociatedBinder( |
| unexpected_interface_name, |
| base::BindOnce( |
| &TestReceiverCollector::BindUnexpectedInterface, |
| base::Unretained(&collector_), |
| unexpected_receiver.As<mojom::TestInterfaceForUnexpected>())); |
| EXPECT_TRUE(collector_.IsGrantReceiverBound()); |
| EXPECT_TRUE(collector_.IsDeferReceiverBound()); |
| EXPECT_TRUE(collector_.IsCancelReceiverBound()); |
| EXPECT_TRUE(collector_.IsUnexpectedReceiverBound()); |
| EXPECT_FALSE(collector_.IsCancelled()); |
| EXPECT_EQ(0U, deferred_binders().size()); |
| } |
| |
| // Verifies that DropDeferredBinders() deletes all deferred binders. |
| TEST_F(MojoBinderPolicyApplierTest, DropDeferredBinders) { |
| // Initialize Mojo interfaces. |
| mojo::Remote<mojom::TestInterfaceForDefer> defer_remote; |
| mojo::GenericPendingReceiver defer_receiver( |
| defer_remote.BindNewPipeAndPassReceiver()); |
| |
| const std::string interface_name = defer_receiver.interface_name().value(); |
| EXPECT_FALSE(collector_.IsCancelled()); |
| policy_applier_.ApplyPolicyToNonAssociatedBinder( |
| interface_name, |
| base::BindOnce(&TestReceiverCollector::BindDeferInterface, |
| base::Unretained(&collector_), |
| defer_receiver.As<mojom::TestInterfaceForDefer>())); |
| EXPECT_FALSE(collector_.IsDeferReceiverBound()); |
| EXPECT_EQ(1U, deferred_binders().size()); |
| policy_applier_.DropDeferredBinders(); |
| EXPECT_EQ(0U, deferred_binders().size()); |
| RunGrantAll(); |
| EXPECT_FALSE(collector_.IsDeferReceiverBound()); |
| } |
| |
| } // namespace content |