1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
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 "dfx_accessors.h"
17 
18 #include <algorithm>
19 #include <elf.h>
20 #include <fcntl.h>
21 #include <securec.h>
22 #include <sys/ptrace.h>
23 #include <sys/syscall.h>
24 #include <sys/uio.h>
25 #include "dfx_define.h"
26 #include "dfx_errors.h"
27 #include "dfx_log.h"
28 #include "dfx_regs.h"
29 #include "dfx_trace_dlsym.h"
30 #include "dfx_elf.h"
31 #include "dfx_maps.h"
32 
33 namespace OHOS {
34 namespace HiviewDFX {
35 namespace {
36 #undef LOG_DOMAIN
37 #undef LOG_TAG
38 #define LOG_DOMAIN 0xD002D11
39 #define LOG_TAG "DfxAccessors"
40 
41 static const int FOUR_BYTES = 4;
42 static const int EIGHT_BYTES = 8;
43 static const int THIRTY_TWO_BITS = 32;
44 }
45 
GetMapByPcAndCtx(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)46 bool DfxAccessors::GetMapByPcAndCtx(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
47 {
48     if (arg == nullptr) {
49         return false;
50     }
51     UnwindContext* ctx = reinterpret_cast<UnwindContext *>(arg);
52     if (ctx->map != nullptr && ctx->map->Contain(static_cast<uint64_t>(pc))) {
53         map = ctx->map;
54         LOGU("map had matched by ctx, map name: %s", map->name.c_str());
55         return true;
56     }
57 
58     if (ctx->maps == nullptr || !ctx->maps->FindMapByAddr(pc, map) || (map == nullptr)) {
59         ctx->map = nullptr;
60         return false;
61     }
62     ctx->map = map;
63     return true;
64 }
65 
CreatePipe()66 bool DfxAccessorsLocal::CreatePipe()
67 {
68     if (initPipe_) {
69         return true;
70     }
71 
72     std::lock_guard<std::mutex> lock(mutex_);
73     if (!initPipe_ && syscall(SYS_pipe2, pfd_, O_CLOEXEC | O_NONBLOCK) == 0) {
74         initPipe_ = true;
75     }
76     return initPipe_;
77 }
78 
~DfxAccessorsLocal(void)79 DfxAccessorsLocal::~DfxAccessorsLocal(void)
80 {
81     if (initPipe_) {
82         syscall(SYS_close, pfd_[PIPE_WRITE]);
83         syscall(SYS_close, pfd_[PIPE_READ]);
84         initPipe_ = false;
85     }
86 }
87 
IsValidFrame(uintptr_t addr,uintptr_t stackBottom,uintptr_t stackTop)88 bool DfxAccessorsLocal::IsValidFrame(uintptr_t addr, uintptr_t stackBottom, uintptr_t stackTop)
89 {
90     if (UNLIKELY(stackTop < stackBottom)) {
91         return false;
92     }
93     if ((addr >= stackBottom) && (addr + sizeof(uintptr_t) < stackTop)) {
94         return true;
95     }
96 
97     if (CreatePipe()) {
98         return OHOS_TEMP_FAILURE_RETRY(syscall(SYS_write, pfd_[PIPE_WRITE], addr, sizeof(uintptr_t))) != -1;
99     }
100     return false;
101 }
102 
AccessMem(uintptr_t addr,uintptr_t * val,void * arg)103 NO_SANITIZE int DfxAccessorsLocal::AccessMem(uintptr_t addr, uintptr_t *val, void *arg)
104 {
105     if (val == nullptr) {
106         return UNW_ERROR_INVALID_MEMORY;
107     }
108     UnwindContext* ctx = reinterpret_cast<UnwindContext *>(arg);
109     if ((ctx != nullptr) && (ctx->stackCheck == true) && (!IsValidFrame(addr, ctx->stackBottom, ctx->stackTop))) {
110         LOGU("%s", "Failed to access addr");
111         return UNW_ERROR_INVALID_MEMORY;
112     }
113     *val = *reinterpret_cast<uintptr_t *>(addr);
114     return UNW_ERROR_NONE;
115 }
116 
AccessReg(int reg,uintptr_t * val,void * arg)117 int DfxAccessorsLocal::AccessReg(int reg, uintptr_t *val, void *arg)
118 {
119     UnwindContext* ctx = reinterpret_cast<UnwindContext *>(arg);
120     if (ctx == nullptr) {
121         return UNW_ERROR_INVALID_CONTEXT;
122     }
123     if (ctx->regs == nullptr || reg < 0 || reg >= (int)ctx->regs->RegsSize()) {
124         return UNW_ERROR_INVALID_REGS;
125     }
126 
127     *val = static_cast<uintptr_t>((*(ctx->regs))[reg]);
128     return UNW_ERROR_NONE;
129 }
130 
FindUnwindTable(uintptr_t pc,UnwindTableInfo & uti,void * arg)131 int DfxAccessorsLocal::FindUnwindTable(uintptr_t pc, UnwindTableInfo& uti, void *arg)
132 {
133     UnwindContext *ctx = reinterpret_cast<UnwindContext *>(arg);
134     if (ctx == nullptr) {
135         return UNW_ERROR_INVALID_CONTEXT;
136     }
137 
138     int ret = UNW_ERROR_INVALID_ELF;
139     if (ctx->map != nullptr && ctx->map->IsVdsoMap()) {
140         auto elf = ctx->map->GetElf(getpid());
141         if (elf == nullptr) {
142             LOGU("%s", "FindUnwindTable elf is null");
143             return ret;
144         }
145         ret = elf->FindUnwindTableInfo(pc, ctx->map, uti);
146     } else {
147         ret = DfxElf::FindUnwindTableLocal(pc, uti);
148     }
149     if (ret == UNW_ERROR_NONE) {
150         ctx->di = uti;
151     }
152     return ret;
153 }
154 
GetMapByPc(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)155 int DfxAccessorsLocal::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
156 {
157     if (!DfxAccessors::GetMapByPcAndCtx(pc, map, arg)) {
158         return UNW_ERROR_INVALID_MAP;
159     }
160     return UNW_ERROR_NONE;
161 }
162 
AccessMem(uintptr_t addr,uintptr_t * val,void * arg)163 int DfxAccessorsRemote::AccessMem(uintptr_t addr, uintptr_t *val, void *arg)
164 {
165 #if !defined(__LP64__)
166     // Cannot read an address greater than 32 bits in a 32 bit context.
167     if (addr > UINT32_MAX) {
168         return UNW_ERROR_ILLEGAL_VALUE;
169     }
170 #endif
171     UnwindContext *ctx = reinterpret_cast<UnwindContext *>(arg);
172     if ((ctx == nullptr) || (ctx->pid <= 0)) {
173         return UNW_ERROR_INVALID_CONTEXT;
174     }
175 
176     if (ctx->map != nullptr && ctx->map->elf != nullptr) {
177         uintptr_t pos = ctx->map->GetRelPc(addr);
178         if (ctx->map->elf->Read(pos, val, sizeof(uintptr_t))) {
179             LOGU("Read elf mmap pos: %p", (void *)pos);
180             return UNW_ERROR_NONE;
181         }
182     }
183 
184     int i, end;
185     if (sizeof(long) == FOUR_BYTES && sizeof(uintptr_t) == EIGHT_BYTES) {
186         end = 2; // 2 : read two times
187     } else {
188         end = 1;
189     }
190 
191     uintptr_t tmpVal;
192     for (i = 0; i < end; i++) {
193         uintptr_t tmpAddr = ((i == 0) ? addr : addr + FOUR_BYTES);
194         errno = 0;
195 
196         tmpVal = (unsigned long) ptrace(PTRACE_PEEKDATA, ctx->pid, tmpAddr, nullptr);
197         if (i == 0) {
198             *val = 0;
199         }
200 
201 #if __BYTE_ORDER == __LITTLE_ENDIAN
202         *val |= tmpVal << (i * THIRTY_TWO_BITS);
203 #else
204         *val |= (i == 0 && end == 2 ? tmpVal << THIRTY_TWO_BITS : tmpVal); // 2 : read two times
205 #endif
206         if (errno) {
207             LOGU("errno: %d", errno);
208             return UNW_ERROR_ILLEGAL_VALUE;
209         }
210     }
211     return UNW_ERROR_NONE;
212 }
213 
AccessReg(int reg,uintptr_t * val,void * arg)214 int DfxAccessorsRemote::AccessReg(int reg, uintptr_t *val, void *arg)
215 {
216     UnwindContext *ctx = reinterpret_cast<UnwindContext *>(arg);
217     if (ctx == nullptr) {
218         return UNW_ERROR_INVALID_CONTEXT;
219     }
220     if (ctx->regs == nullptr || reg < 0 || reg >= (int)ctx->regs->RegsSize()) {
221         return UNW_ERROR_INVALID_REGS;
222     }
223 
224     *val = static_cast<uintptr_t>((*(ctx->regs))[reg]);
225     return UNW_ERROR_NONE;
226 }
227 
FindUnwindTable(uintptr_t pc,UnwindTableInfo & uti,void * arg)228 int DfxAccessorsRemote::FindUnwindTable(uintptr_t pc, UnwindTableInfo& uti, void *arg)
229 {
230     DFX_TRACE_SCOPED_DLSYM("FindUnwindTable");
231     UnwindContext *ctx = reinterpret_cast<UnwindContext *>(arg);
232     if (ctx == nullptr || ctx->map == nullptr) {
233         return UNW_ERROR_INVALID_CONTEXT;
234     }
235     if (pc >= ctx->di.startPc && pc < ctx->di.endPc) {
236         LOGU("%s", "FindUnwindTable had pc matched");
237         uti = ctx->di;
238         return UNW_ERROR_NONE;
239     }
240 
241     auto elf = ctx->map->GetElf(ctx->pid);
242     if (elf == nullptr) {
243         LOGU("%s", "FindUnwindTable elf is null");
244         return UNW_ERROR_INVALID_ELF;
245     }
246     int ret = elf->FindUnwindTableInfo(pc, ctx->map, uti);
247     if (ret == UNW_ERROR_NONE) {
248         ctx->di = uti;
249     }
250     return ret;
251 }
252 
GetMapByPc(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)253 int DfxAccessorsRemote::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
254 {
255     if (!DfxAccessors::GetMapByPcAndCtx(pc, map, arg)) {
256         return UNW_ERROR_INVALID_MAP;
257     }
258     return UNW_ERROR_NONE;
259 }
260 
AccessMem(uintptr_t addr,uintptr_t * val,void * arg)261 int DfxAccessorsCustomize::AccessMem(uintptr_t addr, uintptr_t *val, void *arg)
262 {
263     if (accessors_ == nullptr || accessors_->AccessMem == nullptr) {
264         return -1;
265     }
266     return accessors_->AccessMem(addr, val, arg);
267 }
268 
AccessReg(int reg,uintptr_t * val,void * arg)269 int DfxAccessorsCustomize::AccessReg(int reg, uintptr_t *val, void *arg)
270 {
271     if (accessors_ == nullptr || accessors_->AccessReg == nullptr) {
272         return -1;
273     }
274     return accessors_->AccessReg(reg, val, arg);
275 }
276 
FindUnwindTable(uintptr_t pc,UnwindTableInfo & uti,void * arg)277 int DfxAccessorsCustomize::FindUnwindTable(uintptr_t pc, UnwindTableInfo& uti, void *arg)
278 {
279     if (accessors_ == nullptr || accessors_->FindUnwindTable == nullptr) {
280         return -1;
281     }
282     return accessors_->FindUnwindTable(pc, uti, arg);
283 }
284 
GetMapByPc(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)285 int DfxAccessorsCustomize::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
286 {
287     if (accessors_ == nullptr || accessors_->GetMapByPc == nullptr) {
288         return -1;
289     }
290     return accessors_->GetMapByPc(pc, map, arg);
291 }
292 } // namespace HiviewDFX
293 } // namespace OHOS
294