blob: 089fdedab7bc4dc6c6488660fa9a824dace3b8cc [file] [log] [blame]
siggi420541c2014-11-18 23:16:321// Copyright (c) 2014 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 "components/browser_watcher/watcher_client_win.h"
6
7#include <string>
8
9#include "base/base_switches.h"
erikwright7c4a4262015-01-09 18:32:3310#include "base/bind.h"
siggi420541c2014-11-18 23:16:3211#include "base/command_line.h"
12#include "base/logging.h"
siggi420541c2014-11-18 23:16:3213#include "base/process/kill.h"
rvargasd5626f62015-02-05 19:09:2414#include "base/process/process.h"
siggi420541c2014-11-18 23:16:3215#include "base/strings/string_number_conversions.h"
16#include "base/strings/stringprintf.h"
17#include "base/test/multiprocess_test.h"
18#include "base/test/test_reg_util_win.h"
19#include "base/win/scoped_handle.h"
20#include "base/win/windows_version.h"
21#include "components/browser_watcher/exit_code_watcher_win.h"
22#include "testing/gtest/include/gtest/gtest.h"
23#include "testing/multiprocess_func_list.h"
24
25namespace browser_watcher {
26
27namespace {
28
29// Command line switches used to communiate to the child test.
erikwright7c4a4262015-01-09 18:32:3330const char kParentHandle[] = "parent-handle";
siggi420541c2014-11-18 23:16:3231const char kLeakHandle[] = "leak-handle";
32const char kNoLeakHandle[] = "no-leak-handle";
33
34bool IsValidParentProcessHandle(base::CommandLine& cmd_line,
35 const char* switch_name) {
36 std::string str_handle =
37 cmd_line.GetSwitchValueASCII(switch_name);
38
39 unsigned int_handle = 0;
40 if (!base::StringToUint(str_handle, &int_handle))
41 return false;
42
siggi420541c2014-11-18 23:16:3243 base::ProcessHandle handle =
44 reinterpret_cast<base::ProcessHandle>(int_handle);
45 // Verify that we can get the associated process id.
46 base::ProcessId parent_id = base::GetProcId(handle);
47 if (parent_id == 0) {
48 // Unable to get the parent pid - perhaps insufficient permissions.
49 return false;
50 }
51
52 // Make sure the handle grants SYNCHRONIZE by waiting on it.
53 DWORD err = ::WaitForSingleObject(handle, 0);
54 if (err != WAIT_OBJECT_0 && err != WAIT_TIMEOUT) {
55 // Unable to wait on the handle - perhaps insufficient permissions.
56 return false;
57 }
58
59 return true;
60}
61
62MULTIPROCESS_TEST_MAIN(VerifyParentHandle) {
63 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
64
65 // Make sure we got a valid parent process handle from the watcher client.
erikwright7c4a4262015-01-09 18:32:3366 if (!IsValidParentProcessHandle(*cmd_line, kParentHandle)) {
siggi420541c2014-11-18 23:16:3267 LOG(ERROR) << "Invalid or missing parent-handle.";
68 return 1;
69 }
70
71 // If in the legacy mode, we expect this second handle will leak into the
72 // child process. This mainly serves to verify that the legacy mode is
73 // getting tested.
74 if (cmd_line->HasSwitch(kLeakHandle) &&
75 !IsValidParentProcessHandle(*cmd_line, kLeakHandle)) {
76 LOG(ERROR) << "Parent process handle unexpectedly didn't leak.";
77 return 1;
78 }
79
80 // If not in the legacy mode, this second handle should not leak into the
81 // child process.
82 if (cmd_line->HasSwitch(kNoLeakHandle) &&
83 IsValidParentProcessHandle(*cmd_line, kLeakHandle)) {
84 LOG(ERROR) << "Parent process handle unexpectedly leaked.";
85 return 1;
86 }
87
88 return 0;
89}
90
erikwright7c4a4262015-01-09 18:32:3391class WatcherClientTest : public base::MultiProcessTest {
siggi420541c2014-11-18 23:16:3292 public:
93 virtual void SetUp() {
94 // Open an inheritable handle on our own process to test handle leakage.
95 self_.Set(::OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
96 TRUE, // Ineritable handle.
97 base::GetCurrentProcId()));
98
99 ASSERT_TRUE(self_.IsValid());
100 }
101
102 enum HandlePolicy {
103 LEAK_HANDLE,
104 NO_LEAK_HANDLE
105 };
106
107 // Get a base command line to launch back into this test fixture.
erikwright7c4a4262015-01-09 18:32:33108 base::CommandLine GetBaseCommandLine(HandlePolicy handle_policy,
109 HANDLE parent_handle) {
siggi420541c2014-11-18 23:16:32110 base::CommandLine ret = base::GetMultiProcessTestChildBaseCommandLine();
111
112 ret.AppendSwitchASCII(switches::kTestChildProcess, "VerifyParentHandle");
erikwright7c4a4262015-01-09 18:32:33113 ret.AppendSwitchASCII(kParentHandle,
114 base::StringPrintf("%d", parent_handle));
siggi420541c2014-11-18 23:16:32115
116 switch (handle_policy) {
117 case LEAK_HANDLE:
erikwright813537542015-01-12 20:31:40118 ret.AppendSwitchASCII(kLeakHandle,
119 base::StringPrintf("%d", self_.Get()));
siggi420541c2014-11-18 23:16:32120 break;
121
122 case NO_LEAK_HANDLE:
erikwright813537542015-01-12 20:31:40123 ret.AppendSwitchASCII(kNoLeakHandle,
124 base::StringPrintf("%d", self_.Get()));
siggi420541c2014-11-18 23:16:32125 break;
126
127 default:
128 ADD_FAILURE() << "Impossible handle_policy";
129 }
130
131 return ret;
132 }
133
erikwright7c4a4262015-01-09 18:32:33134 WatcherClient::CommandLineGenerator GetBaseCommandLineGenerator(
135 HandlePolicy handle_policy) {
136 return base::Bind(&WatcherClientTest::GetBaseCommandLine,
137 base::Unretained(this), handle_policy);
138 }
139
erikwrightc57091e2015-02-04 15:19:10140 void AssertSuccessfulExitCode(base::Process process) {
141 ASSERT_TRUE(process.IsValid());
siggi420541c2014-11-18 23:16:32142 int exit_code = 0;
erikwrightc57091e2015-02-04 15:19:10143 if (!process.WaitForExit(&exit_code))
144 FAIL() << "Process::WaitForExit failed.";
siggi420541c2014-11-18 23:16:32145 ASSERT_EQ(0, exit_code);
146 }
147
148 // Inheritable process handle used for testing.
149 base::win::ScopedHandle self_;
150};
151
152} // namespace
153
154// TODO(siggi): More testing - test WatcherClient base implementation.
155
erikwright7c4a4262015-01-09 18:32:33156TEST_F(WatcherClientTest, LaunchWatcherSucceeds) {
siggi420541c2014-11-18 23:16:32157 // We can only use the non-legacy launch method on Windows Vista or better.
158 if (base::win::GetVersion() < base::win::VERSION_VISTA)
159 return;
160
erikwright7c4a4262015-01-09 18:32:33161 WatcherClient client(GetBaseCommandLineGenerator(NO_LEAK_HANDLE));
siggi420541c2014-11-18 23:16:32162 ASSERT_FALSE(client.use_legacy_launch());
163
164 client.LaunchWatcher();
165
erikwrightc57091e2015-02-04 15:19:10166 ASSERT_NO_FATAL_FAILURE(
167 AssertSuccessfulExitCode(client.process().Duplicate()));
siggi420541c2014-11-18 23:16:32168}
169
erikwright7c4a4262015-01-09 18:32:33170TEST_F(WatcherClientTest, LaunchWatcherLegacyModeSucceeds) {
siggi420541c2014-11-18 23:16:32171 // Test the XP-compatible legacy launch mode. This is expected to leak
172 // a handle to the child process.
erikwright7c4a4262015-01-09 18:32:33173 WatcherClient client(GetBaseCommandLineGenerator(LEAK_HANDLE));
siggi420541c2014-11-18 23:16:32174
175 // Use the legacy launch mode.
176 client.set_use_legacy_launch(true);
177
178 client.LaunchWatcher();
179
erikwrightc57091e2015-02-04 15:19:10180 ASSERT_NO_FATAL_FAILURE(
181 AssertSuccessfulExitCode(client.process().Duplicate()));
siggi420541c2014-11-18 23:16:32182}
183
erikwright7c4a4262015-01-09 18:32:33184TEST_F(WatcherClientTest, LegacyModeDetectedOnXP) {
siggi420541c2014-11-18 23:16:32185 // This test only works on Windows XP.
186 if (base::win::GetVersion() > base::win::VERSION_XP)
187 return;
188
189 // Test that the client detects the need to use legacy launch mode, and that
190 // it works on Windows XP.
erikwright7c4a4262015-01-09 18:32:33191 WatcherClient client(GetBaseCommandLineGenerator(LEAK_HANDLE));
siggi420541c2014-11-18 23:16:32192 ASSERT_TRUE(client.use_legacy_launch());
193
194 client.LaunchWatcher();
195
erikwrightc57091e2015-02-04 15:19:10196 ASSERT_NO_FATAL_FAILURE(
197 AssertSuccessfulExitCode(client.process().Duplicate()));
siggi420541c2014-11-18 23:16:32198}
199
200} // namespace browser_watcher