• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved.
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 #include "gwpasan_collector.h"
16 
17 #include <cerrno>
18 #include <cstdlib>
19 #include <cstring>
20 #include <ctime>
21 #include <fcntl.h>
22 #include <mutex>
23 #include <securec.h>
24 #include <sys/time.h>
25 #include <time_util.h>
26 #include <unistd.h>
27 #include <parameters.h>
28 
29 #include "bundle_mgr_client.h"
30 #include "faultlog_util.h"
31 #include "file_util.h"
32 #include "hisysevent.h"
33 #include "hisysevent_easy.h"
34 #include "parameter_ex.h"
35 #include "faultlog_formatter.h"
36 #include "faultloggerd_client.h"
37 #include "tbox.h"
38 #include "common_defines.h"
39 
40 #undef LOG_DOMAIN
41 #define LOG_DOMAIN 0xD002D12
42 
43 #undef LOG_TAG
44 #define LOG_TAG "Sanitizer"
45 
46 namespace {
47 constexpr unsigned MAX_HISYSEVENT_SIZE = 1000;
48 constexpr unsigned BUF_SIZE = 128;
49 constexpr unsigned HWASAN_ERRTYPE_FIELD = 2;
50 constexpr unsigned ASAN_ERRTYPE_FIELD = 2;
51 constexpr mode_t DEFAULT_SANITIZER_LOG_MODE = 0644;
52 constexpr char ADDR_SANITIZER_EVENT[] = "ADDR_SANITIZER";
53 static std::stringstream g_asanlog;
54 }
55 
WriteSanitizerLog(char * buf,size_t sz,char * path)56 void WriteSanitizerLog(char* buf, size_t sz, char* path)
57 {
58     if (buf == nullptr || sz == 0) {
59         return;
60     }
61     static std::mutex sMutex;
62     std::lock_guard<std::mutex> lock(sMutex);
63     // append to buffer
64     for (size_t i = 0; i < sz; i++) {
65         g_asanlog << buf[i];
66     }
67     char *gwpOutput = strstr(buf, "End GWP-ASan report");
68     char *tsanOutput = strstr(buf, "End Tsan report");
69     char *cfiOutput = strstr(buf, "End CFI report");
70     char *ubsanOutput = strstr(buf, "End Ubsan report");
71     char *hwasanOutput = strstr(buf, "End Hwasan report");
72     char *asanOutput = strstr(buf, "End Asan report");
73     if (gwpOutput) {
74         std::string gwpasanlog = g_asanlog.str();
75         ReadGwpAsanRecord(gwpasanlog, "GWP-ASAN", path);
76         // clear buffer
77         g_asanlog.str("");
78     } else if (tsanOutput) {
79         std::string tsanlog = g_asanlog.str();
80         ReadGwpAsanRecord(tsanlog, "TSAN", path);
81         // clear buffer
82         g_asanlog.str("");
83     } else if (cfiOutput || ubsanOutput) {
84         std::string ubsanlog = g_asanlog.str();
85         ReadGwpAsanRecord(ubsanlog, "UBSAN", path);
86         // clear buffer
87         g_asanlog.str("");
88     } else if (hwasanOutput) {
89         std::string hwasanlog = g_asanlog.str();
90         ReadGwpAsanRecord(hwasanlog, "HWASAN", path);
91         // clear buffer
92         g_asanlog.str("");
93     } else if (asanOutput) {
94         std::string asanlog = g_asanlog.str();
95         ReadGwpAsanRecord(asanlog, "ASAN", path);
96         // clear buffer
97         g_asanlog.str("");
98     }
99 }
100 
ReadGwpAsanRecord(const std::string & gwpAsanBuffer,const std::string & faultType,char * logPath)101 void ReadGwpAsanRecord(const std::string& gwpAsanBuffer, const std::string& faultType, char* logPath)
102 {
103     const std::unordered_set<std::string> setAsanOptionTypeList = {"ASAN", "HWASAN"};
104     GwpAsanCurrInfo currInfo;
105     currInfo.description = gwpAsanBuffer;
106     if (logPath == nullptr || strlen(logPath) == 0 ||
107         setAsanOptionTypeList.find(faultType) == setAsanOptionTypeList.end()) {
108         currInfo.logPath = "faultlogger";
109     } else {
110         currInfo.logPath = std::string(logPath);
111     }
112     currInfo.pid = getprocpid();
113     currInfo.uid = getuid();
114     currInfo.faultType = faultType;
115     currInfo.errType = GetErrorTypeFromBuffer(gwpAsanBuffer, faultType);
116     currInfo.moduleName = GetNameByPid(currInfo.pid);
117     time_t timeNow = time(nullptr);
118     uint64_t timeTmp = timeNow;
119     constexpr int decimalBase = 10;
120     std::string timeStr = OHOS::HiviewDFX::GetFormatedTime(timeTmp);
121     currInfo.happenTime = static_cast<uint64_t>(strtoull(timeStr.c_str(), nullptr, decimalBase));
122     currInfo.topStack = GetTopStackWithoutCommonLib(currInfo.description);
123     currInfo.hash = OHOS::HiviewDFX::Tbox::CalcFingerPrint(
124         currInfo.topStack + currInfo.errType + currInfo.moduleName, 0, OHOS::HiviewDFX::FingerPrintMode::FP_BUFFER);
125     currInfo.telemetryId = OHOS::system::GetParameter("persist.hiviewdfx.priv.diagnosis.time.taskId", "");
126     // Do upload when data ready
127     bool isSendHisysevent = false;
128     WriteCollectedData(currInfo, isSendHisysevent);
129     if (isSendHisysevent) {
130         SendSanitizerHisysevent(currInfo);
131     }
132 }
133 
SendSanitizerHisysevent(const GwpAsanCurrInfo & currInfo)134 void SendSanitizerHisysevent(const GwpAsanCurrInfo& currInfo)
135 {
136     std::stringstream ssPrefix;
137     ssPrefix << "FAULT_TYPE:" << currInfo.faultType <<
138         ";MODULE:" << currInfo.moduleName <<
139         ";REASON:" << currInfo.errType <<
140         ";PID:" << currInfo.pid <<
141         ";UID:" << currInfo.uid <<
142         ";HAPPEN_TIME:" << currInfo.happenTime <<
143         ";FINGERPRINT:" << currInfo.hash <<
144         ";FIRST_FRAME:" << currInfo.errType <<
145         ";SECOND_FRAME:" << currInfo.topStack;
146     if (!currInfo.telemetryId.empty()) {
147         ssPrefix << ";TELEMETRY_ID:" << currInfo.telemetryId;
148     }
149 
150     std::string prefixStr = ssPrefix.str();
151     size_t maxSummaryLen = 0;
152     const std::string summaryPrefix = ";SUMMARY:";
153     if (prefixStr.size() + summaryPrefix.size() < MAX_HISYSEVENT_SIZE) {
154         maxSummaryLen = MAX_HISYSEVENT_SIZE - prefixStr.size() - summaryPrefix.size();
155     }
156     std::string summary = currInfo.description.substr(0, maxSummaryLen);
157 
158     std::stringstream ssParams;
159     ssParams << prefixStr << summaryPrefix << summary;
160     std::string params = ssParams.str();
161     HiSysEventEasyWrite(
162         OHOS::HiviewDFX::HiSysEvent::Domain::RELIABILITY,
163         ADDR_SANITIZER_EVENT,
164         HiSysEventEasyType::EASY_EVENT_TYPE_FAULT,
165         params.c_str()
166     );
167 }
168 
GetErrorTypeFromBuffer(const std::string & buffer,const std::string & faultType)169 std::string GetErrorTypeFromBuffer(const std::string& buffer, const std::string& faultType)
170 {
171     const std::regex asanRegex("SUMMARY: (AddressSanitizer|LeakSanitizer): (\\S+)");
172     const std::regex hwasanRegex("(Potential Cause|Cause): ([\\w -]+)");
173 
174     std::smatch match;
175     if (std::regex_search(buffer, match, asanRegex)) {
176         return match[ASAN_ERRTYPE_FIELD].str();
177     }
178     if (std::regex_search(buffer, match, hwasanRegex)) {
179         return match[HWASAN_ERRTYPE_FIELD].str();
180     }
181     return faultType;
182 }
183 
WriteCollectedData(const GwpAsanCurrInfo & currInfo,bool & isSendHisysevent)184 void WriteCollectedData(const GwpAsanCurrInfo& currInfo, bool& isSendHisysevent)
185 {
186     if (currInfo.logPath != "faultlogger" && WriteToSandbox(currInfo)) {
187         return;
188     }
189 
190     isSendHisysevent = true;
191     WriteToFaultLogger(currInfo);
192 }
193 
WriteToFaultLogger(const GwpAsanCurrInfo & currInfo)194 void WriteToFaultLogger(const GwpAsanCurrInfo& currInfo)
195 {
196     struct FaultLoggerdRequest request;
197     (void)memset_s(&request, sizeof(request), 0, sizeof(request));
198     request.type = FaultLoggerType::ADDR_SANITIZER;
199     request.pid = currInfo.pid;
200     request.time = currInfo.happenTime;
201     int fd = RequestFileDescriptorEx(&request);
202     if (fd < 0) {
203         return;
204     }
205 
206     OHOS::HiviewDFX::FileUtil::SaveStringToFd(fd, currInfo.description);
207     close(fd);
208 }
209 
WriteToSandbox(const GwpAsanCurrInfo & currInfo)210 bool WriteToSandbox(const GwpAsanCurrInfo& currInfo)
211 {
212     auto pos = currInfo.logPath.find_last_of('/');
213     if (pos == std::string::npos || pos == currInfo.logPath.length() - 1) {
214         return false;
215     }
216 
217     std::string logDir = currInfo.logPath.substr(0, pos);
218     std::string fileName = currInfo.logPath.substr(pos + 1);
219     std::string realPath;
220     if (!OHOS::HiviewDFX::FileUtil::PathToRealPath(logDir, realPath)) {
221         return false;
222     }
223 
224     const std::regex sandboxRegex("^/data/storage/el[0-9]+(?:/|$)");
225     if (!std::regex_search(realPath, sandboxRegex)) {
226         return false;
227     }
228 
229     std::string logFilePath = realPath + "/" + fileName + "." +
230         std::to_string(currInfo.pid) + "." +
231         std::to_string(currInfo.happenTime);
232     int fd = open(logFilePath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, DEFAULT_SANITIZER_LOG_MODE);
233     if (fd < 0) {
234         return false;
235     }
236 
237     std::ostringstream content;
238     content << "Generated by HiviewDFX @OpenHarmony\n"
239             << "===============================================================\n"
240             << "Device info:"
241             << OHOS::HiviewDFX::Parameter::GetString("const.product.name", "Unknown") << "\n"
242             << "Build info:"
243             << OHOS::HiviewDFX::Parameter::GetString("const.product.software.version", "Unknown") << "\n"
244             << "Timestamp:" << currInfo.happenTime << "\n"
245             << "Module name:" << currInfo.moduleName << "\n"
246             << "Pid:" << std::to_string(currInfo.pid) << "\n"
247             << "Uid:" << std::to_string(currInfo.uid) << "\n"
248             << "Reason:" << currInfo.errType << "\n"
249             << currInfo.description;
250     OHOS::HiviewDFX::FileUtil::SaveStringToFd(fd, content.str());
251     close(fd);
252     return true;
253 }
254 
IsIgnoreStack(const std::string & stack)255 bool IsIgnoreStack(const std::string& stack)
256 {
257     const std::unordered_set<std::string> ignoreList = {
258         "libclang_rt.hwasan.so",
259         "libclang_rt.asan.so",
260         "ld-musl-aarch64.so",
261         "ld-musl-aarch64-asan.so"
262     };
263     for (const auto& str : ignoreList) {
264         if (stack.find(str, 0) != std::string::npos) {
265             return true;
266         }
267     }
268     return false;
269 }
270 
GetTopStackWithoutCommonLib(const std::string & description)271 std::string GetTopStackWithoutCommonLib(const std::string& description)
272 {
273     std::string topstack;
274     std::string record = description;
275     std::smatch stackCaptured;
276     std::string stackRecord =
277         std::string("#[\\d+] ") +
278         "0[xX][0-9a-fA-F]+" +
279         "[\\s\\?(]+" +
280         "[^\\+ ]+/([a-zA-Z0-9_.-]+)(\\.z)?(\\.so)?\\+" +
281         "0[xX][0-9a-fA-F]+";
282     static const std::regex STACK_RE(stackRecord);
283 
284     while (std::regex_search(record, stackCaptured, STACK_RE)) {
285         std::string current = stackCaptured[1].str();
286         if (!IsIgnoreStack(current)) {
287             return current;
288         }
289         topstack = current;
290         record = stackCaptured.suffix().str();
291     }
292     return topstack;
293 }
294 
GetNameByPid(int32_t pid)295 std::string GetNameByPid(int32_t pid)
296 {
297     char path[BUF_SIZE] = { 0 };
298     int err = snprintf_s(path, sizeof(path), sizeof(path) - 1, "/proc/%d/cmdline", pid);
299     if (err <= 0) {
300         return "";
301     }
302     char cmdline[BUF_SIZE] = { 0 };
303     size_t i = 0;
304     FILE *fp = fopen(path, "r");
305     if (fp == nullptr) {
306         return "";
307     }
308     while (i < (BUF_SIZE - 1)) {
309         char c = static_cast<char>(fgetc(fp));
310         // 0. don't need args of cmdline
311         // 1. ignore unvisible character
312         if (!isgraph(c)) {
313             break;
314         }
315         cmdline[i] = c;
316         i++;
317     }
318     (void)fclose(fp);
319     return cmdline;
320 }
321