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
25namespace {
26constexpr int BASE_MIN = 2;
27constexpr int BASE_CENTRE = 10;
28constexpr int BASE_MAX = 16;
29
30struct StackScope {
31    const char* start;
32    const char* end;
33};
34struct StandardLibrary {
35    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
43static StackScope g_mainStack;
44static std::map<std::string, StandardLibrary> g_stdLib;
45}  // namespace
46
47static 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
62static 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
93static 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
106static 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
135static 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
146static 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
167void 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
198static bool IfContained(const char* start, const char* end, const char* ptr)
199{
200    bool ret = (ptr >= start && ptr < end);
201    return ret;
202}
203
204static 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
219static bool IfSubThread(pid_t pid, pid_t tid)
220{
221    return pid != tid;
222}
223
224void 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