• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 stackEntryRe(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 stackEndRe(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, stackEntryRe)) {
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, stackEndRe)) {
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 
GetTopStackWithoutCommonLib(const std::string & description)196 std::string AsanCollector::GetTopStackWithoutCommonLib(const std::string& description)
197 {
198     std::string topstack;
199     std::string record = description;
200     std::smatch stackCaptured;
201     std::string stackRecord =
202     "  #[\\d+] " + std::string(XDIGIT_REGEX) +
203     "[\\s\\?(]+" +
204     "[^\\+ ]+/(\\w+)(.z)?.so\\+" + std::string(XDIGIT_REGEX);
205     static const std::regex stackRe(stackRecord);
206 
207     while (std::regex_search(record, stackCaptured, stackRe)) {
208         if (topstack.size() == 0) {
209             topstack = stackCaptured[1].str();
210         }
211         if (SKIP_SPECIAL_LIB.find(stackCaptured[1].str().c_str()) == std::string::npos) {
212             return stackCaptured[1].str();
213         }
214         record = stackCaptured.suffix().str();
215     }
216 
217     return topstack;
218 }
219 
CalibrateErrTypeProcName()220 void AsanCollector::CalibrateErrTypeProcName()
221 {
222     char procName[MAX_PROCESS_PATH];
223     std::map<std::string, std::string>::iterator fault_type_iter;
224 
225     fault_type_iter = g_faultTypeInShort.find(curr_.errType);
226     if (fault_type_iter != g_faultTypeInShort.end()) {
227         curr_.errTypeInShort = fault_type_iter->second;
228     } else {
229         curr_.errTypeInShort = curr_.errType;
230     }
231 
232     if (curr_.uid >= MIN_APP_USERID) {
233         curr_.procName = GetApplicationNameById(curr_.uid);
234     }
235 
236     if  (curr_.uid >= MIN_APP_USERID && !curr_.procName.empty() && IsModuleNameValid(curr_.procName)) {
237         curr_.procName = RegulateModuleNameIfNeed(curr_.procName);
238         HIVIEW_LOGI("Get procName %{public}s from uid %{public}d.", curr_.procName.c_str(), curr_.uid);
239         curr_.appVersion = GetApplicationVersion(curr_.uid, curr_.procName);
240         HIVIEW_LOGI("Version is %{public}s.", curr_.appVersion.c_str());
241     } else if (OHOS::HiviewDFX::GetNameByPid(static_cast<pid_t>(curr_.pid), procName) == true) {
242         curr_.procName = std::string(procName);
243     } else if (SKIP_SPECIAL_PROCESS.find(curr_.procName.c_str()) != std::string::npos) {
244         // get top stack
245         curr_.procName = GetTopStackWithoutCommonLib(curr_.description);
246     }
247 }
248 
SetHappenTime()249 void AsanCollector::SetHappenTime()
250 {
251     time_t timeNow = time(nullptr);
252     uint64_t timeTmp = timeNow;
253     std::string timeStr = GetFormatedTime(timeTmp);
254     curr_.happenTime = std::stoll(timeStr);
255 }
256 
ReadRecordToString(std::string & fullFile,const std::string & fileName)257 bool AsanCollector::ReadRecordToString(std::string& fullFile, const std::string& fileName)
258 {
259     // A record is a log dump. It has an associated size of "record_size".
260     std::string record;
261     std::smatch captured;
262 
263     record.clear();
264     if (!OHOS::HiviewDFX::ReadFileToString(fullFile, record)) {
265         HIVIEW_LOGI("Unable to open %{public}s", fullFile.c_str());
266         return false;
267     }
268 
269     int hitcount = 0;
270     struct stat st;
271     if (stat(fullFile.c_str(), &st) == -1) {
272         HIVIEW_LOGI("stat %{public}s error", fullFile.c_str());
273     } else {
274         curr_.uid = static_cast<int32_t>(st.st_uid);
275     }
276 
277     static const std::regex recordRe(ASAN_RECORD_REGEX);
278     while (std::regex_search(record, captured, recordRe)) {
279         std::string signature;
280 
281         curr_.description = captured[DESCRIPTION_FIELD].str();
282         curr_.procName = captured[PROCNAME_FIELD].str();
283         curr_.pid = stoi(captured[PID_FIELD].str());
284         if (captured[ORISANITIZERTYPE_FIELD].str().compare(
285             std::string(SANITIZERD_TYPE_STR[LSAN_LOG_RPT][ORISANITIZERTYPE])) == 0) {
286             curr_.errType = captured[ORISANITIZERTYPE_FIELD].str();
287             curr_.type = LSAN_LOG_RPT;
288         } else {
289             curr_.errType = captured[ERRTYPE_FIELD].str();
290         }
291 
292         CalibrateErrTypeProcName();
293 
294         // if record_found
295         if (ComputeStackSignature(captured[DESCRIPTION_FIELD].str(), signature, false)) {
296             SetHappenTime();
297             curr_.logName = fileName;
298             curr_.hash = signature;
299         }
300 
301         if (hitcount > 0) {
302             sleep(1);
303         }
304 
305         // Sync hash map in memory
306         UpdateCollectedData(signature, fullFile);
307         hitcount++;
308         record = captured.suffix().str();
309     }
310 
311     return true;
312 }
313 
Collect(const std::string & filepath)314 void AsanCollector::Collect(const std::string& filepath)
315 {
316     // Compose the full path string
317     std::string strAsanLogPath(ASAN_LOG_PATH);
318     std::string fullPath = strAsanLogPath + "/" + filepath;
319     ReadRecordToString(fullPath, filepath);
320 }
321 } // namespace HiviewDFX
322 } // namespace OHOS
323