// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ipc/ipc_sync_channel.h" #include <stddef.h> #include <memory> #include <string> #include <utility> #include <vector> #include "base/functional/bind.h" #include "base/location.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" #include "base/memory/raw_ptr_exclusion.h" #include "base/message_loop/message_pump_type.h" #include "base/process/process_handle.h" #include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/synchronization/waitable_event.h" #include "base/task/single_thread_task_runner.h" #include "base/test/task_environment.h" #include "base/threading/platform_thread.h" #include "base/threading/thread.h" #include "build/build_config.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_message.h" #include "ipc/ipc_sender.h" #include "ipc/ipc_sync_message_filter.h" #include "ipc/ipc_sync_message_unittest.h" #include "mojo/public/cpp/system/message_pipe.h" #include "testing/gtest/include/gtest/gtest.h" WaitableEvent; namespace IPC { namespace { // Base class for a "process" with listener and IPC threads. class Worker : public Listener, public Sender { … }; // Starts the test with the given workers. This function deletes the workers // when it's done. void RunTest(std::vector<Worker*> workers) { … } class IPCSyncChannelTest : public testing::Test { … }; //------------------------------------------------------------------------------ class SimpleServer : public Worker { … }; class SimpleClient : public Worker { … }; void Simple() { … } #if BUILDFLAG(IS_ANDROID) #define MAYBE_Simple … #else #define MAYBE_Simple … #endif // Tests basic synchronous call TEST_F(IPCSyncChannelTest, MAYBE_Simple) { … } //------------------------------------------------------------------------------ // Worker classes which override how the sync channel is created to use the // two-step initialization (calling the lightweight constructor and then // ChannelProxy::Init separately) process. class TwoStepServer : public Worker { … }; class TwoStepClient : public Worker { … }; void TwoStep(bool create_server_pipe_now, bool create_client_pipe_now) { … } // Tests basic two-step initialization, where you call the lightweight // constructor then Init. TEST_F(IPCSyncChannelTest, TwoStepInitialization) { … } //------------------------------------------------------------------------------ class DelayClient : public Worker { … }; void DelayReply() { … } // Tests that asynchronous replies work TEST_F(IPCSyncChannelTest, DelayReply) { … } //------------------------------------------------------------------------------ class NoHangServer : public Worker { … }; class NoHangClient : public Worker { … }; void NoHang() { … } // Tests that caller doesn't hang if receiver dies TEST_F(IPCSyncChannelTest, NoHang) { … } //------------------------------------------------------------------------------ class UnblockServer : public Worker { … }; class UnblockClient : public Worker { … }; void Unblock(bool delete_during_send) { … } // Tests that the caller unblocks to answer a sync message from the receiver. TEST_F(IPCSyncChannelTest, Unblock) { … } //------------------------------------------------------------------------------ #if BUILDFLAG(IS_ANDROID) #define MAYBE_ChannelDeleteDuringSend … #else #define MAYBE_ChannelDeleteDuringSend … #endif // Tests that the the SyncChannel object can be deleted during a Send. TEST_F(IPCSyncChannelTest, MAYBE_ChannelDeleteDuringSend) { … } //------------------------------------------------------------------------------ class RecursiveServer : public Worker { … }; class RecursiveClient : public Worker { … }; void Recursive() { … } // Tests a server calling Send while another Send is pending. TEST_F(IPCSyncChannelTest, Recursive) { … } //------------------------------------------------------------------------------ void RecursiveNoHang() { … } // Tests that if a caller makes a sync call during an existing sync call and // the receiver dies, neither of the Send() calls hang. TEST_F(IPCSyncChannelTest, RecursiveNoHang) { … } //------------------------------------------------------------------------------ class MultipleServer1 : public Worker { … }; class MultipleClient1 : public Worker { … }; class MultipleServer2 : public Worker { … }; class MultipleClient2 : public Worker { … }; void Multiple() { … } // Tests that multiple SyncObjects on the same listener thread can unblock each // other. TEST_F(IPCSyncChannelTest, Multiple) { … } //------------------------------------------------------------------------------ // This class provides server side functionality to test the case where // multiple sync channels are in use on the same thread on the client. class QueuedReplyServer : public Worker { … }; // The QueuedReplyClient class provides functionality to test the case where // multiple sync channels are in use on the same thread. class QueuedReplyClient : public Worker { … }; void QueuedReply() { … } // While a blocking send is in progress, the listener thread might answer other // synchronous messages. This tests that if during the response to another // message the reply to the original messages comes, it is queued up correctly // and the original Send is unblocked later. TEST_F(IPCSyncChannelTest, QueuedReply) { … } //------------------------------------------------------------------------------ class TestSyncMessageFilter : public SyncMessageFilter { … }; class SyncMessageFilterServer : public Worker { … }; // This class provides functionality to test the case that a Send on the sync // channel does not crash after the channel has been closed. class ServerSendAfterClose : public Worker { … }; // Tests basic synchronous call TEST_F(IPCSyncChannelTest, SyncMessageFilter) { … } // Test the case when the channel is closed and a Send is attempted after that. TEST_F(IPCSyncChannelTest, SendAfterClose) { … } //------------------------------------------------------------------------------ class RestrictedDispatchServer : public Worker { … }; class NonRestrictedDispatchServer : public Worker { … }; class RestrictedDispatchClient : public Worker { … }; TEST_F(IPCSyncChannelTest, RestrictedDispatch) { … } //------------------------------------------------------------------------------ // This test case inspired by crbug.com/108491 // We create two servers that use the same ListenerThread but have // SetRestrictDispatchToSameChannel set to true. // We create clients, then use some specific WaitableEvent wait/signalling to // ensure that messages get dispatched in a way that causes a deadlock due to // a nested dispatch and an eligible message in a higher-level dispatch's // delayed_queue. Specifically, we start with client1 about so send an // unblocking message to server1, while the shared listener thread for the // servers server1 and server2 is about to send a non-unblocking message to // client1. At the same time, client2 will be about to send an unblocking // message to server2. Server1 will handle the client1->server1 message by // telling server2 to send a non-unblocking message to client2. // What should happen is that the send to server2 should find the pending, // same-context client2->server2 message to dispatch, causing client2 to // unblock then handle the server2->client2 message, so that the shared // servers' listener thread can then respond to the client1->server1 message. // Then client1 can handle the non-unblocking server1->client1 message. // The old code would end up in a state where the server2->client2 message is // sent, but the client2->server2 message (which is eligible for dispatch, and // which is what client2 is waiting for) is stashed in a local delayed_queue // that has server1's channel context, causing a deadlock. // WaitableEvents in the events array are used to: // event 0: indicate to client1 that server listener is in OnDoServerTask // event 1: indicate to client1 that client2 listener is in OnDoClient2Task // event 2: indicate to server1 that client2 listener is in OnDoClient2Task // event 3: indicate to client2 that server listener is in OnDoServerTask class RestrictedDispatchDeadlockServer : public Worker { … }; class RestrictedDispatchDeadlockClient2 : public Worker { … }; class RestrictedDispatchDeadlockClient1 : public Worker { … }; TEST_F(IPCSyncChannelTest, RestrictedDispatchDeadlock) { … } //------------------------------------------------------------------------------ // This test case inspired by crbug.com/120530 // We create 4 workers that pipe to each other W1->W2->W3->W4->W1 then we send a // message that recurses through 3, 4 or 5 steps to make sure, say, W1 can // re-enter when called from W4 while it's sending a message to W2. // The first worker drives the whole test so it must be treated specially. class RestrictedDispatchPipeWorker : public Worker { … }; #if BUILDFLAG(IS_ANDROID) #define MAYBE_RestrictedDispatch4WayDeadlock … #else #define MAYBE_RestrictedDispatch4WayDeadlock … #endif TEST_F(IPCSyncChannelTest, MAYBE_RestrictedDispatch4WayDeadlock) { … } //------------------------------------------------------------------------------ // This test case inspired by crbug.com/122443 // We want to make sure a reply message with the unblock flag set correctly // behaves as a reply, not a regular message. // We have 3 workers. Server1 will send a message to Server2 (which will block), // during which it will dispatch a message comming from Client, at which point // it will send another message to Server2. While sending that second message it // will receive a reply from Server1 with the unblock flag. class ReentrantReplyServer1 : public Worker { … }; class ReentrantReplyServer2 : public Worker { … }; class ReentrantReplyClient : public Worker { … }; TEST_F(IPCSyncChannelTest, ReentrantReply) { … } } // namespace } // namespace IPC