1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. 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
16 #include "runtime_stack_range.h"
17
18 #include <csignal>
19 #include <cstring>
20 #include <map>
21 #include <sys/types.h>
22 #include "get_thread_id.h"
23 #include "utilities.h"
24
25 namespace {
26 constexpr int BASE_MIN = 2;
27 constexpr int BASE_CENTRE = 10;
28 constexpr int BASE_MAX = 16;
29
30 struct StackScope {
31 const char* start;
32 const char* end;
33 };
34 struct StandardLibrary {
StandardLibrary__anon254::StandardLibrary35 StandardLibrary(uint64_t begin, uint64_t end, const std::string& name)
36 : soBegin_(begin), soEnd_(end), name_(name)
37 {}
38 uint64_t soBegin_;
39 uint64_t soEnd_;
40 std::string name_;
41 };
42
43 static StackScope g_mainStack;
44 static std::map<std::string, StandardLibrary> g_stdLib;
45 } // namespace
46
GetThreadRuntimeStackRange(const char** start, const char** end)47 static void GetThreadRuntimeStackRange(const char** start, const char** end)
48 {
49 *start = nullptr;
50 *end = nullptr;
51 pthread_t tid = pthread_self();
52 pthread_attr_t attr;
53 if (pthread_getattr_np(tid, &attr) == 0) {
54 size_t stackSize;
55 if (pthread_attr_getstack(&attr, reinterpret_cast<void**>(const_cast<char**>(start)), &stackSize) == 0) {
56 *end = *start + stackSize;
57 }
58 pthread_attr_destroy(&attr);
59 }
60 }
61
CvtStrToInt(const char* str, int base)62 static long long CvtStrToInt(const char* str, int base)
63 {
64 long long result = 0;
65 if (base >= BASE_MIN && base <= BASE_CENTRE) {
66 while (*str) {
67 if (*str >= '0' && *str <= '0' + base - 1) {
68 result = result * base + static_cast<long long>((*str) - '0');
69 } else {
70 break;
71 }
72 ++str;
73 }
74 } else if (base > BASE_CENTRE && base <= BASE_MAX) {
75 while (*str) {
76 if (*str >= '0' && *str <= '0' + base - 1) {
77 result = result * base + static_cast<long long>(*str) - '0';
78 } else if (*str >= 'a' && *str <= 'a' + base - 0x0a - 1) {
79 result = result * base + static_cast<long long>(*str) - 'a' + 0x0a;
80 } else if (*str >= 'A' && *str <= 'A' + base - 0x0a - 1) {
81 result = result * base + static_cast<long long>(*str) - 'A' + 0x0a;
82 } else {
83 break;
84 }
85 ++str;
86 }
87 } else {
88 result = 0;
89 }
90 return result;
91 }
92
IsEmptyString(const std::string& str)93 static int IsEmptyString(const std::string& str)
94 {
95 size_t idx = 0;
96 size_t size = str.size();
97 while (idx < size) {
98 if (!isspace(static_cast<unsigned char>(str[idx])) && str[idx] != 0) {
99 return 0;
100 }
101 ++idx;
102 }
103 return 1;
104 }
105
GetAnUnlimitedLine(FILE* fp, std::string& buf)106 static void GetAnUnlimitedLine(FILE* fp, std::string& buf)
107 {
108 if (!fp) {
109 buf.resize(0);
110 return;
111 }
112 char* retLine = nullptr;
113 if (buf.size() == 0) {
114 buf.resize(INIT_LINE_SIZE);
115 }
116
117 int offset = 0;
118 int length = 0;
119 do {
120 if (offset + length >= static_cast<int>(buf.size())) {
121 buf.resize(buf.size() + INC_LINE_SIZE);
122 }
123 retLine = fgets(&buf[0] + offset, buf.size() - offset, fp);
124 if (retLine == nullptr) {
125 break;
126 }
127 length = static_cast<int>(strlen(&buf[0] + offset));
128 if (offset + length - 1 >= 0 && buf[offset + length - 1] == '\n') {
129 break;
130 }
131 offset += length;
132 } while (1);
133 }
134
IsLegalSoName(const std::string &fileName)135 static bool IsLegalSoName(const std::string &fileName)
136 {
137 if (fileName.front() == '[' or fileName.back() == ']' or
138 std::strncmp(fileName.c_str(), "/dev/", sizeof("/dev/")) == 0 or
139 std::strncmp(fileName.c_str(), "/memfd:", sizeof("/memfd:")) == 0 or
140 std::strncmp(fileName.c_str(), "//anon", sizeof("//anon")) == 0) {
141 return false;
142 }
143 return true;
144 }
145
GetStandardLibraryRange(std::string& line)146 static void GetStandardLibraryRange(std::string& line)
147 {
148 line.resize(strlen(line.c_str()));
149 std::vector<std::string> mapTokens = OHOS::Developtools::NativeDaemon::StringSplit(line, " ");
150 const std::string& soRange = mapTokens.front();
151 std::string& soName = mapTokens.back();
152 if (IsLegalSoName(soName)) {
153 std::string::size_type concatPos = soRange.find('-');
154 uint64_t soStart = static_cast<uint64_t>(strtoll(soRange.c_str(), nullptr, BASE_MAX));
155 uint64_t soEnd = static_cast<uint64_t>(strtoll(soRange.c_str() + concatPos + 1, nullptr, BASE_MAX));
156 auto [iter, isExit] = g_stdLib.try_emplace(soName, StandardLibrary(soStart, soEnd, soName));
157 if (!isExit) {
158 if (iter->second.soBegin_ > soStart) {
159 iter->second.soBegin_ = soStart;
160 } else if (iter->second.soEnd_ < soEnd) {
161 iter->second.soEnd_ = soEnd;
162 }
163 }
164 }
165 }
166
GetMainThreadRuntimeStackRange(std::vector<std::pair<uint64_t, uint64_t>>& filterStaLibRange)167 void GetMainThreadRuntimeStackRange(std::vector<std::pair<uint64_t, uint64_t>>& filterStaLibRange)
168 {
169 std::string line;
170 FILE* fp = fopen("/proc/self/maps", "re");
171 if (fp == nullptr) {
172 return;
173 }
174 while (!feof(fp)) {
175 line.clear();
176 GetAnUnlimitedLine(fp, line);
177 if (IsEmptyString(line)) {
178 continue;
179 }
180 if (line.find("[stack]") != std::string::npos) {
181 std::string::size_type concatPos = line.find('-');
182 if (concatPos != std::string::npos) {
183 g_mainStack.start = reinterpret_cast<char*>(CvtStrToInt(line.c_str(), BASE_MAX));
184 g_mainStack.end = reinterpret_cast<char*>(CvtStrToInt(line.c_str() + concatPos + 1, BASE_MAX));
185 }
186 } else if (line.find("ld-musl") != std::string::npos || line.find("libc++") != std::string::npos) {
187 GetStandardLibraryRange(line);
188 }
189 }
190 if (fclose(fp) != 0) {
191 printf("fclose failed.\n");
192 }
193 for (const auto& [soName, stdLibrary]: g_stdLib) {
194 filterStaLibRange.emplace_back(stdLibrary.soBegin_, stdLibrary.soEnd_);
195 }
196 }
197
IfContained(const char* start, const char* end, const char* ptr)198 static bool IfContained(const char* start, const char* end, const char* ptr)
199 {
200 bool ret = (ptr >= start && ptr < end);
201 return ret;
202 }
203
GetRuntimeSigalAltStackRange(char** start, char** end)204 static void GetRuntimeSigalAltStackRange(char** start, char** end)
205 {
206 *start = nullptr;
207 *end = nullptr;
208
209 stack_t altStack;
210
211 if (sigaltstack(nullptr, &altStack) != -1) {
212 if ((altStack.ss_flags & SS_ONSTACK) != 0) {
213 *start = reinterpret_cast<char*>(altStack.ss_sp);
214 *end = reinterpret_cast<char*>(altStack.ss_sp) + altStack.ss_size;
215 }
216 }
217 }
218
IfSubThread(pid_t pid, pid_t tid)219 static bool IfSubThread(pid_t pid, pid_t tid)
220 {
221 return pid != tid;
222 }
223
GetRuntimeStackEnd(const char* stackptr, const char** end, pid_t pid, pid_t tid)224 void GetRuntimeStackEnd(const char* stackptr, const char** end, pid_t pid, pid_t tid)
225 {
226 const char* start = nullptr;
227 *end = nullptr;
228 bool isSubThread = IfSubThread(pid, tid);
229 if (isSubThread) {
230 GetThreadRuntimeStackRange(&start, end);
231 } else {
232 start = g_mainStack.start;
233 *end = g_mainStack.end;
234 }
235 if (!IfContained(start, *end, stackptr)) {
236 char *sigStackStart = nullptr;
237 char *sigStackEnd = nullptr;
238 GetRuntimeSigalAltStackRange(&sigStackStart, &sigStackEnd);
239 if (IfContained(sigStackStart, sigStackEnd, stackptr)) {
240 *end = sigStackEnd;
241 } else if (!(!isSubThread && stackptr < *end)) {
242 *end = nullptr;
243 }
244 }
245 }
246