1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/browser_watcher/watcher_client_win.h"
#include <stdint.h>
#include <stdlib.h>
#include <string>
#include "base/base_switches.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/process/kill.h"
#include "base/process/process.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/multiprocess_test.h"
#include "base/test/test_reg_util_win.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "components/browser_watcher/exit_code_watcher_win.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
namespace browser_watcher {
namespace {
// Command line switches used to communiate to the child test.
const char kParentHandle[] = "parent-handle";
const char kLeakHandle[] = "leak-handle";
const char kNoLeakHandle[] = "no-leak-handle";
bool IsValidParentProcessHandle(base::CommandLine& cmd_line,
const char* switch_name) {
std::string str_handle =
cmd_line.GetSwitchValueASCII(switch_name);
size_t integer_handle = 0;
if (!base::StringToSizeT(str_handle, &integer_handle))
return false;
base::ProcessHandle handle =
reinterpret_cast<base::ProcessHandle>(integer_handle);
// Verify that we can get the associated process id.
base::ProcessId parent_id = base::GetProcId(handle);
if (parent_id == 0) {
// Unable to get the parent pid - perhaps insufficient permissions.
return false;
}
// Make sure the handle grants SYNCHRONIZE by waiting on it.
DWORD err = ::WaitForSingleObject(handle, 0);
if (err != WAIT_OBJECT_0 && err != WAIT_TIMEOUT) {
// Unable to wait on the handle - perhaps insufficient permissions.
return false;
}
return true;
}
std::string HandleToString(HANDLE handle) {
// A HANDLE is a void* pointer, which is the same size as a size_t,
// so we can use reinterpret_cast<> on it.
size_t integer_handle = reinterpret_cast<size_t>(handle);
return base::SizeTToString(integer_handle);
}
MULTIPROCESS_TEST_MAIN(VerifyParentHandle) {
base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
// Make sure we got a valid parent process handle from the watcher client.
if (!IsValidParentProcessHandle(*cmd_line, kParentHandle)) {
LOG(ERROR) << "Invalid or missing parent-handle.";
return 1;
}
// If in the legacy mode, we expect this second handle will leak into the
// child process. This mainly serves to verify that the legacy mode is
// getting tested.
if (cmd_line->HasSwitch(kLeakHandle) &&
!IsValidParentProcessHandle(*cmd_line, kLeakHandle)) {
LOG(ERROR) << "Parent process handle unexpectedly didn't leak.";
return 1;
}
// If not in the legacy mode, this second handle should not leak into the
// child process.
if (cmd_line->HasSwitch(kNoLeakHandle) &&
IsValidParentProcessHandle(*cmd_line, kLeakHandle)) {
LOG(ERROR) << "Parent process handle unexpectedly leaked.";
return 1;
}
return 0;
}
class WatcherClientTest : public base::MultiProcessTest {
public:
void SetUp() override {
// Open an inheritable handle on our own process to test handle leakage.
self_.Set(::OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
TRUE, // Ineritable handle.
base::GetCurrentProcId()));
ASSERT_TRUE(self_.IsValid());
}
enum HandlePolicy {
LEAK_HANDLE,
NO_LEAK_HANDLE
};
// Get a base command line to launch back into this test fixture.
base::CommandLine GetBaseCommandLine(HandlePolicy handle_policy,
HANDLE parent_handle) {
base::CommandLine ret = base::GetMultiProcessTestChildBaseCommandLine();
ret.AppendSwitchASCII(switches::kTestChildProcess, "VerifyParentHandle");
ret.AppendSwitchASCII(kParentHandle, HandleToString(parent_handle));
switch (handle_policy) {
case LEAK_HANDLE:
ret.AppendSwitchASCII(kLeakHandle, HandleToString(self_.Get()));
break;
case NO_LEAK_HANDLE:
ret.AppendSwitchASCII(kNoLeakHandle, HandleToString(self_.Get()));
break;
default:
ADD_FAILURE() << "Impossible handle_policy";
}
return ret;
}
WatcherClient::CommandLineGenerator GetBaseCommandLineGenerator(
HandlePolicy handle_policy) {
return base::Bind(&WatcherClientTest::GetBaseCommandLine,
base::Unretained(this), handle_policy);
}
void AssertSuccessfulExitCode(base::Process process) {
ASSERT_TRUE(process.IsValid());
int exit_code = 0;
if (!process.WaitForExit(&exit_code))
FAIL() << "Process::WaitForExit failed.";
ASSERT_EQ(0, exit_code);
}
// Inheritable process handle used for testing.
base::win::ScopedHandle self_;
};
} // namespace
// TODO(siggi): More testing - test WatcherClient base implementation.
TEST_F(WatcherClientTest, LaunchWatcherSucceeds) {
// We can only use the non-legacy launch method on Windows Vista or better.
if (base::win::GetVersion() < base::win::VERSION_VISTA)
return;
WatcherClient client(GetBaseCommandLineGenerator(NO_LEAK_HANDLE));
ASSERT_FALSE(client.use_legacy_launch());
client.LaunchWatcher();
ASSERT_NO_FATAL_FAILURE(
AssertSuccessfulExitCode(client.process().Duplicate()));
}
TEST_F(WatcherClientTest, LaunchWatcherLegacyModeSucceeds) {
// Test the XP-compatible legacy launch mode. This is expected to leak
// a handle to the child process.
WatcherClient client(GetBaseCommandLineGenerator(LEAK_HANDLE));
// Use the legacy launch mode.
client.set_use_legacy_launch(true);
client.LaunchWatcher();
ASSERT_NO_FATAL_FAILURE(
AssertSuccessfulExitCode(client.process().Duplicate()));
}
TEST_F(WatcherClientTest, LegacyModeDetectedOnXP) {
// This test only works on Windows XP.
if (base::win::GetVersion() > base::win::VERSION_XP)
return;
// Test that the client detects the need to use legacy launch mode, and that
// it works on Windows XP.
WatcherClient client(GetBaseCommandLineGenerator(LEAK_HANDLE));
ASSERT_TRUE(client.use_legacy_launch());
client.LaunchWatcher();
ASSERT_NO_FATAL_FAILURE(
AssertSuccessfulExitCode(client.process().Duplicate()));
}
} // namespace browser_watcher
|