1 : // Copyright 2014 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/kasko/testing/test_server.h"
16 :
17 : #include <windows.h>
18 :
19 : #include <string>
20 : #include "base/bind.h"
21 : #include "base/command_line.h"
22 : #include "base/location.h"
23 : #include "base/logging.h"
24 : #include "base/process/kill.h"
25 : #include "base/process/launch.h"
26 : #include "base/strings/string_number_conversions.h"
27 : #include "base/strings/utf_string_conversions.h"
28 : #include "base/test/test_timeouts.h"
29 : #include "base/threading/thread.h"
30 : #include "syzygy/common/com_utils.h"
31 : #include "syzygy/core/unittest_util.h"
32 :
33 : namespace kasko {
34 : namespace testing {
35 : namespace {
36 :
37 : // Writes |size| bytes to |handle| and sets |*unblocked| to true.
38 : // Used as a crude timeout mechanism by ReadData().
39 i : void UnblockPipe(HANDLE handle, DWORD size, bool* unblocked) {
40 i : std::string unblock_data(size, '\0');
41 : // Unblock the ReadFile in LocalTestServer::WaitToStart by writing to the
42 : // pipe. Make sure the call succeeded, otherwise we are very likely to hang.
43 i : DWORD bytes_written = 0;
44 i : LOG(WARNING) << "Timeout reached; unblocking pipe by writing " << size
45 : << " bytes";
46 i : CHECK(::WriteFile(handle, unblock_data.data(), size, &bytes_written, NULL));
47 i : CHECK_EQ(size, bytes_written);
48 i : *unblocked = true;
49 i : }
50 :
51 : // Given a file handle, reads into |buffer| until |bytes_max| bytes
52 : // has been read or an error has been encountered. Returns
53 : // true if the read was successful.
54 E : bool ReadData(HANDLE read_fd, HANDLE write_fd, DWORD bytes_max, uint8* buffer) {
55 E : base::Thread thread("test_server_watcher");
56 E : if (!thread.Start())
57 i : return false;
58 :
59 : // Prepare a timeout in case the server fails to start.
60 E : bool unblocked = false;
61 : thread.message_loop()->PostDelayedTask(
62 : FROM_HERE, base::Bind(UnblockPipe, write_fd, bytes_max, &unblocked),
63 E : TestTimeouts::action_max_timeout());
64 :
65 E : DWORD bytes_read = 0;
66 E : while (bytes_read < bytes_max) {
67 : DWORD num_bytes;
68 : if (!::ReadFile(read_fd, buffer + bytes_read, bytes_max - bytes_read,
69 E : &num_bytes, NULL)) {
70 i : LOG(ERROR) << "ReadFile failed" << ::common::LogWe();
71 i : return false;
72 : }
73 E : if (num_bytes <= 0) {
74 i : LOG(ERROR) << "ReadFile returned invalid byte count: " << num_bytes;
75 i : return false;
76 : }
77 E : bytes_read += num_bytes;
78 E : }
79 :
80 E : thread.Stop();
81 : // If the timeout kicked in, abort.
82 E : if (unblocked) {
83 i : LOG(ERROR) << "Timeout exceeded for ReadData";
84 i : return false;
85 : }
86 :
87 E : return true;
88 E : }
89 :
90 E : base::win::ScopedHandle DuplicateStdHandleForInheritance(DWORD std_handle) {
91 E : HANDLE original = ::GetStdHandle(std_handle);
92 E : if (!original || original == INVALID_HANDLE_VALUE)
93 i : return base::win::ScopedHandle();
94 :
95 E : HANDLE duplicate = nullptr;
96 : if (!::DuplicateHandle(base::GetCurrentProcessHandle(), original,
97 : base::GetCurrentProcessHandle(), &duplicate, 0, TRUE,
98 E : DUPLICATE_SAME_ACCESS)) {
99 i : LOG(ERROR) << "Duplicating standard handle " << std_handle
100 : << " failed: " << ::common::LogWe();
101 i : return base::win::ScopedHandle();
102 : }
103 :
104 E : return base::win::ScopedHandle(duplicate);
105 E : }
106 : } // namespace
107 :
108 E : TestServer::TestServer() : port_(0) {
109 E : }
110 :
111 E : TestServer::~TestServer() {
112 E : if (process_handle_) {
113 E : if (!base::WaitForSingleProcess(process_handle_.Get(), base::TimeDelta()))
114 E : base::KillProcess(process_handle_.Get(), 1, true);
115 : }
116 E : }
117 :
118 E : bool TestServer::Start() {
119 E : if (!incoming_directory_.CreateUniqueTempDir()) {
120 i : LOG(ERROR) << "Failed to create temporary 'incoming' directory.";
121 i : return false;
122 : }
123 :
124 : // We will open a pipe used by the child process to indicate its chosen port.
125 E : base::win::ScopedHandle read_fd;
126 E : base::win::ScopedHandle write_fd;
127 : {
128 E : HANDLE child_read = NULL;
129 E : HANDLE child_write = NULL;
130 E : if (!::CreatePipe(&child_read, &child_write, NULL, 0)) {
131 i : LOG(ERROR) << "Failed to create pipe" << ::common::LogWe();
132 i : return false;
133 : }
134 :
135 E : read_fd.Set(child_read);
136 E : write_fd.Set(child_write);
137 : }
138 :
139 : // Have the child inherit the write half.
140 : if (!::SetHandleInformation(write_fd.Get(), HANDLE_FLAG_INHERIT,
141 E : HANDLE_FLAG_INHERIT)) {
142 i : LOG(ERROR) << "Failed to enable pipe inheritance" << ::common::LogWe();
143 i : return false;
144 : }
145 :
146 : base::CommandLine python_command(
147 E : ::testing::GetSrcRelativePath(L"third_party/python_26/python.exe"));
148 : python_command.AppendArgPath(
149 E : ::testing::GetSrcRelativePath(L"syzygy/kasko/testing/test_server.py"));
150 :
151 : // Pass the handle on the command-line. Although HANDLE is a
152 : // pointer, truncating it on 64-bit machines is okay. See
153 : // http://msdn.microsoft.com/en-us/library/aa384203.aspx
154 : //
155 : // "64-bit versions of Windows use 32-bit handles for
156 : // interoperability. When sharing a handle between 32-bit and 64-bit
157 : // applications, only the lower 32 bits are significant, so it is
158 : // safe to truncate the handle (when passing it from 64-bit to
159 : // 32-bit) or sign-extend the handle (when passing it from 32-bit to
160 : // 64-bit)."
161 : python_command.AppendArg(
162 : "--startup-pipe=" +
163 E : base::IntToString(reinterpret_cast<uintptr_t>(write_fd.Get())));
164 :
165 : python_command.AppendArg(
166 : "--incoming-directory=" +
167 E : base::UTF16ToUTF8(incoming_directory_.path().value()));
168 :
169 : base::win::ScopedHandle stdin_dup =
170 E : DuplicateStdHandleForInheritance(STD_INPUT_HANDLE);
171 : base::win::ScopedHandle stdout_dup =
172 E : DuplicateStdHandleForInheritance(STD_OUTPUT_HANDLE);
173 : base::win::ScopedHandle stderr_dup =
174 E : DuplicateStdHandleForInheritance(STD_ERROR_HANDLE);
175 :
176 E : base::LaunchOptions launch_options;
177 E : launch_options.inherit_handles = true;
178 E : launch_options.stdin_handle = stdin_dup.Get();
179 E : launch_options.stdout_handle = stdout_dup.Get();
180 E : launch_options.stderr_handle = stderr_dup.Get();
181 :
182 E : HANDLE process_handle = NULL;
183 E : if (!base::LaunchProcess(python_command, launch_options, &process_handle)) {
184 i : LOG(ERROR) << "Failed to launch " << python_command.GetCommandLineString();
185 i : return false;
186 : }
187 E : process_handle_.Set(process_handle);
188 :
189 : if (!ReadData(read_fd.Get(), write_fd.Get(), sizeof(port_),
190 E : reinterpret_cast<uint8*>(&port_))) {
191 i : LOG(ERROR) << "Could not read port";
192 i : return false;
193 : }
194 :
195 E : return true;
196 E : }
197 :
198 : } // namespace testing
199 : } // namespace kasko
|