1 // Copyright 2015 The Crashpad Authors. 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 "test/multiprocess_exec.h"
16 
17 #include <sys/types.h>
18 
19 #include "base/check.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "gtest/gtest.h"
22 #include "util/win/command_line.h"
23 
24 namespace crashpad {
25 namespace test {
26 
27 namespace internal {
28 
29 struct MultiprocessInfo {
MultiprocessInfocrashpad::test::internal::MultiprocessInfo30   MultiprocessInfo() {}
31   ScopedFileHANDLE pipe_c2p_read;
32   ScopedFileHANDLE pipe_c2p_write;
33   ScopedFileHANDLE pipe_p2c_read;
34   ScopedFileHANDLE pipe_p2c_write;
35   PROCESS_INFORMATION process_info;
36 };
37 
38 }  // namespace internal
39 
Multiprocess()40 Multiprocess::Multiprocess()
41     : info_(nullptr),
42       code_(EXIT_SUCCESS),
43       reason_(kTerminationNormal) {
44 }
45 
Run()46 void Multiprocess::Run() {
47   // Set up and spawn the child process.
48   ASSERT_NO_FATAL_FAILURE(PreFork());
49   RunChild();
50 
51   // And then run the parent actions in this process.
52   RunParent();
53 
54   // Reap the child.
55   WaitForSingleObject(info_->process_info.hProcess, INFINITE);
56   CloseHandle(info_->process_info.hThread);
57   CloseHandle(info_->process_info.hProcess);
58 }
59 
SetExpectedChildTermination(TerminationReason reason,ReturnCodeType code)60 void Multiprocess::SetExpectedChildTermination(TerminationReason reason,
61                                                ReturnCodeType code) {
62   EXPECT_EQ(info_, nullptr)
63       << "SetExpectedChildTermination() must be called before Run()";
64   reason_ = reason;
65   code_ = code;
66 }
67 
~Multiprocess()68 Multiprocess::~Multiprocess() {
69   delete info_;
70 }
71 
ReadPipeHandle() const72 FileHandle Multiprocess::ReadPipeHandle() const {
73   // This is the parent case, it's stdin in the child.
74   return info_->pipe_c2p_read.get();
75 }
76 
WritePipeHandle() const77 FileHandle Multiprocess::WritePipeHandle() const {
78   // This is the parent case, it's stdout in the child.
79   return info_->pipe_p2c_write.get();
80 }
81 
CloseReadPipe()82 void Multiprocess::CloseReadPipe() {
83   info_->pipe_c2p_read.reset();
84 }
85 
CloseWritePipe()86 void Multiprocess::CloseWritePipe() {
87   info_->pipe_p2c_write.reset();
88 }
89 
RunParent()90 void Multiprocess::RunParent() {
91   MultiprocessParent();
92 
93   info_->pipe_c2p_read.reset();
94   info_->pipe_p2c_write.reset();
95 }
96 
RunChild()97 void Multiprocess::RunChild() {
98   MultiprocessChild();
99 
100   info_->pipe_c2p_write.reset();
101   info_->pipe_p2c_read.reset();
102 }
103 
MultiprocessExec()104 MultiprocessExec::MultiprocessExec()
105     : Multiprocess(), command_(), arguments_(), command_line_() {
106 }
107 
SetChildCommand(const base::FilePath & command,const std::vector<std::string> * arguments)108 void MultiprocessExec::SetChildCommand(
109     const base::FilePath& command,
110     const std::vector<std::string>* arguments) {
111   command_ = command;
112   if (arguments) {
113     arguments_ = *arguments;
114   } else {
115     arguments_.clear();
116   }
117 }
118 
~MultiprocessExec()119 MultiprocessExec::~MultiprocessExec() {
120 }
121 
PreFork()122 void MultiprocessExec::PreFork() {
123   ASSERT_FALSE(command_.empty());
124 
125   command_line_.clear();
126   AppendCommandLineArgument(command_.value(), &command_line_);
127   for (size_t i = 0; i < arguments_.size(); ++i) {
128     AppendCommandLineArgument(base::UTF8ToWide(arguments_[i]), &command_line_);
129   }
130 
131   // Make pipes for child-to-parent and parent-to-child communication. Mark them
132   // as inheritable via the SECURITY_ATTRIBUTES, but use SetHandleInformation to
133   // ensure that the parent sides are not inherited.
134   ASSERT_EQ(info(), nullptr);
135   set_info(new internal::MultiprocessInfo());
136 
137   SECURITY_ATTRIBUTES security_attributes = {0};
138   security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
139   security_attributes.bInheritHandle = TRUE;
140 
141   HANDLE c2p_read, c2p_write;
142   PCHECK(CreatePipe(&c2p_read, &c2p_write, &security_attributes, 0));
143   PCHECK(SetHandleInformation(c2p_read, HANDLE_FLAG_INHERIT, 0));
144   info()->pipe_c2p_read.reset(c2p_read);
145   info()->pipe_c2p_write.reset(c2p_write);
146 
147   HANDLE p2c_read, p2c_write;
148   PCHECK(CreatePipe(&p2c_read, &p2c_write, &security_attributes, 0));
149   PCHECK(SetHandleInformation(p2c_write, HANDLE_FLAG_INHERIT, 0));
150   info()->pipe_p2c_read.reset(p2c_read);
151   info()->pipe_p2c_write.reset(p2c_write);
152 }
153 
MultiprocessChild()154 void MultiprocessExec::MultiprocessChild() {
155   STARTUPINFO startup_info = {0};
156   startup_info.cb = sizeof(startup_info);
157   startup_info.hStdInput = info()->pipe_p2c_read.get();
158   startup_info.hStdOutput = info()->pipe_c2p_write.get();
159   startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
160   startup_info.dwFlags = STARTF_USESTDHANDLES;
161   PCHECK(CreateProcess(command_.value().c_str(),
162                        &command_line_[0],  // This cannot be constant, per MSDN.
163                        nullptr,
164                        nullptr,
165                        TRUE,
166                        0,
167                        nullptr,
168                        nullptr,
169                        &startup_info,
170                        &info()->process_info));
171 }
172 
ChildProcess()173 ProcessType MultiprocessExec::ChildProcess() {
174   return info()->process_info.hProcess;
175 }
176 
177 }  // namespace test
178 }  // namespace crashpad
179