1 // Copyright 2017 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 "handler/linux/exception_handler_server.h"
16
17 #include <sys/types.h>
18 #include <unistd.h>
19
20 #include "base/logging.h"
21 #include "build/build_config.h"
22 #include "gtest/gtest.h"
23 #include "snapshot/linux/process_snapshot_linux.h"
24 #include "test/errors.h"
25 #include "test/multiprocess.h"
26 #include "util/linux/direct_ptrace_connection.h"
27 #include "util/linux/exception_handler_client.h"
28 #include "util/linux/ptrace_client.h"
29 #include "util/linux/scoped_pr_set_ptracer.h"
30 #include "util/misc/uuid.h"
31 #include "util/synchronization/semaphore.h"
32 #include "util/thread/thread.h"
33
34 #if defined(OS_ANDROID)
35 #include <android/api-level.h>
36 #endif
37
38 namespace crashpad {
39 namespace test {
40 namespace {
41
42 // Runs the ExceptionHandlerServer on a background thread.
43 class RunServerThread : public Thread {
44 public:
RunServerThread(ExceptionHandlerServer * server,ExceptionHandlerServer::Delegate * delegate)45 RunServerThread(ExceptionHandlerServer* server,
46 ExceptionHandlerServer::Delegate* delegate)
47 : server_(server), delegate_(delegate), join_sem_(0) {}
48
~RunServerThread()49 ~RunServerThread() override {}
50
JoinWithTimeout(double timeout)51 bool JoinWithTimeout(double timeout) {
52 if (!join_sem_.TimedWait(timeout)) {
53 return false;
54 }
55 Join();
56 return true;
57 }
58
59 private:
60 // Thread:
ThreadMain()61 void ThreadMain() override {
62 server_->Run(delegate_);
63 join_sem_.Signal();
64 }
65
66 ExceptionHandlerServer* server_;
67 ExceptionHandlerServer::Delegate* delegate_;
68 Semaphore join_sem_;
69
70 DISALLOW_COPY_AND_ASSIGN(RunServerThread);
71 };
72
73 class ScopedStopServerAndJoinThread {
74 public:
ScopedStopServerAndJoinThread(ExceptionHandlerServer * server,RunServerThread * thread)75 ScopedStopServerAndJoinThread(ExceptionHandlerServer* server,
76 RunServerThread* thread)
77 : server_(server), thread_(thread) {}
78
~ScopedStopServerAndJoinThread()79 ~ScopedStopServerAndJoinThread() {
80 server_->Stop();
81 EXPECT_TRUE(thread_->JoinWithTimeout(5.0));
82 }
83
84 private:
85 ExceptionHandlerServer* server_;
86 RunServerThread* thread_;
87
88 DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread);
89 };
90
91 class TestDelegate : public ExceptionHandlerServer::Delegate {
92 public:
TestDelegate()93 TestDelegate()
94 : Delegate(), last_exception_address_(0), last_client_(-1), sem_(0) {}
95
~TestDelegate()96 ~TestDelegate() {}
97
WaitForException(double timeout_seconds,pid_t * last_client,VMAddress * last_address)98 bool WaitForException(double timeout_seconds,
99 pid_t* last_client,
100 VMAddress* last_address) {
101 if (sem_.TimedWait(timeout_seconds)) {
102 *last_client = last_client_;
103 *last_address = last_exception_address_;
104 return true;
105 }
106
107 return false;
108 }
109
HandleException(pid_t client_process_id,uid_t client_uid,const ExceptionHandlerProtocol::ClientInformation & info,VMAddress requesting_thread_stack_address,pid_t * requesting_thread_id=nullptr,UUID * local_report_id=nullptr)110 bool HandleException(pid_t client_process_id,
111 uid_t client_uid,
112 const ExceptionHandlerProtocol::ClientInformation& info,
113 VMAddress requesting_thread_stack_address,
114 pid_t* requesting_thread_id = nullptr,
115 UUID* local_report_id = nullptr) override {
116 DirectPtraceConnection connection;
117 bool connected = connection.Initialize(client_process_id);
118 EXPECT_TRUE(connected);
119
120 last_exception_address_ = info.exception_information_address;
121 last_client_ = client_process_id;
122 sem_.Signal();
123 if (!connected) {
124 return false;
125 }
126
127 if (requesting_thread_id) {
128 if (requesting_thread_stack_address) {
129 ProcessSnapshotLinux process_snapshot;
130 if (!process_snapshot.Initialize(&connection)) {
131 ADD_FAILURE();
132 return false;
133 }
134 *requesting_thread_id = process_snapshot.FindThreadWithStackAddress(
135 requesting_thread_stack_address);
136 } else {
137 *requesting_thread_id = -1;
138 }
139 }
140 return true;
141 }
142
HandleExceptionWithBroker(pid_t client_process_id,uid_t client_uid,const ExceptionHandlerProtocol::ClientInformation & info,int broker_sock,UUID * local_report_id=nullptr)143 bool HandleExceptionWithBroker(
144 pid_t client_process_id,
145 uid_t client_uid,
146 const ExceptionHandlerProtocol::ClientInformation& info,
147 int broker_sock,
148 UUID* local_report_id = nullptr) override {
149 PtraceClient client;
150 bool connected = client.Initialize(broker_sock, client_process_id);
151 EXPECT_TRUE(connected);
152
153 last_exception_address_ = info.exception_information_address,
154 last_client_ = client_process_id;
155 sem_.Signal();
156 return connected;
157 }
158
159 private:
160 VMAddress last_exception_address_;
161 pid_t last_client_;
162 Semaphore sem_;
163
164 DISALLOW_COPY_AND_ASSIGN(TestDelegate);
165 };
166
167 class MockPtraceStrategyDecider : public PtraceStrategyDecider {
168 public:
MockPtraceStrategyDecider(PtraceStrategyDecider::Strategy strategy)169 MockPtraceStrategyDecider(PtraceStrategyDecider::Strategy strategy)
170 : PtraceStrategyDecider(), strategy_(strategy) {}
171
~MockPtraceStrategyDecider()172 ~MockPtraceStrategyDecider() {}
173
ChooseStrategy(int sock,bool multiple_clients,const ucred & client_credentials)174 Strategy ChooseStrategy(int sock,
175 bool multiple_clients,
176 const ucred& client_credentials) override {
177 if (strategy_ == Strategy::kUseBroker) {
178 ExceptionHandlerProtocol::ServerToClientMessage message = {};
179 message.type =
180 ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker;
181
182 ExceptionHandlerProtocol::Errno status;
183 bool result = LoggingWriteFile(sock, &message, sizeof(message)) &&
184 LoggingReadFileExactly(sock, &status, sizeof(status));
185 EXPECT_TRUE(result);
186
187 if (!result) {
188 return Strategy::kError;
189 }
190
191 if (status != 0) {
192 errno = status;
193 ADD_FAILURE() << ErrnoMessage("Handler Client ForkBroker");
194 return Strategy::kNoPtrace;
195 }
196 }
197 return strategy_;
198 }
199
200 private:
201 Strategy strategy_;
202
203 DISALLOW_COPY_AND_ASSIGN(MockPtraceStrategyDecider);
204 };
205
206 class ExceptionHandlerServerTest : public testing::TestWithParam<bool> {
207 public:
ExceptionHandlerServerTest()208 ExceptionHandlerServerTest()
209 : server_(),
210 delegate_(),
211 server_thread_(&server_, &delegate_),
212 sock_to_handler_(),
213 use_multi_client_socket_(GetParam()) {}
214
215 ~ExceptionHandlerServerTest() = default;
216
SockToHandler()217 int SockToHandler() { return sock_to_handler_.get(); }
218
Delegate()219 TestDelegate* Delegate() { return &delegate_; }
220
Hangup()221 void Hangup() { sock_to_handler_.reset(); }
222
ServerThread()223 RunServerThread* ServerThread() { return &server_thread_; }
224
Server()225 ExceptionHandlerServer* Server() { return &server_; }
226
227 class CrashDumpTest : public Multiprocess {
228 public:
CrashDumpTest(ExceptionHandlerServerTest * server_test,bool succeeds)229 CrashDumpTest(ExceptionHandlerServerTest* server_test, bool succeeds)
230 : Multiprocess(), server_test_(server_test), succeeds_(succeeds) {}
231
232 ~CrashDumpTest() = default;
233
MultiprocessParent()234 void MultiprocessParent() override {
235 ExceptionHandlerProtocol::ClientInformation info;
236 ASSERT_TRUE(
237 LoggingReadFileExactly(ReadPipeHandle(), &info, sizeof(info)));
238
239 if (succeeds_) {
240 VMAddress last_address;
241 pid_t last_client;
242 ASSERT_TRUE(server_test_->Delegate()->WaitForException(
243 5.0, &last_client, &last_address));
244 EXPECT_EQ(last_address, info.exception_information_address);
245 EXPECT_EQ(last_client, ChildPID());
246 } else {
247 CheckedReadFileAtEOF(ReadPipeHandle());
248 }
249 }
250
MultiprocessChild()251 void MultiprocessChild() override {
252 ASSERT_EQ(close(server_test_->sock_to_client_), 0);
253
254 ExceptionHandlerProtocol::ClientInformation info;
255 info.exception_information_address = 42;
256 ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &info, sizeof(info)));
257
258 // If the current ptrace_scope is restricted, the broker needs to be set
259 // as the ptracer for this process. Setting this process as its own
260 // ptracer allows the broker to inherit this condition.
261 ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ true);
262
263 ExceptionHandlerClient client(server_test_->SockToHandler(),
264 server_test_->use_multi_client_socket_);
265 ASSERT_EQ(client.RequestCrashDump(info), 0);
266 }
267
268 private:
269 ExceptionHandlerServerTest* server_test_;
270 bool succeeds_;
271
272 DISALLOW_COPY_AND_ASSIGN(CrashDumpTest);
273 };
274
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy strategy,bool succeeds)275 void ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy strategy,
276 bool succeeds) {
277 Server()->SetPtraceStrategyDecider(
278 std::make_unique<MockPtraceStrategyDecider>(strategy));
279
280 ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
281 ServerThread()->Start();
282
283 CrashDumpTest test(this, succeeds);
284 test.Run();
285 }
286
UsingMultiClientSocket() const287 bool UsingMultiClientSocket() const { return use_multi_client_socket_; }
288
289 protected:
SetUp()290 void SetUp() override {
291 int socks[2];
292 ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, socks), 0);
293 sock_to_handler_.reset(socks[0]);
294 sock_to_client_ = socks[1];
295
296 ASSERT_TRUE(server_.InitializeWithClient(ScopedFileHandle(socks[1]),
297 use_multi_client_socket_));
298 }
299
300 private:
301 ExceptionHandlerServer server_;
302 TestDelegate delegate_;
303 RunServerThread server_thread_;
304 ScopedFileHandle sock_to_handler_;
305 int sock_to_client_;
306 bool use_multi_client_socket_;
307
308 DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerTest);
309 };
310
TEST_P(ExceptionHandlerServerTest,ShutdownWithNoClients)311 TEST_P(ExceptionHandlerServerTest, ShutdownWithNoClients) {
312 ServerThread()->Start();
313 Hangup();
314 ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
315 }
316
TEST_P(ExceptionHandlerServerTest,StopWithClients)317 TEST_P(ExceptionHandlerServerTest, StopWithClients) {
318 ServerThread()->Start();
319 Server()->Stop();
320 ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
321 }
322
TEST_P(ExceptionHandlerServerTest,StopBeforeRun)323 TEST_P(ExceptionHandlerServerTest, StopBeforeRun) {
324 Server()->Stop();
325 ServerThread()->Start();
326 ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
327 }
328
TEST_P(ExceptionHandlerServerTest,MultipleStops)329 TEST_P(ExceptionHandlerServerTest, MultipleStops) {
330 ServerThread()->Start();
331 Server()->Stop();
332 Server()->Stop();
333 ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
334 }
335
TEST_P(ExceptionHandlerServerTest,RequestCrashDumpDefault)336 TEST_P(ExceptionHandlerServerTest, RequestCrashDumpDefault) {
337 ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
338 ServerThread()->Start();
339
340 CrashDumpTest test(this, true);
341 test.Run();
342 }
343
TEST_P(ExceptionHandlerServerTest,RequestCrashDumpNoPtrace)344 TEST_P(ExceptionHandlerServerTest, RequestCrashDumpNoPtrace) {
345 ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kNoPtrace,
346 false);
347 }
348
TEST_P(ExceptionHandlerServerTest,RequestCrashDumpForkBroker)349 TEST_P(ExceptionHandlerServerTest, RequestCrashDumpForkBroker) {
350 if (UsingMultiClientSocket()) {
351 // The broker is not supported with multiple clients connected on a single
352 // socket.
353 return;
354 }
355 ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kUseBroker,
356 true);
357 }
358
TEST_P(ExceptionHandlerServerTest,RequestCrashDumpDirectPtrace)359 TEST_P(ExceptionHandlerServerTest, RequestCrashDumpDirectPtrace) {
360 ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kDirectPtrace,
361 true);
362 }
363
TEST_P(ExceptionHandlerServerTest,RequestCrashDumpError)364 TEST_P(ExceptionHandlerServerTest, RequestCrashDumpError) {
365 ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kError, false);
366 }
367
368 INSTANTIATE_TEST_SUITE_P(ExceptionHandlerServerTestSuite,
369 ExceptionHandlerServerTest,
370 testing::Bool()
371 );
372
373 } // namespace
374 } // namespace test
375 } // namespace crashpad
376