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 "util/linux/ptrace_broker.h"
16 
17 #include <sys/socket.h>
18 #include <sys/syscall.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 
22 #include <utility>
23 
24 #include "build/build_config.h"
25 #include "gtest/gtest.h"
26 #include "test/filesystem.h"
27 #include "test/linux/get_tls.h"
28 #include "test/multiprocess.h"
29 #include "test/scoped_temp_dir.h"
30 #include "util/file/file_io.h"
31 #include "util/linux/ptrace_client.h"
32 #include "util/posix/scoped_mmap.h"
33 #include "util/synchronization/semaphore.h"
34 #include "util/thread/thread.h"
35 
36 namespace crashpad {
37 namespace test {
38 namespace {
39 
40 class ScopedTimeoutThread : public Thread {
41  public:
ScopedTimeoutThread()42   ScopedTimeoutThread() : join_sem_(0) {}
~ScopedTimeoutThread()43   ~ScopedTimeoutThread() { EXPECT_TRUE(JoinWithTimeout(5.0)); }
44 
45  protected:
ThreadMain()46   void ThreadMain() override { join_sem_.Signal(); }
47 
48  private:
JoinWithTimeout(double timeout)49   bool JoinWithTimeout(double timeout) {
50     if (!join_sem_.TimedWait(timeout)) {
51       return false;
52     }
53     Join();
54     return true;
55   }
56 
57   Semaphore join_sem_;
58 
59   DISALLOW_COPY_AND_ASSIGN(ScopedTimeoutThread);
60 };
61 
62 class RunBrokerThread : public ScopedTimeoutThread {
63  public:
RunBrokerThread(PtraceBroker * broker)64   RunBrokerThread(PtraceBroker* broker)
65       : ScopedTimeoutThread(), broker_(broker) {}
66 
~RunBrokerThread()67   ~RunBrokerThread() {}
68 
69  private:
ThreadMain()70   void ThreadMain() override {
71     EXPECT_EQ(broker_->Run(), 0);
72     ScopedTimeoutThread::ThreadMain();
73   }
74 
75   PtraceBroker* broker_;
76 
77   DISALLOW_COPY_AND_ASSIGN(RunBrokerThread);
78 };
79 
80 class BlockOnReadThread : public ScopedTimeoutThread {
81  public:
BlockOnReadThread(int readfd,int writefd)82   BlockOnReadThread(int readfd, int writefd)
83       : ScopedTimeoutThread(), readfd_(readfd), writefd_(writefd) {}
84 
~BlockOnReadThread()85   ~BlockOnReadThread() {}
86 
87  private:
ThreadMain()88   void ThreadMain() override {
89     pid_t pid = syscall(SYS_gettid);
90     LoggingWriteFile(writefd_, &pid, sizeof(pid));
91 
92     LinuxVMAddress tls = GetTLS();
93     LoggingWriteFile(writefd_, &tls, sizeof(tls));
94 
95     CheckedReadFileAtEOF(readfd_);
96     ScopedTimeoutThread::ThreadMain();
97   }
98 
99   int readfd_;
100   int writefd_;
101 
102   DISALLOW_COPY_AND_ASSIGN(BlockOnReadThread);
103 };
104 
105 class SameBitnessTest : public Multiprocess {
106  public:
SameBitnessTest()107   SameBitnessTest() : Multiprocess(), mapping_() {}
~SameBitnessTest()108   ~SameBitnessTest() {}
109 
110  protected:
PreFork()111   void PreFork() override {
112     ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork());
113 
114     size_t page_size = getpagesize();
115     ASSERT_TRUE(mapping_.ResetMmap(nullptr,
116                                    page_size * 3,
117                                    PROT_READ | PROT_WRITE,
118                                    MAP_PRIVATE | MAP_ANON,
119                                    -1,
120                                    0));
121     ASSERT_TRUE(mapping_.ResetAddrLen(mapping_.addr(), page_size * 2));
122 
123     auto buffer = mapping_.addr_as<char*>();
124     for (size_t index = 0; index < mapping_.len(); ++index) {
125       buffer[index] = index % 256;
126     }
127   }
128 
129  private:
BrokerTests(bool set_broker_pid,LinuxVMAddress child1_tls,LinuxVMAddress child2_tls,pid_t child2_tid,const base::FilePath & file_dir,const base::FilePath & test_file,const std::string & expected_file_contents)130   void BrokerTests(bool set_broker_pid,
131                    LinuxVMAddress child1_tls,
132                    LinuxVMAddress child2_tls,
133                    pid_t child2_tid,
134                    const base::FilePath& file_dir,
135                    const base::FilePath& test_file,
136                    const std::string& expected_file_contents) {
137     int socks[2];
138     ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, socks), 0);
139     ScopedFileHandle broker_sock(socks[0]);
140     ScopedFileHandle client_sock(socks[1]);
141 
142 #if defined(ARCH_CPU_64_BITS)
143     constexpr bool am_64_bit = true;
144 #else
145     constexpr bool am_64_bit = false;
146 #endif  // ARCH_CPU_64_BITS
147 
148     PtraceBroker broker(
149         broker_sock.get(), set_broker_pid ? ChildPID() : -1, am_64_bit);
150     RunBrokerThread broker_thread(&broker);
151     broker_thread.Start();
152 
153     PtraceClient client;
154     ASSERT_TRUE(client.Initialize(
155         client_sock.get(), ChildPID(), /* try_direct_memory= */ false));
156 
157     EXPECT_EQ(client.GetProcessID(), ChildPID());
158 
159     std::vector<pid_t> threads;
160     ASSERT_TRUE(client.Threads(&threads));
161     EXPECT_EQ(threads.size(), 2u);
162     if (threads[0] == ChildPID()) {
163       EXPECT_EQ(threads[1], child2_tid);
164     } else {
165       EXPECT_EQ(threads[0], child2_tid);
166       EXPECT_EQ(threads[1], ChildPID());
167     }
168 
169     EXPECT_TRUE(client.Attach(child2_tid));
170     EXPECT_EQ(client.Is64Bit(), am_64_bit);
171 
172     ThreadInfo info1;
173     ASSERT_TRUE(client.GetThreadInfo(ChildPID(), &info1));
174     EXPECT_EQ(info1.thread_specific_data_address, child1_tls);
175 
176     ThreadInfo info2;
177     ASSERT_TRUE(client.GetThreadInfo(child2_tid, &info2));
178     EXPECT_EQ(info2.thread_specific_data_address, child2_tls);
179 
180     ProcessMemory* memory = client.Memory();
181     ASSERT_TRUE(memory);
182 
183     auto buffer = std::make_unique<char[]>(mapping_.len());
184     ASSERT_TRUE(memory->Read(
185         mapping_.addr_as<VMAddress>(), mapping_.len(), buffer.get()));
186     auto expected_buffer = mapping_.addr_as<char*>();
187     for (size_t index = 0; index < mapping_.len(); ++index) {
188       EXPECT_EQ(buffer[index], expected_buffer[index]);
189     }
190 
191     char first;
192     ASSERT_TRUE(
193         memory->Read(mapping_.addr_as<VMAddress>(), sizeof(first), &first));
194     EXPECT_EQ(first, expected_buffer[0]);
195 
196     char last;
197     ASSERT_TRUE(memory->Read(mapping_.addr_as<VMAddress>() + mapping_.len() - 1,
198                              sizeof(last),
199                              &last));
200     EXPECT_EQ(last, expected_buffer[mapping_.len() - 1]);
201 
202     char unmapped;
203     EXPECT_FALSE(memory->Read(mapping_.addr_as<VMAddress>() + mapping_.len(),
204                               sizeof(unmapped),
205                               &unmapped));
206 
207     std::string file_root = file_dir.value() + '/';
208     broker.SetFileRoot(file_root.c_str());
209 
210     std::string file_contents;
211     ASSERT_TRUE(client.ReadFileContents(test_file, &file_contents));
212     EXPECT_EQ(file_contents, expected_file_contents);
213 
214     ScopedTempDir temp_dir2;
215     base::FilePath test_file2(temp_dir2.path().Append("test_file2"));
216     ASSERT_TRUE(CreateFile(test_file2));
217     EXPECT_FALSE(client.ReadFileContents(test_file2, &file_contents));
218   }
219 
MultiprocessParent()220   void MultiprocessParent() override {
221     LinuxVMAddress child1_tls;
222     ASSERT_TRUE(LoggingReadFileExactly(
223         ReadPipeHandle(), &child1_tls, sizeof(child1_tls)));
224 
225     pid_t child2_tid;
226     ASSERT_TRUE(LoggingReadFileExactly(
227         ReadPipeHandle(), &child2_tid, sizeof(child2_tid)));
228 
229     LinuxVMAddress child2_tls;
230     ASSERT_TRUE(LoggingReadFileExactly(
231         ReadPipeHandle(), &child2_tls, sizeof(child2_tls)));
232 
233     ScopedTempDir temp_dir;
234     base::FilePath file_path(temp_dir.path().Append("test_file"));
235     std::string expected_file_contents;
236     {
237       expected_file_contents.resize(4097);
238       for (size_t i = 0; i < expected_file_contents.size(); ++i) {
239         expected_file_contents[i] = static_cast<char>(i % 256);
240       }
241       ScopedFileHandle handle(
242           LoggingOpenFileForWrite(file_path,
243                                   FileWriteMode::kCreateOrFail,
244                                   FilePermissions::kWorldReadable));
245       ASSERT_TRUE(LoggingWriteFile(handle.get(),
246                                    expected_file_contents.data(),
247                                    expected_file_contents.size()));
248     }
249 
250     BrokerTests(true,
251                 child1_tls,
252                 child2_tls,
253                 child2_tid,
254                 temp_dir.path(),
255                 file_path,
256                 expected_file_contents);
257     BrokerTests(false,
258                 child1_tls,
259                 child2_tls,
260                 child2_tid,
261                 temp_dir.path(),
262                 file_path,
263                 expected_file_contents);
264   }
265 
MultiprocessChild()266   void MultiprocessChild() override {
267     LinuxVMAddress tls = GetTLS();
268     ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &tls, sizeof(tls)));
269 
270     BlockOnReadThread thread(ReadPipeHandle(), WritePipeHandle());
271     thread.Start();
272 
273     CheckedReadFileAtEOF(ReadPipeHandle());
274   }
275 
276   ScopedMmap mapping_;
277 
278   DISALLOW_COPY_AND_ASSIGN(SameBitnessTest);
279 };
280 
TEST(PtraceBroker,SameBitness)281 TEST(PtraceBroker, SameBitness) {
282   SameBitnessTest test;
283   test.Run();
284 }
285 
286 // TODO(jperaza): Test against a process with different bitness.
287 
288 }  // namespace
289 }  // namespace test
290 }  // namespace crashpad
291