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