1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <libdebuggerd/tombstone.h>
18 
19 #include <inttypes.h>
20 
21 #include <functional>
22 #include <set>
23 #include <string>
24 #include <unordered_set>
25 #include <utility>
26 #include <vector>
27 
28 #include <android-base/stringprintf.h>
29 #include <android-base/strings.h>
30 #include <android-base/unique_fd.h>
31 #include <async_safe/log.h>
32 #include <bionic/macros.h>
33 
34 #include "tombstone.pb.h"
35 
36 using android::base::StringAppendF;
37 using android::base::StringPrintf;
38 
39 #define CB(log, ...) callback(StringPrintf(__VA_ARGS__), log)
40 #define CBL(...) CB(true, __VA_ARGS__)
41 #define CBS(...) CB(false, __VA_ARGS__)
42 using CallbackType = std::function<void(const std::string& line, bool should_log)>;
43 
abi_string(const Tombstone & tombstone)44 static const char* abi_string(const Tombstone& tombstone) {
45   switch (tombstone.arch()) {
46     case Architecture::ARM32:
47       return "arm";
48     case Architecture::ARM64:
49       return "arm64";
50     case Architecture::RISCV64:
51       return "riscv64";
52     case Architecture::X86:
53       return "x86";
54     case Architecture::X86_64:
55       return "x86_64";
56     default:
57       return "<unknown>";
58   }
59 }
60 
pointer_width(const Tombstone & tombstone)61 static int pointer_width(const Tombstone& tombstone) {
62   switch (tombstone.arch()) {
63     case Architecture::ARM32:
64       return 4;
65     case Architecture::ARM64:
66       return 8;
67     case Architecture::RISCV64:
68       return 8;
69     case Architecture::X86:
70       return 4;
71     case Architecture::X86_64:
72       return 8;
73     default:
74       return 8;
75   }
76 }
77 
print_thread_header(CallbackType callback,const Tombstone & tombstone,const Thread & thread,bool should_log)78 static void print_thread_header(CallbackType callback, const Tombstone& tombstone,
79                                 const Thread& thread, bool should_log) {
80   const char* process_name = "<unknown>";
81   if (!tombstone.command_line().empty()) {
82     process_name = tombstone.command_line()[0].c_str();
83     CB(should_log, "Cmdline: %s", android::base::Join(tombstone.command_line(), " ").c_str());
84   }
85   CB(should_log, "pid: %d, tid: %d, name: %s  >>> %s <<<", tombstone.pid(), thread.id(),
86      thread.name().c_str(), process_name);
87   CB(should_log, "uid: %d", tombstone.uid());
88   if (thread.tagged_addr_ctrl() != -1) {
89     CB(should_log, "tagged_addr_ctrl: %016" PRIx64 "%s", thread.tagged_addr_ctrl(),
90        describe_tagged_addr_ctrl(thread.tagged_addr_ctrl()).c_str());
91   }
92   if (thread.pac_enabled_keys() != -1) {
93     CB(should_log, "pac_enabled_keys: %016" PRIx64 "%s", thread.pac_enabled_keys(),
94        describe_pac_enabled_keys(thread.pac_enabled_keys()).c_str());
95   }
96 }
97 
print_register_row(CallbackType callback,int word_size,std::vector<std::pair<std::string,uint64_t>> row,bool should_log)98 static void print_register_row(CallbackType callback, int word_size,
99                                std::vector<std::pair<std::string, uint64_t>> row, bool should_log) {
100   std::string output = "  ";
101   for (const auto& [name, value] : row) {
102     output += android::base::StringPrintf("  %-3s %0*" PRIx64, name.c_str(), 2 * word_size,
103                                           static_cast<uint64_t>(value));
104   }
105   callback(output, should_log);
106 }
107 
print_thread_registers(CallbackType callback,const Tombstone & tombstone,const Thread & thread,bool should_log)108 static void print_thread_registers(CallbackType callback, const Tombstone& tombstone,
109                                    const Thread& thread, bool should_log) {
110   static constexpr size_t column_count = 4;
111   std::vector<std::pair<std::string, uint64_t>> current_row;
112   std::vector<std::pair<std::string, uint64_t>> special_row;
113   std::unordered_set<std::string> special_registers;
114 
115   int word_size = pointer_width(tombstone);
116 
117   switch (tombstone.arch()) {
118     case Architecture::ARM32:
119       special_registers = {"ip", "lr", "sp", "pc", "pst"};
120       break;
121 
122     case Architecture::ARM64:
123       special_registers = {"ip", "lr", "sp", "pc", "pst"};
124       break;
125 
126     case Architecture::RISCV64:
127       special_registers = {"ra", "sp", "pc"};
128       break;
129 
130     case Architecture::X86:
131       special_registers = {"ebp", "esp", "eip"};
132       break;
133 
134     case Architecture::X86_64:
135       special_registers = {"rbp", "rsp", "rip"};
136       break;
137 
138     default:
139       async_safe_fatal("unknown architecture");
140   }
141 
142   for (const auto& reg : thread.registers()) {
143     auto row = &current_row;
144     if (special_registers.count(reg.name()) == 1) {
145       row = &special_row;
146     }
147 
148     row->emplace_back(reg.name(), reg.u64());
149     if (current_row.size() == column_count) {
150       print_register_row(callback, word_size, current_row, should_log);
151       current_row.clear();
152     }
153   }
154 
155   if (!current_row.empty()) {
156     print_register_row(callback, word_size, current_row, should_log);
157   }
158 
159   print_register_row(callback, word_size, special_row, should_log);
160 }
161 
print_backtrace(CallbackType callback,const Tombstone & tombstone,const google::protobuf::RepeatedPtrField<BacktraceFrame> & backtrace,bool should_log)162 static void print_backtrace(CallbackType callback, const Tombstone& tombstone,
163                             const google::protobuf::RepeatedPtrField<BacktraceFrame>& backtrace,
164                             bool should_log) {
165   int index = 0;
166   for (const auto& frame : backtrace) {
167     std::string function;
168 
169     if (!frame.function_name().empty()) {
170       function =
171           StringPrintf(" (%s+%" PRId64 ")", frame.function_name().c_str(), frame.function_offset());
172     }
173 
174     std::string build_id;
175     if (!frame.build_id().empty()) {
176       build_id = StringPrintf(" (BuildId: %s)", frame.build_id().c_str());
177     }
178 
179     std::string line =
180         StringPrintf("      #%02d pc %0*" PRIx64 "  %s", index++, pointer_width(tombstone) * 2,
181                      frame.rel_pc(), frame.file_name().c_str());
182     if (frame.file_map_offset() != 0) {
183       line += StringPrintf(" (offset 0x%" PRIx64 ")", frame.file_map_offset());
184     }
185     line += function + build_id;
186     CB(should_log, "%s", line.c_str());
187   }
188 }
189 
print_thread_backtrace(CallbackType callback,const Tombstone & tombstone,const Thread & thread,bool should_log)190 static void print_thread_backtrace(CallbackType callback, const Tombstone& tombstone,
191                                    const Thread& thread, bool should_log) {
192   CBS("");
193   CB(should_log, "%d total frames", thread.current_backtrace().size());
194   CB(should_log, "backtrace:");
195   if (!thread.backtrace_note().empty()) {
196     CB(should_log, "  NOTE: %s",
197        android::base::Join(thread.backtrace_note(), "\n  NOTE: ").c_str());
198   }
199   print_backtrace(callback, tombstone, thread.current_backtrace(), should_log);
200 }
201 
print_thread_memory_dump(CallbackType callback,const Tombstone & tombstone,const Thread & thread)202 static void print_thread_memory_dump(CallbackType callback, const Tombstone& tombstone,
203                                      const Thread& thread) {
204   static constexpr size_t bytes_per_line = 16;
205   static_assert(bytes_per_line == kTagGranuleSize);
206   int word_size = pointer_width(tombstone);
207   for (const auto& mem : thread.memory_dump()) {
208     CBS("");
209     if (mem.mapping_name().empty()) {
210       CBS("memory near %s:", mem.register_name().c_str());
211     } else {
212       CBS("memory near %s (%s):", mem.register_name().c_str(), mem.mapping_name().c_str());
213     }
214     uint64_t addr = mem.begin_address();
215     for (size_t offset = 0; offset < mem.memory().size(); offset += bytes_per_line) {
216       uint64_t tagged_addr = addr;
217       if (mem.has_arm_mte_metadata() &&
218           mem.arm_mte_metadata().memory_tags().size() > offset / kTagGranuleSize) {
219         tagged_addr |=
220             static_cast<uint64_t>(mem.arm_mte_metadata().memory_tags()[offset / kTagGranuleSize])
221             << 56;
222       }
223       std::string line = StringPrintf("    %0*" PRIx64, word_size * 2, tagged_addr + offset);
224 
225       size_t bytes = std::min(bytes_per_line, mem.memory().size() - offset);
226       for (size_t i = 0; i < bytes; i += word_size) {
227         uint64_t word = 0;
228 
229         // Assumes little-endian, but what doesn't?
230         memcpy(&word, mem.memory().data() + offset + i, word_size);
231 
232         StringAppendF(&line, " %0*" PRIx64, word_size * 2, word);
233       }
234 
235       char ascii[bytes_per_line + 1];
236 
237       memset(ascii, '.', sizeof(ascii));
238       ascii[bytes_per_line] = '\0';
239 
240       for (size_t i = 0; i < bytes; ++i) {
241         uint8_t byte = mem.memory()[offset + i];
242         if (byte >= 0x20 && byte < 0x7f) {
243           ascii[i] = byte;
244         }
245       }
246 
247       CBS("%s  %s", line.c_str(), ascii);
248     }
249   }
250 }
251 
print_thread(CallbackType callback,const Tombstone & tombstone,const Thread & thread)252 static void print_thread(CallbackType callback, const Tombstone& tombstone, const Thread& thread) {
253   print_thread_header(callback, tombstone, thread, false);
254   print_thread_registers(callback, tombstone, thread, false);
255   print_thread_backtrace(callback, tombstone, thread, false);
256   print_thread_memory_dump(callback, tombstone, thread);
257 }
258 
print_tag_dump(CallbackType callback,const Tombstone & tombstone)259 static void print_tag_dump(CallbackType callback, const Tombstone& tombstone) {
260   if (!tombstone.has_signal_info()) return;
261 
262   const Signal& signal = tombstone.signal_info();
263 
264   if (!signal.has_fault_address() || !signal.has_fault_adjacent_metadata()) {
265     return;
266   }
267 
268   const MemoryDump& memory_dump = signal.fault_adjacent_metadata();
269 
270   if (!memory_dump.has_arm_mte_metadata() || memory_dump.arm_mte_metadata().memory_tags().empty()) {
271     return;
272   }
273 
274   const std::string& tags = memory_dump.arm_mte_metadata().memory_tags();
275 
276   CBS("");
277   CBS("Memory tags around the fault address (0x%" PRIx64 "), one tag per %zu bytes:",
278       signal.fault_address(), kTagGranuleSize);
279   constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
280 
281   size_t tag_index = 0;
282   size_t num_tags = tags.length();
283   uintptr_t fault_granule = untag_address(signal.fault_address()) & ~(kTagGranuleSize - 1);
284   for (size_t row = 0; tag_index < num_tags; ++row) {
285     uintptr_t row_addr =
286         (memory_dump.begin_address() + row * kNumTagColumns * kTagGranuleSize) & kRowStartMask;
287     std::string row_contents;
288     bool row_has_fault = false;
289 
290     for (size_t column = 0; column < kNumTagColumns; ++column) {
291       uintptr_t granule_addr = row_addr + column * kTagGranuleSize;
292       if (granule_addr < memory_dump.begin_address() ||
293           granule_addr >= memory_dump.begin_address() + num_tags * kTagGranuleSize) {
294         row_contents += " . ";
295       } else if (granule_addr == fault_granule) {
296         row_contents += StringPrintf("[%1hhx]", tags[tag_index++]);
297         row_has_fault = true;
298       } else {
299         row_contents += StringPrintf(" %1hhx ", tags[tag_index++]);
300       }
301     }
302 
303     if (row_contents.back() == ' ') row_contents.pop_back();
304 
305     if (row_has_fault) {
306       CBS("    =>0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
307     } else {
308       CBS("      0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
309     }
310   }
311 }
312 
print_memory_maps(CallbackType callback,const Tombstone & tombstone)313 static void print_memory_maps(CallbackType callback, const Tombstone& tombstone) {
314   int word_size = pointer_width(tombstone);
315   const auto format_pointer = [word_size](uint64_t ptr) -> std::string {
316     if (word_size == 8) {
317       uint64_t top = ptr >> 32;
318       uint64_t bottom = ptr & 0xFFFFFFFF;
319       return StringPrintf("%08" PRIx64 "'%08" PRIx64, top, bottom);
320     }
321 
322     return StringPrintf("%0*" PRIx64, word_size * 2, ptr);
323   };
324 
325   std::string memory_map_header =
326       StringPrintf("memory map (%d %s):", tombstone.memory_mappings().size(),
327                    tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
328 
329   const Signal& signal_info = tombstone.signal_info();
330   bool has_fault_address = signal_info.has_fault_address();
331   uint64_t fault_address = untag_address(signal_info.fault_address());
332   bool preamble_printed = false;
333   bool printed_fault_address_marker = false;
334   for (const auto& map : tombstone.memory_mappings()) {
335     if (!preamble_printed) {
336       preamble_printed = true;
337       if (has_fault_address) {
338         if (fault_address < map.begin_address()) {
339           memory_map_header +=
340               StringPrintf("\n--->Fault address falls at %s before any mapped regions",
341                            format_pointer(fault_address).c_str());
342           printed_fault_address_marker = true;
343         } else {
344           memory_map_header += " (fault address prefixed with --->)";
345         }
346       }
347       CBS("%s", memory_map_header.c_str());
348     }
349 
350     std::string line = "    ";
351     if (has_fault_address && !printed_fault_address_marker) {
352       if (fault_address < map.begin_address()) {
353         printed_fault_address_marker = true;
354         CBS("--->Fault address falls at %s between mapped regions",
355             format_pointer(fault_address).c_str());
356       } else if (fault_address >= map.begin_address() && fault_address < map.end_address()) {
357         printed_fault_address_marker = true;
358         line = "--->";
359       }
360     }
361     StringAppendF(&line, "%s-%s", format_pointer(map.begin_address()).c_str(),
362                   format_pointer(map.end_address() - 1).c_str());
363     StringAppendF(&line, " %s%s%s", map.read() ? "r" : "-", map.write() ? "w" : "-",
364                   map.execute() ? "x" : "-");
365     StringAppendF(&line, "  %8" PRIx64 "  %8" PRIx64, map.offset(),
366                   map.end_address() - map.begin_address());
367 
368     if (!map.mapping_name().empty()) {
369       StringAppendF(&line, "  %s", map.mapping_name().c_str());
370 
371       if (!map.build_id().empty()) {
372         StringAppendF(&line, " (BuildId: %s)", map.build_id().c_str());
373       }
374 
375       if (map.load_bias() != 0) {
376         StringAppendF(&line, " (load bias 0x%" PRIx64 ")", map.load_bias());
377       }
378     }
379 
380     CBS("%s", line.c_str());
381   }
382 
383   if (has_fault_address && !printed_fault_address_marker) {
384     CBS("--->Fault address falls at %s after any mapped regions",
385         format_pointer(fault_address).c_str());
386   }
387 }
388 
print_main_thread(CallbackType callback,const Tombstone & tombstone,const Thread & thread)389 static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
390                               const Thread& thread) {
391   print_thread_header(callback, tombstone, thread, true);
392 
393   const Signal& signal_info = tombstone.signal_info();
394   std::string sender_desc;
395 
396   if (signal_info.has_sender()) {
397     sender_desc =
398         StringPrintf(" from pid %d, uid %d", signal_info.sender_pid(), signal_info.sender_uid());
399   }
400 
401   bool is_async_mte_crash = false;
402   bool is_mte_crash = false;
403   if (!tombstone.has_signal_info()) {
404     CBL("signal information missing");
405   } else {
406     std::string fault_addr_desc;
407     if (signal_info.has_fault_address()) {
408       fault_addr_desc =
409           StringPrintf("0x%0*" PRIx64, 2 * pointer_width(tombstone), signal_info.fault_address());
410     } else {
411       fault_addr_desc = "--------";
412     }
413 
414     CBL("signal %d (%s), code %d (%s%s), fault addr %s", signal_info.number(),
415         signal_info.name().c_str(), signal_info.code(), signal_info.code_name().c_str(),
416         sender_desc.c_str(), fault_addr_desc.c_str());
417 #ifdef SEGV_MTEAERR
418     is_async_mte_crash = signal_info.number() == SIGSEGV && signal_info.code() == SEGV_MTEAERR;
419     is_mte_crash = is_async_mte_crash ||
420                    (signal_info.number() == SIGSEGV && signal_info.code() == SEGV_MTESERR);
421 #endif
422   }
423 
424   if (tombstone.causes_size() == 1) {
425     CBL("Cause: %s", tombstone.causes(0).human_readable().c_str());
426   }
427 
428   if (!tombstone.abort_message().empty()) {
429     CBL("Abort message: '%s'", tombstone.abort_message().c_str());
430   }
431 
432   print_thread_registers(callback, tombstone, thread, true);
433   if (is_async_mte_crash) {
434     CBL("Note: This crash is a delayed async MTE crash. Memory corruption has occurred");
435     CBL("      in this process. The stack trace below is the first system call or context");
436     CBL("      switch that was executed after the memory corruption happened.");
437   }
438   print_thread_backtrace(callback, tombstone, thread, true);
439 
440   if (tombstone.causes_size() > 1) {
441     CBS("");
442     CBL("Note: multiple potential causes for this crash were detected, listing them in decreasing "
443         "order of likelihood.");
444   }
445 
446   for (const Cause& cause : tombstone.causes()) {
447     if (tombstone.causes_size() > 1) {
448       CBS("");
449       CBL("Cause: %s", cause.human_readable().c_str());
450     }
451 
452     if (cause.has_memory_error() && cause.memory_error().has_heap()) {
453       const HeapObject& heap_object = cause.memory_error().heap();
454 
455       if (heap_object.deallocation_backtrace_size() != 0) {
456         CBS("");
457         CBL("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid());
458         print_backtrace(callback, tombstone, heap_object.deallocation_backtrace(), true);
459       }
460 
461       if (heap_object.allocation_backtrace_size() != 0) {
462         CBS("");
463         CBL("allocated by thread %" PRIu64 ":", heap_object.allocation_tid());
464         print_backtrace(callback, tombstone, heap_object.allocation_backtrace(), true);
465       }
466     }
467   }
468 
469   print_tag_dump(callback, tombstone);
470 
471   if (is_mte_crash) {
472     CBS("");
473     CBL("Learn more about MTE reports: "
474         "https://source.android.com/docs/security/test/memory-safety/mte-reports");
475   }
476 
477   print_thread_memory_dump(callback, tombstone, thread);
478 
479   CBS("");
480 
481   // No memory maps to print.
482   if (!tombstone.memory_mappings().empty()) {
483     print_memory_maps(callback, tombstone);
484   } else {
485     CBS("No memory maps found");
486   }
487 }
488 
print_logs(CallbackType callback,const Tombstone & tombstone,int tail)489 void print_logs(CallbackType callback, const Tombstone& tombstone, int tail) {
490   for (const auto& buffer : tombstone.log_buffers()) {
491     if (tail) {
492       CBS("--------- tail end of log %s", buffer.name().c_str());
493     } else {
494       CBS("--------- log %s", buffer.name().c_str());
495     }
496 
497     int begin = 0;
498     if (tail != 0) {
499       begin = std::max(0, buffer.logs().size() - tail);
500     }
501 
502     for (int i = begin; i < buffer.logs().size(); ++i) {
503       const LogMessage& msg = buffer.logs(i);
504 
505       static const char* kPrioChars = "!.VDIWEFS";
506       char priority = (msg.priority() < strlen(kPrioChars) ? kPrioChars[msg.priority()] : '?');
507       CBS("%s %5u %5u %c %-8s: %s", msg.timestamp().c_str(), msg.pid(), msg.tid(), priority,
508           msg.tag().c_str(), msg.message().c_str());
509     }
510   }
511 }
512 
tombstone_proto_to_text(const Tombstone & tombstone,CallbackType callback)513 bool tombstone_proto_to_text(const Tombstone& tombstone, CallbackType callback) {
514   CBL("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***");
515   CBL("Build fingerprint: '%s'", tombstone.build_fingerprint().c_str());
516   CBL("Revision: '%s'", tombstone.revision().c_str());
517   CBL("ABI: '%s'", abi_string(tombstone));
518   CBL("Timestamp: %s", tombstone.timestamp().c_str());
519   CBL("Process uptime: %ds", tombstone.process_uptime());
520 
521   // Process header
522   const auto& threads = tombstone.threads();
523   auto main_thread_it = threads.find(tombstone.tid());
524   if (main_thread_it == threads.end()) {
525     CBL("failed to find entry for main thread in tombstone");
526     return false;
527   }
528 
529   const auto& main_thread = main_thread_it->second;
530 
531   print_main_thread(callback, tombstone, main_thread);
532 
533   print_logs(callback, tombstone, 50);
534 
535   // protobuf's map is unordered, so sort the keys first.
536   std::set<int> thread_ids;
537   for (const auto& [tid, _] : threads) {
538     if (tid != tombstone.tid()) {
539       thread_ids.insert(tid);
540     }
541   }
542 
543   for (const auto& tid : thread_ids) {
544     CBS("--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---");
545     print_thread(callback, tombstone, threads.find(tid)->second);
546   }
547 
548   if (tombstone.open_fds().size() > 0) {
549     CBS("");
550     CBS("open files:");
551     for (const auto& fd : tombstone.open_fds()) {
552       std::optional<std::string> owner;
553       if (!fd.owner().empty()) {
554         owner = StringPrintf("owned by %s 0x%" PRIx64, fd.owner().c_str(), fd.tag());
555       }
556 
557       CBS("    fd %d: %s (%s)", fd.fd(), fd.path().c_str(), owner ? owner->c_str() : "unowned");
558     }
559   }
560 
561   print_logs(callback, tombstone, 0);
562 
563   return true;
564 }
565