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