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 
28 #include "bundle_mgr_client.h"
29 #include "event_publish.h"
30 #include "faultlog_util.h"
31 #include "file_util.h"
32 #include "hisysevent.h"
33 #include "json/json.h"
34 #include "hilog/log.h"
35 #include "hiview_logger.h"
36 #include "parameter_ex.h"
37 
38 #undef LOG_DOMAIN
39 #define LOG_DOMAIN 0xD002D12
40 
41 #undef LOG_TAG
42 #define LOG_TAG "Sanitizer"
43 
44 namespace {
45 constexpr unsigned ASAN_LOG_SIZE = 350 * 1024;
46 constexpr unsigned BUF_SIZE = 128;
47 constexpr unsigned HWASAN_ERRTYPE_FIELD = 1;
48 constexpr unsigned ASAN_ERRTYPE_FIELD = 2;
49 static std::stringstream g_asanlog;
50 }
51 
WriteGwpAsanLog(char * buf,size_t sz)52 void WriteGwpAsanLog(char* buf, size_t sz)
53 {
54     if (buf == nullptr || sz == 0) {
55         return;
56     }
57     static std::mutex sMutex;
58     std::lock_guard<std::mutex> lock(sMutex);
59     // append to buffer
60     for (size_t i = 0; i < sz; i++) {
61         g_asanlog << buf[i];
62     }
63     char *gwpOutput = strstr(buf, "End GWP-ASan report");
64     char *tsanOutput = strstr(buf, "End Tsan report");
65     char *cfiOutput = strstr(buf, "End CFI report");
66     char *ubsanOutput = strstr(buf, "End Ubsan report");
67     char *hwasanOutput = strstr(buf, "End Hwasan report");
68     char *asanOutput = strstr(buf, "End Asan report");
69     if (gwpOutput) {
70         std::string gwpasanlog = g_asanlog.str();
71         std::string errType = "GWP-ASAN";
72         ReadGwpAsanRecord(gwpasanlog, errType);
73         // clear buffer
74         g_asanlog.str("");
75     } else if (tsanOutput) {
76         std::string tsanlog = g_asanlog.str();
77         std::string errType = "TSAN";
78         ReadGwpAsanRecord(tsanlog, errType);
79         // clear buffer
80         g_asanlog.str("");
81     } else if (cfiOutput || ubsanOutput) {
82         std::string ubsanlog = g_asanlog.str();
83         std::string errType = "UBSAN";
84         ReadGwpAsanRecord(ubsanlog, errType);
85         // clear buffer
86         g_asanlog.str("");
87     } else if (hwasanOutput) {
88         std::string hwasanlog = g_asanlog.str();
89         std::string errType = "HWASAN_" + GetErrorTypeFromHwAsanLog(hwasanlog);
90         ReadGwpAsanRecord(hwasanlog, errType);
91         // clear buffer
92         g_asanlog.str("");
93     } else if (asanOutput) {
94         std::string asanlog = g_asanlog.str();
95         std::string errType = "ASAN_" + GetErrorTypeFromAsanLog(asanlog);
96         ReadGwpAsanRecord(asanlog, errType);
97         // clear buffer
98         g_asanlog.str("");
99     }
100 }
101 
GetErrorTypeFromHwAsanLog(const std::string & hwAsanBuffer)102 std::string GetErrorTypeFromHwAsanLog(const std::string& hwAsanBuffer)
103 {
104     constexpr const char* const hwAsanRecordRegex = "Cause: ([\\w -]+)";
105     static const std::regex hwAsanRecordRe(hwAsanRecordRegex);
106     return GetErrorTypeFromLog(hwAsanBuffer, hwAsanRecordRe, HWASAN_ERRTYPE_FIELD, "HWASAN");
107 }
108 
GetErrorTypeFromAsanLog(const std::string & asanBuffer)109 std::string GetErrorTypeFromAsanLog(const std::string& asanBuffer)
110 {
111     constexpr const char* const asanRecordRegex =
112         "SUMMARY: (AddressSanitizer|LeakSanitizer): (\\S+)";
113     static const std::regex asanRecordRe(asanRecordRegex);
114     return GetErrorTypeFromLog(asanBuffer, asanRecordRe, ASAN_ERRTYPE_FIELD, "ASAN");
115 }
116 
GetErrorTypeFromLog(const std::string & logBuffer,const std::regex & recordRe,int errTypeField,const std::string & defaultType)117 std::string GetErrorTypeFromLog(const std::string& logBuffer, const std::regex& recordRe,
118     int errTypeField, const std::string& defaultType)
119 {
120     std::smatch captured;
121     if (!std::regex_search(logBuffer, captured, recordRe)) {
122         HILOG_INFO(LOG_CORE, "%{public}s Regex not match, set default type.", defaultType.c_str());
123         return defaultType;
124     }
125     std::string errType = captured[errTypeField].str();
126     HILOG_INFO(LOG_CORE, "%{public}s errType is %{public}s", defaultType.c_str(), errType.c_str());
127     return errType;
128 }
129 
ReadGwpAsanRecord(const std::string & gwpAsanBuffer,const std::string & errType)130 void ReadGwpAsanRecord(const std::string& gwpAsanBuffer, const std::string& errType)
131 {
132     GwpAsanCurrInfo currInfo;
133     currInfo.description = ((gwpAsanBuffer.size() > ASAN_LOG_SIZE) ? (gwpAsanBuffer.substr(0, ASAN_LOG_SIZE) +
134                                                                       "\nEnd Asan report") : gwpAsanBuffer);
135     currInfo.pid = getpid();
136     currInfo.uid = getuid();
137     currInfo.errType = errType;
138     currInfo.procName = GetNameByPid(currInfo.pid);
139     currInfo.appVersion = "";
140     time_t timeNow = time(nullptr);
141     uint64_t timeTmp = timeNow;
142     std::string timeStr = OHOS::HiviewDFX::GetFormatedTime(timeTmp);
143     currInfo.happenTime = std::stoll(timeStr);
144     std::string fullName = CalcCollectedLogName(currInfo);
145     currInfo.logPath = fullName;
146     HILOG_INFO(LOG_CORE, "ReportSanitizerAppEvent: uid:%{public}d, logPath:%{public}s.",
147         currInfo.uid, currInfo.logPath.c_str());
148 
149     // Do upload when data ready
150     HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::RELIABILITY, "ADDR_SANITIZER",
151                     OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
152                     "MODULE", currInfo.procName,
153                     "VERSION", currInfo.appVersion,
154                     "REASON", currInfo.errType,
155                     "PID", currInfo.pid,
156                     "UID", currInfo.uid,
157                     "SUMMARY", currInfo.description,
158                     "HAPPEN_TIME", currInfo.happenTime,
159                     "LOG_PATH", currInfo.logPath);
160 }
161 
CalcCollectedLogName(const GwpAsanCurrInfo & currInfo)162 std::string CalcCollectedLogName(const GwpAsanCurrInfo &currInfo)
163 {
164     std::string filePath = "/data/log/faultlog/faultlogger/";
165     std::string prefix = "";
166     if (currInfo.errType.compare("GWP-ASAN") == 0) {
167         prefix = "gwpasan";
168     } else if (currInfo.errType.compare("TSAN") == 0) {
169         prefix = "tsan";
170     } else if (currInfo.errType.compare("UBSAN") == 0) {
171         prefix = "ubsan";
172     } else if (currInfo.errType.find("HWASAN") != std::string::npos) {
173         prefix = "hwasan";
174     } else if (currInfo.errType.find("ASAN") != std::string::npos) {
175         prefix = "asan";
176     } else {
177         prefix = "sanitizer";
178     }
179     std::string name = currInfo.procName;
180     if (name.find("/") != std::string::npos) {
181         name = currInfo.procName.substr(currInfo.procName.find_last_of("/") + 1);
182     }
183 
184     std::string fileName = "";
185     fileName.append(prefix);
186     fileName.append("-");
187     fileName.append(name);
188     fileName.append("-");
189     fileName.append(std::to_string(currInfo.uid));
190     fileName.append("-");
191     fileName.append(std::to_string(currInfo.happenTime));
192 
193     std::string fullName = filePath + fileName;
194     return fullName;
195 }
196 
GetNameByPid(int32_t pid)197 std::string GetNameByPid(int32_t pid)
198 {
199     char path[BUF_SIZE] = { 0 };
200     int err = snprintf_s(path, sizeof(path), sizeof(path) - 1, "/proc/%d/cmdline", pid);
201     if (err <= 0) {
202         return "";
203     }
204     char cmdline[BUF_SIZE] = { 0 };
205     size_t i = 0;
206     FILE *fp = fopen(path, "r");
207     if (fp == nullptr) {
208         return "";
209     }
210     while (i < (BUF_SIZE - 1)) {
211         char c = static_cast<char>(fgetc(fp));
212         // 0. don't need args of cmdline
213         // 1. ignore unvisible character
214         if (!isgraph(c)) {
215             break;
216         }
217         cmdline[i] = c;
218         i++;
219     }
220     (void)fclose(fp);
221     return cmdline;
222 }
223