1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
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
16 #include "asan_collector.h"
17
18 #include <fcntl.h>
19 #include <map>
20 #include <regex>
21 #include <sys/time.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include "logger.h"
25
26 #include "reporter.h"
27 #include "zip_helper.h"
28 #include "faultlog_util.h"
29
30 namespace OHOS {
31 namespace HiviewDFX {
32 DEFINE_LOG_TAG("Faultlogger");
33 const char CLANGLIB[] = "libclang_rt";
34 const std::string SKIP_SPECIAL_PROCESS = "sa_main appspawn";
35 const std::string SKIP_SPECIAL_LIB = "libclang_rt libc libutils libcutils";
36
37 const std::string ASAN_RECORD_REGEX =
38 std::string("==([0-9a-zA-Z_.]{1,})==(\\d+)==ERROR: (AddressSanitizer|LeakSanitizer): "
39 "(\\S+) (.|\\r|\\n)*?SUMMARY: AddressSanitizer:(.|\\r|\\n)*");
40
41 enum FieldOfAsanRecord {
42 DESCRIPTION_FIELD,
43 PROCNAME_FIELD,
44 PID_FIELD,
45 ORISANITIZERTYPE_FIELD,
46 ERRTYPE_FIELD,
47 MAX_FIELD
48 };
49
50 const char XDIGIT_REGEX[] = "0[xX][0-9a-fA-F]+";
51
52 static std::map<std::string, std::string> g_faultTypeInShort = {
53 {"heap-use-after-free", "uaf"}, {"heap-buffer-overflow", "of"},
54 {"stack-buffer-underflow", "uf"}, {"initialization-order-fiasco", "iof"},
55 {"stack-buffer-overflow", "of"}, {"stack-use-after-return", "uar"},
56 {"stack-use-after-scope", "uas"}, {"global-buffer-overflow", "of"},
57 {"use-after-poison", "uap"}, {"dynamic-stack-buffer-overflow", "of"},
58 {"SEGV", "SEGV"},
59 };
60
AsanCollector(std::unordered_map<std::string,std::string> & stkmap)61 AsanCollector::AsanCollector(std::unordered_map<std::string, std::string> &stkmap) : SanitizerdCollector(stkmap)
62 {
63 curr_.type = ASAN_LOG_RPT;
64 curr_.pid = -1;
65 curr_.uid = -1;
66 curr_.procName = "0";
67 curr_.appVersion = "0";
68 curr_.happenTime = 0;
69 }
70
~AsanCollector()71 AsanCollector::~AsanCollector()
72 {
73 }
74
ProcessStackTrace(const std::string & asanDump,bool printDiagnostics,unsigned * hash)75 void AsanCollector::ProcessStackTrace(
76 const std::string& asanDump,
77 bool printDiagnostics,
78 unsigned *hash)
79 {
80 // std::string delimiter = "[\\r\\n]+";
81 // include blankline
82 std::string delimiter = "(?:\\r\\n|\\r|\\n)";
83 auto str_lines = OHOS::HiviewDFX::SplitString(asanDump, delimiter);
84
85 // Match lines such as the following and grab out "function_name".
86 // The ? may or may not be present.
87 //
88 // : #0 0x7215208f97 (/vendor/lib64/hwcam/hwcam.hi3660.m.DUKE.so+0x289f97)
89
90 std::string stackEntry =
91 " #(\\d+) " + std::string(XDIGIT_REGEX) + // Matches "0x7215208f97"
92 "([\\s\\?(]+)" + // Matches " ? ("
93 "([^\\+ )]+\\+" + std::string(XDIGIT_REGEX) + ")"; // Matches until delimiter reached
94 static const std::regex STACK_ENTRY_RE(stackEntry);
95 std::match_results<std::string::iterator> stack_entry_captured;
96
97 std::string hashable;
98 std::string previous_hashable;
99
100 *hash = 0;
101 // Stacktrace end until "is located" is reached.
102 std::string stackEnd = " is located";
103 static const std::regex STACK_END_RE(stackEnd);
104 std::match_results<std::string::iterator> stack_end_captured;
105
106 for (auto str_line: str_lines) {
107 std::string frm_no;
108 std::string xdigit;
109 std::string function_name;
110 if (std::regex_search(str_line.begin(), str_line.end(), stack_entry_captured, STACK_ENTRY_RE)) {
111 frm_no = stack_entry_captured[1].str();
112 xdigit = stack_entry_captured[2].str();
113 function_name = stack_entry_captured[3].str();
114
115 if (frm_no == "0") {
116 if (printDiagnostics) {
117 HIVIEW_LOGI("Stack trace starting.%{public}s",
118 hashable.empty() ? "" : " Saving prior trace.");
119 }
120 previous_hashable = hashable;
121 hashable.clear();
122 curr_.func.clear();
123 }
124
125 if (!hashable.empty())
126 hashable.append("|");
127 hashable.append(function_name);
128
129 if (curr_.func.empty()) {
130 if (!function_name.empty()) {
131 // skip special libclang_rt lib
132 if (function_name.find(CLANGLIB) == std::string::npos) {
133 curr_.func = function_name;
134 }
135 }
136 }
137 } else if (std::regex_search(str_line.begin(), str_line.end(), stack_end_captured, STACK_END_RE)) {
138 if (printDiagnostics) {
139 SANITIZERD_LOGI("end of stack matched reline:(%{public}s)\n", str_line.c_str());
140 }
141 break;
142 }
143 }
144
145 // If the hashable is empty (meaning all frames are uncertain,
146 // for whatever reason) also use the previous frame, as it cannot be any
147 // worse.
148 if (hashable.empty()) {
149 hashable = previous_hashable;
150 }
151
152 *hash = OHOS::HiviewDFX::HashString(hashable);
153 }
154
155 // Compute the stacktrace signature
ComputeStackSignature(const std::string & asanDump,std::string & asanSignature,bool printDiagnostics)156 bool AsanCollector::ComputeStackSignature(const std::string& asanDump, std::string& asanSignature,
157 bool printDiagnostics)
158 {
159 unsigned stackHash = 0;
160 std::string human_string;
161
162 ProcessStackTrace(asanDump,
163 printDiagnostics,
164 &stackHash);
165
166 if (stackHash == 0) {
167 if (printDiagnostics) {
168 HIVIEW_LOGI("Maple Found not a stack, failing.");
169 }
170 return false;
171 }
172
173 // Format to the hex string
174 asanSignature = "%08X" + std::to_string(stackHash);
175 return true;
176 }
177
IsDuplicate(const std::string & hash)178 bool AsanCollector::IsDuplicate(const std::string& hash)
179 {
180 auto back_iter = stacks_.find(hash);
181 return (back_iter != stacks_.end());
182 }
183
UpdateCollectedData(const std::string & hash,const std::string & rfile)184 int AsanCollector::UpdateCollectedData(const std::string& hash, const std::string& rfile)
185 {
186 auto jstack = std::pair<std::string, std::string> {hash, rfile};
187 std::lock_guard<std::mutex> lockGuard(mutex_);
188 stacks_.insert(jstack);
189
190 HIVIEW_LOGI("Updating collected data ...");
191 // Do upload when data ready
192 OHOS::HiviewDFX::Upload(&curr_);
193 return 0;
194 }
195
CalibrateErrTypeProcName()196 void AsanCollector::CalibrateErrTypeProcName()
197 {
198 std::map<std::string, std::string>::iterator fault_type_iter;
199
200 fault_type_iter = g_faultTypeInShort.find(curr_.errType);
201 if (fault_type_iter != g_faultTypeInShort.end()) {
202 curr_.errTypeInShort = fault_type_iter->second;
203 } else {
204 curr_.errTypeInShort = curr_.errType;
205 }
206
207 if (curr_.uid >= MIN_APP_USERID) {
208 curr_.procName = GetApplicationNameById(curr_.uid);
209 curr_.appVersion = GetApplicationVersion(curr_.uid, curr_.procName);
210 }
211 }
212
SetHappenTime()213 void AsanCollector::SetHappenTime()
214 {
215 time_t timeNow = time(nullptr);
216 uint64_t timeTmp = timeNow;
217 std::string timeStr = GetFormatedTime(timeTmp);
218 curr_.happenTime = std::stoll(timeStr);
219 }
220
ReadRecordToString(std::string & fullFile,const std::string & fileName)221 bool AsanCollector::ReadRecordToString(std::string& fullFile, const std::string& fileName)
222 {
223 // A record is a log dump. It has an associated size of "record_size".
224 std::string record;
225 std::smatch captured;
226
227 record.clear();
228 if (!OHOS::HiviewDFX::ReadFileToString(fullFile, record)) {
229 HIVIEW_LOGI("Unable to open %{public}s", fullFile.c_str());
230 return false;
231 }
232
233 int hitcount = 0;
234 struct stat st;
235 if (stat(fullFile.c_str(), &st) == -1) {
236 HIVIEW_LOGI("stat %{public}s error", fullFile.c_str());
237 } else {
238 curr_.uid = static_cast<int32_t>(st.st_uid);
239 }
240
241 static const std::regex RECORD_RE(ASAN_RECORD_REGEX);
242 while (std::regex_search(record, captured, RECORD_RE)) {
243 std::string signature;
244
245 curr_.description = captured[DESCRIPTION_FIELD].str();
246 curr_.procName = captured[PROCNAME_FIELD].str();
247 curr_.pid = stoi(captured[PID_FIELD].str());
248 if (captured[ORISANITIZERTYPE_FIELD].str().compare(
249 std::string(SANITIZERD_TYPE_STR[LSAN_LOG_RPT][ORISANITIZERTYPE])) == 0) {
250 curr_.errType = captured[ORISANITIZERTYPE_FIELD].str();
251 curr_.type = LSAN_LOG_RPT;
252 } else {
253 curr_.errType = captured[ERRTYPE_FIELD].str();
254 }
255
256 CalibrateErrTypeProcName();
257
258 // if record_found
259 if (ComputeStackSignature(captured[DESCRIPTION_FIELD].str(), signature, false)) {
260 // Do filtering here before upload the data
261 // for capture from hilog whith multiple asan fault log
262 SetHappenTime();
263 curr_.logName = fileName;
264 curr_.hash = signature;
265 }
266
267 if (hitcount > 0) {
268 sleep(1);
269 }
270
271 // Sync hash map in memory
272 UpdateCollectedData(signature, fullFile);
273 hitcount++;
274 record = captured.suffix().str();
275 }
276
277 return true;
278 }
279
Collect(const std::string & filepath)280 void AsanCollector::Collect(const std::string& filepath)
281 {
282 // Compose the full path string
283 std::string strAsanLogPath(ASAN_LOG_PATH);
284 std::string fullPath = strAsanLogPath + "/" + filepath;
285 ReadRecordToString(fullPath, filepath);
286 }
287 } // namespace HiviewDFX
288 } // namespace OHOS
289