1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "remoting/host/win/worker_process_launcher.h"
6 
7 #include <stdint.h>
8 
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/macros.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/run_loop.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/test/task_environment.h"
17 #include "base/win/scoped_handle.h"
18 #include "base/win/scoped_process_information.h"
19 #include "ipc/ipc_channel.h"
20 #include "ipc/ipc_channel_proxy.h"
21 #include "ipc/ipc_listener.h"
22 #include "ipc/ipc_message.h"
23 #include "mojo/public/cpp/system/message_pipe.h"
24 #include "remoting/base/auto_thread_task_runner.h"
25 #include "remoting/host/chromoting_messages.h"
26 #include "remoting/host/host_exit_codes.h"
27 #include "remoting/host/win/launch_process_with_token.h"
28 #include "remoting/host/worker_process_ipc_delegate.h"
29 #include "testing/gmock/include/gmock/gmock.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 
32 using base::win::ScopedHandle;
33 using testing::_;
34 using testing::AnyNumber;
35 using testing::Expectation;
36 using testing::Invoke;
37 using testing::InvokeWithoutArgs;
38 
39 namespace remoting {
40 
41 namespace {
42 
43 class MockProcessLauncherDelegate : public WorkerProcessLauncher::Delegate {
44  public:
MockProcessLauncherDelegate()45   MockProcessLauncherDelegate() {}
~MockProcessLauncherDelegate()46   ~MockProcessLauncherDelegate() override {}
47 
48   // WorkerProcessLauncher::Delegate interface.
49   MOCK_METHOD1(LaunchProcess, void(WorkerProcessLauncher*));
50   MOCK_METHOD1(Send, void(IPC::Message*));
51   MOCK_METHOD0(CloseChannel, void());
52   MOCK_METHOD0(KillProcess, void());
53 
54  private:
55   DISALLOW_COPY_AND_ASSIGN(MockProcessLauncherDelegate);
56 };
57 
58 class MockIpcDelegate : public WorkerProcessIpcDelegate {
59  public:
MockIpcDelegate()60   MockIpcDelegate() {}
~MockIpcDelegate()61   ~MockIpcDelegate() override {}
62 
63   // WorkerProcessIpcDelegate interface.
64   MOCK_METHOD1(OnChannelConnected, void(int32_t));
65   MOCK_METHOD1(OnMessageReceived, bool(const IPC::Message&));
66   MOCK_METHOD1(OnPermanentError, void(int));
67   MOCK_METHOD0(OnWorkerProcessStopped, void());
68 
69  private:
70   DISALLOW_COPY_AND_ASSIGN(MockIpcDelegate);
71 };
72 
73 class MockWorkerListener : public IPC::Listener {
74  public:
MockWorkerListener()75   MockWorkerListener() {}
~MockWorkerListener()76   ~MockWorkerListener() override {}
77 
78   MOCK_METHOD3(OnCrash, void(const std::string&, const std::string&, int));
79 
80   // IPC::Listener implementation
81   bool OnMessageReceived(const IPC::Message& message) override;
82 
83  private:
84   DISALLOW_COPY_AND_ASSIGN(MockWorkerListener);
85 };
86 
OnMessageReceived(const IPC::Message & message)87 bool MockWorkerListener::OnMessageReceived(const IPC::Message& message) {
88   bool handled = true;
89   IPC_BEGIN_MESSAGE_MAP(MockWorkerListener, message)
90     IPC_MESSAGE_HANDLER(ChromotingDaemonMsg_Crash, OnCrash)
91     IPC_MESSAGE_UNHANDLED(handled = false)
92   IPC_END_MESSAGE_MAP()
93 
94   EXPECT_TRUE(handled);
95 
96   return handled;
97 }
98 
99 }  // namespace
100 
101 class WorkerProcessLauncherTest
102     : public testing::Test,
103       public IPC::Listener {
104  public:
105   WorkerProcessLauncherTest();
106   ~WorkerProcessLauncherTest() override;
107 
108   void SetUp() override;
109   void TearDown() override;
110 
111   // IPC::Listener implementation.
112   bool OnMessageReceived(const IPC::Message& message) override;
113   void OnChannelConnected(int32_t peer_pid) override;
114   void OnChannelError() override;
115 
116   // WorkerProcessLauncher::Delegate mocks
117   void LaunchProcess(
118       WorkerProcessLauncher* event_handler);
119   void LaunchProcessAndConnect(
120       WorkerProcessLauncher* event_handler);
121   void FailLaunchAndStopWorker(
122       WorkerProcessLauncher* event_handler);
123   void KillProcess();
124 
125   void TerminateWorker(DWORD exit_code);
126 
127   // Connects the client end of the channel (the worker process's end).
128   void ConnectClient();
129 
130   // Disconnects the client end of the channel.
131   void DisconnectClient();
132 
133   // Disconnects the server end of the channel (the launcher's end).
134   void DisconnectServer();
135 
136   // Sends a message to the worker process.
137   void SendToProcess(IPC::Message* message);
138 
139   // Sends a fake message to the launcher.
140   void SendFakeMessageToLauncher();
141 
142   // Requests the worker to crash.
143   void CrashWorker();
144 
145   // Starts the worker.
146   void StartWorker();
147 
148   // Stops the worker.
149   void StopWorker();
150 
151   // Quits |message_loop_|.
152   void QuitMainMessageLoop();
153 
154  protected:
155   void DoLaunchProcess();
156 
157   base::test::SingleThreadTaskEnvironment task_environment_{
158       base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
159   scoped_refptr<AutoThreadTaskRunner> task_runner_;
160 
161   // Receives messages sent to the worker process.
162   MockWorkerListener client_listener_;
163 
164   // Receives messages sent from the worker process.
165   MockIpcDelegate server_listener_;
166 
167   // Implements WorkerProcessLauncher::Delegate.
168   std::unique_ptr<MockProcessLauncherDelegate> launcher_delegate_;
169 
170   // The client handle to the channel.
171   mojo::ScopedMessagePipeHandle client_channel_handle_;
172 
173   // Client and server ends of the IPC channel.
174   std::unique_ptr<IPC::ChannelProxy> channel_client_;
175   std::unique_ptr<IPC::ChannelProxy> channel_server_;
176 
177   WorkerProcessLauncher* event_handler_;
178 
179   // The worker process launcher.
180   std::unique_ptr<WorkerProcessLauncher> launcher_;
181 
182   // An event that is used to emulate the worker process's handle.
183   ScopedHandle worker_process_;
184 };
185 
WorkerProcessLauncherTest()186 WorkerProcessLauncherTest::WorkerProcessLauncherTest()
187     : event_handler_(nullptr) {
188 }
189 
~WorkerProcessLauncherTest()190 WorkerProcessLauncherTest::~WorkerProcessLauncherTest() {
191 }
192 
SetUp()193 void WorkerProcessLauncherTest::SetUp() {
194   task_runner_ = new AutoThreadTaskRunner(
195       task_environment_.GetMainThreadTaskRunner(),
196       base::BindOnce(&WorkerProcessLauncherTest::QuitMainMessageLoop,
197                      base::Unretained(this)));
198 
199   // Set up process launcher delegate
200   launcher_delegate_.reset(new MockProcessLauncherDelegate());
201   EXPECT_CALL(*launcher_delegate_, Send(_))
202       .Times(AnyNumber())
203       .WillRepeatedly(Invoke(this, &WorkerProcessLauncherTest::SendToProcess));
204   EXPECT_CALL(*launcher_delegate_, CloseChannel())
205       .Times(AnyNumber())
206       .WillRepeatedly(Invoke(this,
207                              &WorkerProcessLauncherTest::DisconnectServer));
208   EXPECT_CALL(*launcher_delegate_, KillProcess())
209       .Times(AnyNumber())
210       .WillRepeatedly(Invoke(this, &WorkerProcessLauncherTest::KillProcess));
211 
212   // Set up IPC delegate.
213   EXPECT_CALL(server_listener_, OnMessageReceived(_))
214       .Times(0);
215 }
216 
TearDown()217 void WorkerProcessLauncherTest::TearDown() {
218 }
219 
OnMessageReceived(const IPC::Message & message)220 bool WorkerProcessLauncherTest::OnMessageReceived(const IPC::Message& message) {
221   return event_handler_->OnMessageReceived(message);
222 }
223 
OnChannelConnected(int32_t peer_pid)224 void WorkerProcessLauncherTest::OnChannelConnected(int32_t peer_pid) {
225   event_handler_->OnChannelConnected(peer_pid);
226 }
227 
OnChannelError()228 void WorkerProcessLauncherTest::OnChannelError() {
229   event_handler_->OnChannelError();
230 }
231 
LaunchProcess(WorkerProcessLauncher * event_handler)232 void WorkerProcessLauncherTest::LaunchProcess(
233     WorkerProcessLauncher* event_handler) {
234   EXPECT_FALSE(event_handler_);
235   event_handler_ = event_handler;
236 
237   DoLaunchProcess();
238 }
239 
LaunchProcessAndConnect(WorkerProcessLauncher * event_handler)240 void WorkerProcessLauncherTest::LaunchProcessAndConnect(
241     WorkerProcessLauncher* event_handler) {
242   EXPECT_FALSE(event_handler_);
243   event_handler_ = event_handler;
244 
245   DoLaunchProcess();
246 
247   task_runner_->PostTask(
248       FROM_HERE, base::BindOnce(&WorkerProcessLauncherTest::ConnectClient,
249                                 base::Unretained(this)));
250 }
251 
FailLaunchAndStopWorker(WorkerProcessLauncher * event_handler)252 void WorkerProcessLauncherTest::FailLaunchAndStopWorker(
253     WorkerProcessLauncher* event_handler) {
254   EXPECT_FALSE(event_handler_);
255 
256   event_handler->OnFatalError();
257 
258   task_runner_->PostTask(FROM_HERE,
259                          base::BindOnce(&WorkerProcessLauncherTest::StopWorker,
260                                         base::Unretained(this)));
261 }
262 
KillProcess()263 void WorkerProcessLauncherTest::KillProcess() {
264   event_handler_ = nullptr;
265 
266   DisconnectClient();
267   if (worker_process_.IsValid()) {
268     TerminateProcess(worker_process_.Get(), CONTROL_C_EXIT);
269     worker_process_.Close();
270   }
271 }
272 
TerminateWorker(DWORD exit_code)273 void WorkerProcessLauncherTest::TerminateWorker(DWORD exit_code) {
274   if (worker_process_.IsValid())
275     TerminateProcess(worker_process_.Get(), exit_code);
276 }
277 
ConnectClient()278 void WorkerProcessLauncherTest::ConnectClient() {
279   channel_client_ = IPC::ChannelProxy::Create(
280       client_channel_handle_.release(), IPC::Channel::MODE_CLIENT,
281       &client_listener_, task_runner_, base::ThreadTaskRunnerHandle::Get());
282 
283   // Pretend that |kLaunchSuccessTimeoutSeconds| passed since launching
284   // the worker process. This will make the backoff algorithm think that this
285   // launch attempt was successful and it will not delay the next launch.
286   launcher_->RecordSuccessfulLaunchForTest();
287 }
288 
DisconnectClient()289 void WorkerProcessLauncherTest::DisconnectClient() {
290   if (channel_client_) {
291     channel_client_->Close();
292     channel_client_.reset();
293   }
294 }
295 
DisconnectServer()296 void WorkerProcessLauncherTest::DisconnectServer() {
297   if (channel_server_) {
298     channel_server_->Close();
299     channel_server_.reset();
300   }
301 }
302 
SendToProcess(IPC::Message * message)303 void WorkerProcessLauncherTest::SendToProcess(IPC::Message* message) {
304   if (channel_server_) {
305     channel_server_->Send(message);
306     return;
307   }
308 
309   delete message;
310 }
311 
SendFakeMessageToLauncher()312 void WorkerProcessLauncherTest::SendFakeMessageToLauncher() {
313   if (channel_client_) {
314     channel_client_->Send(
315         new ChromotingDesktopNetworkMsg_DisconnectSession(protocol::OK));
316   }
317 }
318 
CrashWorker()319 void WorkerProcessLauncherTest::CrashWorker() {
320   launcher_->Crash(FROM_HERE);
321 }
322 
StartWorker()323 void WorkerProcessLauncherTest::StartWorker() {
324   launcher_.reset(new WorkerProcessLauncher(std::move(launcher_delegate_),
325                                             &server_listener_));
326 
327   launcher_->SetKillProcessTimeoutForTest(
328       base::TimeDelta::FromMilliseconds(100));
329 }
330 
StopWorker()331 void WorkerProcessLauncherTest::StopWorker() {
332   launcher_.reset();
333   DisconnectClient();
334   client_channel_handle_.reset();
335   channel_server_.reset();
336   task_runner_ = nullptr;
337 }
338 
QuitMainMessageLoop()339 void WorkerProcessLauncherTest::QuitMainMessageLoop() {
340   task_environment_.GetMainThreadTaskRunner()->PostTask(
341       FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
342 }
343 
DoLaunchProcess()344 void WorkerProcessLauncherTest::DoLaunchProcess() {
345   EXPECT_TRUE(event_handler_);
346   EXPECT_FALSE(worker_process_.IsValid());
347 
348   WCHAR notepad[MAX_PATH + 1];
349   ASSERT_GT(ExpandEnvironmentStrings(
350       L"\045SystemRoot\045\\system32\\notepad.exe", notepad, MAX_PATH), 0u);
351 
352   STARTUPINFOW startup_info = { 0 };
353   startup_info.cb = sizeof(startup_info);
354 
355   PROCESS_INFORMATION temp_process_info = {};
356   ASSERT_TRUE(CreateProcess(nullptr,
357                             notepad,
358                             nullptr,   // default process attibutes
359                             nullptr,   // default thread attibutes
360                             FALSE,  // do not inherit handles
361                             CREATE_SUSPENDED,
362                             nullptr,   // no environment
363                             nullptr,   // default current directory
364                             &startup_info,
365                             &temp_process_info));
366   base::win::ScopedProcessInformation process_information(temp_process_info);
367   worker_process_.Set(process_information.TakeProcessHandle());
368   ASSERT_TRUE(worker_process_.IsValid());
369 
370   mojo::MessagePipe pipe;
371   client_channel_handle_ = std::move(pipe.handle0);
372 
373   // Wrap the pipe into an IPC channel.
374   channel_server_ = IPC::ChannelProxy::Create(
375       pipe.handle1.release(), IPC::Channel::MODE_SERVER, this, task_runner_,
376       base::ThreadTaskRunnerHandle::Get());
377 
378   HANDLE temp_handle;
379   ASSERT_TRUE(DuplicateHandle(GetCurrentProcess(), worker_process_.Get(),
380                               GetCurrentProcess(), &temp_handle, 0, FALSE,
381                               DUPLICATE_SAME_ACCESS));
382   event_handler_->OnProcessLaunched(ScopedHandle(temp_handle));
383 }
384 
TEST_F(WorkerProcessLauncherTest,Start)385 TEST_F(WorkerProcessLauncherTest, Start) {
386   EXPECT_CALL(*launcher_delegate_, LaunchProcess(_))
387       .Times(1)
388       .WillRepeatedly(Invoke(this, &WorkerProcessLauncherTest::LaunchProcess));
389 
390   EXPECT_CALL(server_listener_, OnChannelConnected(_))
391       .Times(0);
392   EXPECT_CALL(server_listener_, OnPermanentError(_))
393       .Times(0);
394   EXPECT_CALL(server_listener_, OnWorkerProcessStopped())
395       .Times(0);
396 
397   StartWorker();
398   StopWorker();
399   base::RunLoop().Run();
400 }
401 
402 // Starts and connects to the worker process. Expect OnChannelConnected to be
403 // called.
TEST_F(WorkerProcessLauncherTest,StartAndConnect)404 TEST_F(WorkerProcessLauncherTest, StartAndConnect) {
405   EXPECT_CALL(*launcher_delegate_, LaunchProcess(_))
406       .Times(1)
407       .WillRepeatedly(Invoke(
408           this, &WorkerProcessLauncherTest::LaunchProcessAndConnect));
409 
410   EXPECT_CALL(server_listener_, OnChannelConnected(_))
411       .Times(1)
412       .WillOnce(InvokeWithoutArgs(this,
413                                   &WorkerProcessLauncherTest::StopWorker));
414   EXPECT_CALL(server_listener_, OnPermanentError(_))
415       .Times(0);
416   EXPECT_CALL(server_listener_, OnWorkerProcessStopped())
417       .Times(0);
418 
419   StartWorker();
420   base::RunLoop().Run();
421 }
422 
423 // Kills the worker process after the 1st connect and expects it to be
424 // restarted.
TEST_F(WorkerProcessLauncherTest,Restart)425 TEST_F(WorkerProcessLauncherTest, Restart) {
426   EXPECT_CALL(*launcher_delegate_, LaunchProcess(_))
427       .Times(2)
428       .WillRepeatedly(Invoke(
429           this, &WorkerProcessLauncherTest::LaunchProcessAndConnect));
430   Expectation first_connect =
431       EXPECT_CALL(server_listener_, OnChannelConnected(_))
432           .Times(2)
433           .WillOnce(
434               InvokeWithoutArgs([=]() { TerminateWorker(CONTROL_C_EXIT); }))
435           .WillOnce(
436               InvokeWithoutArgs(this, &WorkerProcessLauncherTest::StopWorker));
437 
438   EXPECT_CALL(server_listener_, OnPermanentError(_))
439       .Times(0);
440   EXPECT_CALL(server_listener_, OnWorkerProcessStopped())
441       .Times(1);
442 
443   StartWorker();
444   base::RunLoop().Run();
445 }
446 
447 // Drops the IPC channel to the worker process after the 1st connect and expects
448 // the worker process to be restarted.
TEST_F(WorkerProcessLauncherTest,DropIpcChannel)449 TEST_F(WorkerProcessLauncherTest, DropIpcChannel) {
450   EXPECT_CALL(*launcher_delegate_, LaunchProcess(_))
451       .Times(2)
452       .WillRepeatedly(Invoke(
453           this, &WorkerProcessLauncherTest::LaunchProcessAndConnect));
454 
455   Expectation first_connect =
456       EXPECT_CALL(server_listener_, OnChannelConnected(_))
457           .Times(2)
458           .WillOnce(InvokeWithoutArgs(
459               this, &WorkerProcessLauncherTest::DisconnectClient))
460           .WillOnce(InvokeWithoutArgs(
461               this, &WorkerProcessLauncherTest::StopWorker));
462 
463   EXPECT_CALL(server_listener_, OnPermanentError(_))
464       .Times(0);
465   EXPECT_CALL(server_listener_, OnWorkerProcessStopped())
466       .Times(1);
467 
468   StartWorker();
469   base::RunLoop().Run();
470 }
471 
472 // Returns a permanent error exit code and expects OnPermanentError() to be
473 // invoked.
TEST_F(WorkerProcessLauncherTest,PermanentError)474 TEST_F(WorkerProcessLauncherTest, PermanentError) {
475   EXPECT_CALL(*launcher_delegate_, LaunchProcess(_))
476       .Times(1)
477       .WillRepeatedly(Invoke(
478           this, &WorkerProcessLauncherTest::LaunchProcessAndConnect));
479 
480   EXPECT_CALL(server_listener_, OnChannelConnected(_))
481       .Times(1)
482       .WillOnce(InvokeWithoutArgs(
483           [=] { TerminateWorker(kMinPermanentErrorExitCode); }));
484   EXPECT_CALL(server_listener_, OnPermanentError(_))
485       .Times(1)
486       .WillOnce(InvokeWithoutArgs(this,
487                                   &WorkerProcessLauncherTest::StopWorker));
488   EXPECT_CALL(server_listener_, OnWorkerProcessStopped())
489       .Times(1);
490 
491   StartWorker();
492   base::RunLoop().Run();
493 }
494 
495 // Requests the worker to crash and expects it to honor the request.
TEST_F(WorkerProcessLauncherTest,Crash)496 TEST_F(WorkerProcessLauncherTest, Crash) {
497   EXPECT_CALL(*launcher_delegate_, LaunchProcess(_))
498       .Times(2)
499       .WillRepeatedly(Invoke(
500           this, &WorkerProcessLauncherTest::LaunchProcessAndConnect));
501 
502   EXPECT_CALL(server_listener_, OnChannelConnected(_))
503       .Times(2)
504       .WillOnce(InvokeWithoutArgs(this,
505                                   &WorkerProcessLauncherTest::CrashWorker))
506       .WillOnce(InvokeWithoutArgs(this,
507                                   &WorkerProcessLauncherTest::StopWorker));
508 
509   EXPECT_CALL(client_listener_, OnCrash(_, _, _))
510       .Times(1)
511       .WillOnce(
512           InvokeWithoutArgs([=]() { TerminateWorker(EXCEPTION_BREAKPOINT); }));
513   EXPECT_CALL(server_listener_, OnWorkerProcessStopped())
514       .Times(1);
515 
516   StartWorker();
517   base::RunLoop().Run();
518 }
519 
520 // Requests the worker to crash and terminates the worker even if it does not
521 // comply.
TEST_F(WorkerProcessLauncherTest,CrashAnyway)522 TEST_F(WorkerProcessLauncherTest, CrashAnyway) {
523   EXPECT_CALL(*launcher_delegate_, LaunchProcess(_))
524       .Times(2)
525       .WillRepeatedly(Invoke(
526           this, &WorkerProcessLauncherTest::LaunchProcessAndConnect));
527 
528   EXPECT_CALL(server_listener_, OnChannelConnected(_))
529       .Times(2)
530       .WillOnce(InvokeWithoutArgs(this,
531                                   &WorkerProcessLauncherTest::CrashWorker))
532       .WillOnce(InvokeWithoutArgs(this,
533                                   &WorkerProcessLauncherTest::StopWorker));
534 
535   // Ignore the crash request and try send another message to the launcher.
536   EXPECT_CALL(client_listener_, OnCrash(_, _, _))
537       .Times(1)
538       .WillOnce(InvokeWithoutArgs(
539           this, &WorkerProcessLauncherTest::SendFakeMessageToLauncher));
540   EXPECT_CALL(server_listener_, OnWorkerProcessStopped())
541       .Times(1);
542 
543   StartWorker();
544   base::RunLoop().Run();
545 }
546 
547 }  // namespace remoting
548