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