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/scudo.h"
18 #include "libdebuggerd/gwp_asan.h"
19
20 #include "unwindstack/Memory.h"
21 #include "unwindstack/Unwinder.h"
22
23 #include <bionic/macros.h>
24
AllocAndReadFully(unwindstack::Memory * process_memory,uint64_t addr,size_t size)25 std::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, uint64_t addr,
26 size_t size) {
27 auto buf = std::make_unique<char[]>(size);
28 if (!process_memory->ReadFully(addr, buf.get(), size)) {
29 return std::unique_ptr<char[]>();
30 }
31 return buf;
32 }
33
34 static const uintptr_t kTagGranuleSize = 16;
35
ScudoCrashData(unwindstack::Memory * process_memory,const ProcessInfo & process_info)36 ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,
37 const ProcessInfo& process_info) {
38 if (!process_info.has_fault_address) {
39 return;
40 }
41
42 auto stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
43 __scudo_get_stack_depot_size());
44 auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
45 __scudo_get_region_info_size());
46
47 untagged_fault_addr_ = untag_address(process_info.fault_address);
48 uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
49
50 uintptr_t memory_begin = fault_page - PAGE_SIZE * 16;
51 if (memory_begin > fault_page) {
52 return;
53 }
54
55 uintptr_t memory_end = fault_page + PAGE_SIZE * 16;
56 if (memory_end < fault_page) {
57 return;
58 }
59
60 auto memory = std::make_unique<char[]>(memory_end - memory_begin);
61 for (auto i = memory_begin; i != memory_end; i += PAGE_SIZE) {
62 process_memory->ReadFully(i, memory.get() + i - memory_begin, PAGE_SIZE);
63 }
64
65 auto memory_tags = std::make_unique<char[]>((memory_end - memory_begin) / kTagGranuleSize);
66 for (auto i = memory_begin; i != memory_end; i += kTagGranuleSize) {
67 memory_tags[(i - memory_begin) / kTagGranuleSize] = process_memory->ReadTag(i);
68 }
69
70 __scudo_get_error_info(&error_info_, process_info.fault_address, stack_depot.get(),
71 region_info.get(), memory.get(), memory_tags.get(), memory_begin,
72 memory_end - memory_begin);
73 }
74
CrashIsMine() const75 bool ScudoCrashData::CrashIsMine() const {
76 return error_info_.reports[0].error_type != UNKNOWN;
77 }
78
DumpCause(log_t * log,unwindstack::Unwinder * unwinder) const79 void ScudoCrashData::DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const {
80 if (error_info_.reports[1].error_type != UNKNOWN) {
81 _LOG(log, logtype::HEADER,
82 "\nNote: multiple potential causes for this crash were detected, listing them in "
83 "decreasing order of probability.\n");
84 }
85
86 size_t report_num = 0;
87 while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
88 error_info_.reports[report_num].error_type != UNKNOWN) {
89 DumpReport(&error_info_.reports[report_num++], log, unwinder);
90 }
91 }
92
DumpReport(const scudo_error_report * report,log_t * log,unwindstack::Unwinder * unwinder) const93 void ScudoCrashData::DumpReport(const scudo_error_report* report, log_t* log,
94 unwindstack::Unwinder* unwinder) const {
95 const char *error_type_str;
96 switch (report->error_type) {
97 case USE_AFTER_FREE:
98 error_type_str = "Use After Free";
99 break;
100 case BUFFER_OVERFLOW:
101 error_type_str = "Buffer Overflow";
102 break;
103 case BUFFER_UNDERFLOW:
104 error_type_str = "Buffer Underflow";
105 break;
106 default:
107 error_type_str = "Unknown";
108 break;
109 }
110
111 uintptr_t diff;
112 const char* location_str;
113
114 if (untagged_fault_addr_ < report->allocation_address) {
115 // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
116 location_str = "left of";
117 diff = report->allocation_address - untagged_fault_addr_;
118 } else if (untagged_fault_addr_ - report->allocation_address < report->allocation_size) {
119 // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
120 location_str = "into";
121 diff = untagged_fault_addr_ - report->allocation_address;
122 } else {
123 // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef.
124 location_str = "right of";
125 diff = untagged_fault_addr_ - report->allocation_address - report->allocation_size;
126 }
127
128 // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
129 const char* byte_suffix = "s";
130 if (diff == 1) {
131 byte_suffix = "";
132 }
133 _LOG(log, logtype::HEADER,
134 "\nCause: [MTE]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
135 error_type_str, diff, byte_suffix, location_str, report->allocation_size,
136 report->allocation_address);
137
138 if (report->allocation_trace[0]) {
139 _LOG(log, logtype::BACKTRACE, "\nallocated by thread %u:\n", report->allocation_tid);
140 unwinder->SetDisplayBuildID(true);
141 for (size_t i = 0; i < 64 && report->allocation_trace[i]; ++i) {
142 unwindstack::FrameData frame_data =
143 unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);
144 frame_data.num = i;
145 _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
146 }
147 }
148
149 if (report->deallocation_trace[0]) {
150 _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %u:\n", report->deallocation_tid);
151 unwinder->SetDisplayBuildID(true);
152 for (size_t i = 0; i < 64 && report->deallocation_trace[i]; ++i) {
153 unwindstack::FrameData frame_data =
154 unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
155 frame_data.num = i;
156 _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
157 }
158 }
159 }
160