• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2014 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include <ctype.h>
31 #include <sys/syscall.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34 #include <ucontext.h>
35 
36 #include <sstream>
37 #include <string>
38 
39 #include "breakpad_googletest_includes.h"
40 #include "client/linux/handler/exception_handler.h"
41 #include "client/linux/handler/microdump_extra_info.h"
42 #include "client/linux/microdump_writer/microdump_writer.h"
43 #include "common/linux/breakpad_getcontext.h"
44 #include "common/linux/eintr_wrapper.h"
45 #include "common/linux/ignore_ret.h"
46 #include "common/scoped_ptr.h"
47 #include "common/tests/auto_tempdir.h"
48 #include "common/using_std_string.h"
49 
50 using namespace google_breakpad;
51 
52 extern "C" {
53 extern char __executable_start;
54 extern char __etext;
55 }
56 
57 namespace {
58 
59 typedef testing::Test MicrodumpWriterTest;
60 
MakeMicrodumpExtraInfo(const char * build_fingerprint,const char * product_info,const char * gpu_fingerprint)61 MicrodumpExtraInfo MakeMicrodumpExtraInfo(
62     const char* build_fingerprint,
63     const char* product_info,
64     const char* gpu_fingerprint) {
65   MicrodumpExtraInfo info;
66   info.build_fingerprint = build_fingerprint;
67   info.product_info = product_info;
68   info.gpu_fingerprint = gpu_fingerprint;
69   info.process_type = "Browser";
70   return info;
71 }
72 
ContainsMicrodump(const std::string & buf)73 bool ContainsMicrodump(const std::string& buf) {
74   return std::string::npos != buf.find("-----BEGIN BREAKPAD MICRODUMP-----") &&
75          std::string::npos != buf.find("-----END BREAKPAD MICRODUMP-----");
76 }
77 
78 const char kIdentifiableString[] = "_IDENTIFIABLE_";
79 const uintptr_t kCrashAddress = 0xdeaddeadu;
80 
CrashAndGetMicrodump(const MappingList & mappings,const MicrodumpExtraInfo & microdump_extra_info,std::string * microdump,bool skip_dump_if_principal_mapping_not_referenced=false,uintptr_t address_within_principal_mapping=0,bool sanitize_stack=false)81 void CrashAndGetMicrodump(const MappingList& mappings,
82                           const MicrodumpExtraInfo& microdump_extra_info,
83                           std::string* microdump,
84                           bool skip_dump_if_principal_mapping_not_referenced = false,
85                           uintptr_t address_within_principal_mapping = 0,
86                           bool sanitize_stack = false) {
87   int fds[2];
88   ASSERT_NE(-1, pipe(fds));
89 
90   AutoTempDir temp_dir;
91   string stderr_file = temp_dir.path() + "/stderr.log";
92   int err_fd = open(stderr_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
93   ASSERT_NE(-1, err_fd);
94 
95   char identifiable_string[sizeof(kIdentifiableString)];
96 
97   // This string should not appear in the resulting microdump if it
98   // has been sanitized.
99   strcpy(identifiable_string, kIdentifiableString);
100   // Force the strcpy to not be optimized away.
101   IGNORE_RET(write(STDOUT_FILENO, identifiable_string, 0));
102 
103   const pid_t child = fork();
104   if (child == 0) {
105     close(fds[1]);
106     char b;
107     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
108     close(fds[0]);
109     syscall(__NR_exit);
110   }
111   close(fds[0]);
112 
113   ExceptionHandler::CrashContext context;
114   memset(&context, 0, sizeof(context));
115   // Pretend the current context is the child context (which is
116   // approximately right) so that we have a valid stack pointer, and
117   // can fetch child stack data via ptrace.
118   getcontext(&context.context);
119   // Set a non-zero tid to avoid tripping asserts.
120   context.tid = child;
121   context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED;
122   context.siginfo.si_addr = reinterpret_cast<void*>(kCrashAddress);
123 
124   // Redirect temporarily stderr to the stderr.log file.
125   int save_err = dup(STDERR_FILENO);
126   ASSERT_NE(-1, save_err);
127   ASSERT_NE(-1, dup2(err_fd, STDERR_FILENO));
128 
129   ASSERT_TRUE(WriteMicrodump(child, &context, sizeof(context), mappings,
130                              skip_dump_if_principal_mapping_not_referenced,
131                              address_within_principal_mapping, sanitize_stack,
132                              microdump_extra_info));
133 
134   // Revert stderr back to the console.
135   dup2(save_err, STDERR_FILENO);
136   close(save_err);
137 
138   // Read back the stderr file and check for the microdump marker.
139   fsync(err_fd);
140   lseek(err_fd, 0, SEEK_SET);
141 
142   microdump->clear();
143   char buf[1024];
144 
145   while (true) {
146     int bytes_read = IGNORE_EINTR(read(err_fd, buf, 1024));
147     if (bytes_read <= 0) break;
148     microdump->append(buf, buf + bytes_read);
149   }
150   close(err_fd);
151   close(fds[1]);
152 }
153 
ExtractMicrodumpStackContents(const string & microdump_content,string * result)154 void ExtractMicrodumpStackContents(const string& microdump_content,
155                                    string* result) {
156   std::istringstream iss(microdump_content);
157   result->clear();
158   for (string line; std::getline(iss, line);) {
159     if (line.find("S ") == 0) {
160       std::istringstream stack_data(line);
161       std::string key;
162       std::string addr;
163       std::string data;
164       stack_data >> key >> addr >> data;
165       EXPECT_TRUE((data.size() & 1u) == 0u);
166       result->reserve(result->size() + data.size() / 2);
167       for (size_t i = 0; i < data.size(); i += 2) {
168         std::string byte = data.substr(i, 2);
169         result->push_back(static_cast<char>(strtoul(byte.c_str(), NULL, 16)));
170       }
171     }
172   }
173 }
174 
CheckMicrodumpContents(const string & microdump_content,const MicrodumpExtraInfo & expected_info)175 void CheckMicrodumpContents(const string& microdump_content,
176                             const MicrodumpExtraInfo& expected_info) {
177   std::istringstream iss(microdump_content);
178   bool did_find_os_info = false;
179   bool did_find_product_info = false;
180   bool did_find_process_type = false;
181   bool did_find_crash_reason = false;
182   bool did_find_gpu_info = false;
183   for (string line; std::getline(iss, line);) {
184     if (line.find("O ") == 0) {
185       std::istringstream os_info_tokens(line);
186       string token;
187       os_info_tokens.ignore(2); // Ignore the "O " preamble.
188       // Check the OS descriptor char (L=Linux, A=Android).
189       os_info_tokens >> token;
190       ASSERT_TRUE(token == "L" || token == "A");
191 
192       os_info_tokens >> token; // HW architecture.
193       os_info_tokens >> token; // Number of cpus.
194       for (size_t i = 0; i < token.size(); ++i)
195         ASSERT_TRUE(isxdigit(token[i]));
196       os_info_tokens >> token; // SW architecture.
197 
198       // Check that the build fingerprint is in the right place.
199       os_info_tokens >> token;
200       ASSERT_FALSE(os_info_tokens.fail());
201       if (expected_info.build_fingerprint)
202         ASSERT_EQ(expected_info.build_fingerprint, token);
203       did_find_os_info = true;
204     } else if (line.find("P ") == 0) {
205       if (expected_info.process_type)
206         ASSERT_EQ(string("P ") + expected_info.process_type, line);
207       did_find_process_type = true;
208     } else if (line.find("R ") == 0) {
209       std::istringstream crash_reason_tokens(line);
210       string token;
211       unsigned crash_reason;
212       string crash_reason_str;
213       uintptr_t crash_address;
214       crash_reason_tokens.ignore(2); // Ignore the "R " preamble.
215       crash_reason_tokens >> std::hex >> crash_reason >> crash_reason_str >>
216           crash_address;
217       ASSERT_FALSE(crash_reason_tokens.fail());
218       ASSERT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED, crash_reason);
219       ASSERT_EQ("DUMP_REQUESTED", crash_reason_str);
220       ASSERT_EQ(kCrashAddress, crash_address);
221       did_find_crash_reason = true;
222     } else if (line.find("V ") == 0) {
223       if (expected_info.product_info)
224         ASSERT_EQ(string("V ") + expected_info.product_info, line);
225       did_find_product_info = true;
226     } else if (line.find("G ") == 0) {
227       if (expected_info.gpu_fingerprint)
228         ASSERT_EQ(string("G ") + expected_info.gpu_fingerprint, line);
229       did_find_gpu_info = true;
230     }
231   }
232   ASSERT_TRUE(did_find_os_info);
233   ASSERT_TRUE(did_find_product_info);
234   ASSERT_TRUE(did_find_process_type);
235   ASSERT_TRUE(did_find_crash_reason);
236   ASSERT_TRUE(did_find_gpu_info);
237 }
238 
MicrodumpStackContains(const string & microdump_content,const string & expected_content)239 bool MicrodumpStackContains(const string& microdump_content,
240                             const string& expected_content) {
241   string result;
242   ExtractMicrodumpStackContents(microdump_content, &result);
243   return result.find(kIdentifiableString) != string::npos;
244 }
245 
CheckMicrodumpContents(const string & microdump_content,const string & expected_fingerprint,const string & expected_product_info,const string & expected_gpu_fingerprint)246 void CheckMicrodumpContents(const string& microdump_content,
247                             const string& expected_fingerprint,
248                             const string& expected_product_info,
249                             const string& expected_gpu_fingerprint) {
250   CheckMicrodumpContents(
251       microdump_content,
252       MakeMicrodumpExtraInfo(expected_fingerprint.c_str(),
253                              expected_product_info.c_str(),
254                              expected_gpu_fingerprint.c_str()));
255 }
256 
TEST(MicrodumpWriterTest,BasicWithMappings)257 TEST(MicrodumpWriterTest, BasicWithMappings) {
258   // Push some extra mapping to check the MappingList logic.
259   const uint32_t memory_size = sysconf(_SC_PAGESIZE);
260   const char* kMemoryName = "libfoo.so";
261   const uint8_t kModuleGUID[sizeof(MDGUID)] = {
262      0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
263      0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
264   };
265 
266   MappingInfo info;
267   info.start_addr = memory_size;
268   info.size = memory_size;
269   info.offset = 42;
270   strcpy(info.name, kMemoryName);
271 
272   MappingList mappings;
273   MappingEntry mapping;
274   mapping.first = info;
275   memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
276   mappings.push_back(mapping);
277 
278   std::string buf;
279   CrashAndGetMicrodump(mappings, MicrodumpExtraInfo(), &buf);
280   ASSERT_TRUE(ContainsMicrodump(buf));
281 
282 #ifdef __LP64__
283   ASSERT_NE(std::string::npos,
284             buf.find("M 0000000000001000 000000000000002A 0000000000001000 "
285                      "33221100554477668899AABBCCDDEEFF0 libfoo.so"));
286 #else
287   ASSERT_NE(std::string::npos,
288             buf.find("M 00001000 0000002A 00001000 "
289                      "33221100554477668899AABBCCDDEEFF0 libfoo.so"));
290 #endif
291 
292   // In absence of a product info in the minidump, the writer should just write
293   // an unknown marker.
294   ASSERT_NE(std::string::npos, buf.find("V UNKNOWN:0.0.0.0"));
295 }
296 
297 // Ensure that no output occurs if the interest region is set, but
298 // doesn't overlap anything on the stack.
TEST(MicrodumpWriterTest,NoOutputIfUninteresting)299 TEST(MicrodumpWriterTest, NoOutputIfUninteresting) {
300   const char kProductInfo[] = "MockProduct:42.0.2311.99";
301   const char kBuildFingerprint[] =
302       "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
303   const char kGPUFingerprint[] =
304       "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@  (GIT@Id3510ff6dc)";
305   const MicrodumpExtraInfo kMicrodumpExtraInfo(
306       MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
307 
308   std::string buf;
309   MappingList no_mappings;
310 
311   CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, true, 0);
312   ASSERT_FALSE(ContainsMicrodump(buf));
313 }
314 
315 // Ensure that stack content does not contain an identifiable string if the
316 // stack is sanitized.
TEST(MicrodumpWriterTest,StringRemovedBySanitization)317 TEST(MicrodumpWriterTest, StringRemovedBySanitization) {
318   const char kProductInfo[] = "MockProduct:42.0.2311.99";
319   const char kBuildFingerprint[] =
320       "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
321   const char kGPUFingerprint[] =
322       "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@  (GIT@Id3510ff6dc)";
323 
324   const MicrodumpExtraInfo kMicrodumpExtraInfo(
325       MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
326 
327   std::string buf;
328   MappingList no_mappings;
329 
330   CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, false, 0u, true);
331   ASSERT_TRUE(ContainsMicrodump(buf));
332   ASSERT_FALSE(MicrodumpStackContains(buf, kIdentifiableString));
333 }
334 
335 // Ensure that stack content does contain an identifiable string if the
336 // stack is not sanitized.
TEST(MicrodumpWriterTest,StringPresentIfNotSanitized)337 TEST(MicrodumpWriterTest, StringPresentIfNotSanitized) {
338   const char kProductInfo[] = "MockProduct:42.0.2311.99";
339   const char kBuildFingerprint[] =
340       "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
341   const char kGPUFingerprint[] =
342       "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@  (GIT@Id3510ff6dc)";
343 
344   const MicrodumpExtraInfo kMicrodumpExtraInfo(
345       MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
346 
347   std::string buf;
348   MappingList no_mappings;
349 
350   CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, false, 0u, false);
351   ASSERT_TRUE(ContainsMicrodump(buf));
352   ASSERT_TRUE(MicrodumpStackContains(buf, kIdentifiableString));
353 }
354 
355 // Ensure that output occurs if the interest region is set, and
356 // does overlap something on the stack.
TEST(MicrodumpWriterTest,OutputIfInteresting)357 TEST(MicrodumpWriterTest, OutputIfInteresting) {
358   const char kProductInfo[] = "MockProduct:42.0.2311.99";
359   const char kBuildFingerprint[] =
360       "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
361   const char kGPUFingerprint[] =
362       "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@  (GIT@Id3510ff6dc)";
363 
364   const MicrodumpExtraInfo kMicrodumpExtraInfo(
365       MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
366 
367   std::string buf;
368   MappingList no_mappings;
369 
370   CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, true,
371                        reinterpret_cast<uintptr_t>(CrashAndGetMicrodump));
372   ASSERT_TRUE(ContainsMicrodump(buf));
373 }
374 
375 // Ensure that the product info and build fingerprint metadata show up in the
376 // final microdump if present.
TEST(MicrodumpWriterTest,BuildFingerprintAndProductInfo)377 TEST(MicrodumpWriterTest, BuildFingerprintAndProductInfo) {
378   const char kProductInfo[] = "MockProduct:42.0.2311.99";
379   const char kBuildFingerprint[] =
380       "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
381   const char kGPUFingerprint[] =
382       "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@  (GIT@Id3510ff6dc)";
383   const MicrodumpExtraInfo kMicrodumpExtraInfo(
384       MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
385   std::string buf;
386   MappingList no_mappings;
387 
388   CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf);
389   ASSERT_TRUE(ContainsMicrodump(buf));
390   CheckMicrodumpContents(buf, kMicrodumpExtraInfo);
391 }
392 
TEST(MicrodumpWriterTest,NoProductInfo)393 TEST(MicrodumpWriterTest, NoProductInfo) {
394   const char kBuildFingerprint[] = "foobar";
395   const char kGPUFingerprint[] = "bazqux";
396   std::string buf;
397   MappingList no_mappings;
398 
399   const MicrodumpExtraInfo kMicrodumpExtraInfoNoProductInfo(
400       MakeMicrodumpExtraInfo(kBuildFingerprint, NULL, kGPUFingerprint));
401 
402   CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoProductInfo, &buf);
403   ASSERT_TRUE(ContainsMicrodump(buf));
404   CheckMicrodumpContents(buf, kBuildFingerprint, "UNKNOWN:0.0.0.0",
405                          kGPUFingerprint);
406 }
407 
TEST(MicrodumpWriterTest,NoGPUInfo)408 TEST(MicrodumpWriterTest, NoGPUInfo) {
409   const char kProductInfo[] = "bazqux";
410   const char kBuildFingerprint[] = "foobar";
411   std::string buf;
412   MappingList no_mappings;
413 
414   const MicrodumpExtraInfo kMicrodumpExtraInfoNoGPUInfo(
415       MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, NULL));
416 
417   CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoGPUInfo, &buf);
418   ASSERT_TRUE(ContainsMicrodump(buf));
419   CheckMicrodumpContents(buf, kBuildFingerprint, kProductInfo, "UNKNOWN");
420 }
421 }  // namespace
422