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