1 /**
2 * Copyright (c) 2021-2022 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 "signal_handler.h"
17 #include "utils/logger.h"
18 #include <algorithm>
19 #include <cstdlib>
20 #include "include/method.h"
21 #include "include/runtime.h"
22 #include "include/panda_vm.h"
23 #include <sys/ucontext.h>
24 #include "compiler_options.h"
25 #include "code_info/code_info.h"
26 #include "include/stack_walker.h"
27 #include "tooling/pt_thread_info.h"
28 #include "tooling/sampler/sampling_profiler.h"
29
30 #ifdef PANDA_TARGET_AMD64
31 extern "C" void StackOverflowExceptionEntrypointTrampoline();
32 #endif
33
34 namespace panda {
35
IsValidStack(const ManagedThread * thread)36 static bool IsValidStack([[maybe_unused]] const ManagedThread *thread)
37 {
38 // #3649 CFrame::Initialize fires an ASSERT fail.
39 // The issue is that ManagedStack is not always in a consistent state.
40 // NOTE(bulasevich): implement ManagedStack state check before the printout. For now, disable the output.
41 return false;
42 }
43
44 // Something goes really wrong. Dump info and exit.
DumpStackTrace(int signo,const siginfo_t * info,const void * context)45 static void DumpStackTrace([[maybe_unused]] int signo, [[maybe_unused]] const siginfo_t *info,
46 [[maybe_unused]] const void *context)
47 {
48 auto thread = ManagedThread::GetCurrent();
49 if (thread == nullptr) {
50 LOG(ERROR, RUNTIME) << "Native thread segmentation fault";
51 return;
52 }
53 if (!IsValidStack(thread)) {
54 return;
55 }
56
57 LOG(ERROR, RUNTIME) << "Managed thread segmentation fault";
58 for (auto stack = StackWalker::Create(thread); stack.HasFrame(); stack.NextFrame()) {
59 Method *method = stack.GetMethod();
60 LOG(ERROR, RUNTIME) << method->GetClass()->GetName() << "." << method->GetName().data << " at "
61 << method->GetLineNumberAndSourceFile(stack.GetBytecodePc());
62 }
63 }
64
UseDebuggerdSignalHandler(int sig)65 static void UseDebuggerdSignalHandler(int sig)
66 {
67 LOG(WARNING, RUNTIME) << "panda vm can not handle sig " << sig << ", call next handler";
68 }
69
CallSignalActionHandler(int sig,siginfo_t * info,void * context)70 static bool CallSignalActionHandler(int sig, siginfo_t *info, void *context)
71 { // NOLINT
72 return Runtime::GetCurrent()->GetSignalManager()->SignalActionHandler(sig, info, context);
73 }
74
SignalActionHandler(int sig,siginfo_t * info,void * context)75 bool SignalManager::SignalActionHandler(int sig, siginfo_t *info, void *context)
76 {
77 panda::Logger::Sync();
78 if (InOatCode(info, context, true)) {
79 for (const auto &handler : oatCodeHandler_) {
80 if (handler->Action(sig, info, context)) {
81 return true;
82 }
83 }
84 }
85
86 // a signal can not handle in oat
87 if (InOtherCode(sig, info, context)) {
88 return true;
89 }
90
91 // Use the default exception handler function.
92 UseDebuggerdSignalHandler(sig);
93 return false;
94 }
95
InOatCode(const siginfo_t * siginfo,const void * context,bool checkBytecodePc) const96 bool SignalManager::InOatCode([[maybe_unused]] const siginfo_t *siginfo, [[maybe_unused]] const void *context,
97 [[maybe_unused]] bool checkBytecodePc) const
98 {
99 // NOTE(00510180) leak judge GetMethodAndReturnPcAndSp
100 return true;
101 }
102
InOtherCode(int sig,const siginfo_t * info,const void * context) const103 bool SignalManager::InOtherCode([[maybe_unused]] int sig, [[maybe_unused]] const siginfo_t *info,
104 [[maybe_unused]] const void *context) const
105 {
106 return false;
107 }
108
AddHandler(SignalHandler * handler,bool oatCode)109 void SignalManager::AddHandler(SignalHandler *handler, bool oatCode)
110 {
111 if (oatCode) {
112 oatCodeHandler_.push_back(handler);
113 } else {
114 otherHandlers_.push_back(handler);
115 }
116 }
117
RemoveHandler(SignalHandler * handler)118 void SignalManager::RemoveHandler(SignalHandler *handler)
119 {
120 auto itOat = std::find(oatCodeHandler_.begin(), oatCodeHandler_.end(), handler);
121 if (itOat != oatCodeHandler_.end()) {
122 oatCodeHandler_.erase(itOat);
123 return;
124 }
125 auto itOther = std::find(otherHandlers_.begin(), otherHandlers_.end(), handler);
126 if (itOther != otherHandlers_.end()) {
127 otherHandlers_.erase(itOther);
128 return;
129 }
130 LOG(FATAL, RUNTIME) << "handler doesn't exist: " << handler;
131 }
132
InitSignals()133 void SignalManager::InitSignals()
134 {
135 if (isInit_) {
136 return;
137 }
138
139 sigset_t mask;
140 sigfillset(&mask);
141 sigdelset(&mask, SIGABRT);
142 sigdelset(&mask, SIGBUS);
143 sigdelset(&mask, SIGFPE);
144 sigdelset(&mask, SIGILL);
145 sigdelset(&mask, SIGSEGV);
146
147 ClearSignalHooksHandlersArray();
148
149 // if running in phone,Sigchain will work,AddSpecialSignalHandlerFn in sighook will not be used
150 SigchainAction sigchainAction = {
151 CallSignalActionHandler,
152 mask,
153 SA_SIGINFO,
154 };
155 AddSpecialSignalHandlerFn(SIGSEGV, &sigchainAction);
156
157 isInit_ = true;
158
159 for (auto tmp : oatCodeHandler_) {
160 allocator_->Delete(tmp);
161 }
162 oatCodeHandler_.clear();
163 for (auto tmp : otherHandlers_) {
164 allocator_->Delete(tmp);
165 }
166 otherHandlers_.clear();
167 }
168
GetMethodAndReturnPcAndSp(const siginfo_t * siginfo,const void * context,const Method ** outMethod,const uintptr_t * outReturnPc,const uintptr_t * outSp)169 void SignalManager::GetMethodAndReturnPcAndSp([[maybe_unused]] const siginfo_t *siginfo,
170 [[maybe_unused]] const void *context,
171 [[maybe_unused]] const Method **outMethod,
172 [[maybe_unused]] const uintptr_t *outReturnPc,
173 [[maybe_unused]] const uintptr_t *outSp)
174 {
175 // just stub now
176 }
177
DeleteHandlersArray()178 void SignalManager::DeleteHandlersArray()
179 {
180 if (isInit_) {
181 for (auto tmp : oatCodeHandler_) {
182 allocator_->Delete(tmp);
183 }
184 oatCodeHandler_.clear();
185 for (auto tmp : otherHandlers_) {
186 allocator_->Delete(tmp);
187 }
188 otherHandlers_.clear();
189 RemoveSpecialSignalHandlerFn(SIGSEGV, CallSignalActionHandler);
190 isInit_ = false;
191 }
192 }
193
InAllocatedCodeRange(uintptr_t pc)194 bool InAllocatedCodeRange(uintptr_t pc)
195 {
196 Thread *thread = Thread::GetCurrent();
197 if (thread == nullptr) {
198 // Current thread is not attatched to any of the VMs
199 return false;
200 }
201
202 if (Runtime::GetCurrent()->GetClassLinker()->GetAotManager()->InAotFileRange(pc)) {
203 return true;
204 }
205
206 auto heapManager = thread->GetVM()->GetHeapManager();
207 if (heapManager == nullptr) {
208 return false;
209 }
210 auto codeAllocator = heapManager->GetCodeAllocator();
211 if (codeAllocator == nullptr) {
212 return false;
213 }
214 return codeAllocator->InAllocatedCodeRange(ToVoidPtr(pc));
215 }
216
IsInvalidPointer(uintptr_t addr)217 bool IsInvalidPointer(uintptr_t addr)
218 {
219 if (addr == 0) {
220 return true;
221 }
222 // address is at least 8-byte aligned
223 uintptr_t mask = 0x7;
224 return ((addr & mask) != 0);
225 }
226
227 // This is the way to get compiled method entry point
228 // FP regsiter -> stack -> method -> compilerEntryPoint
FindCompilerEntrypoint(const uintptr_t * fp)229 static uintptr_t FindCompilerEntrypoint(const uintptr_t *fp)
230 {
231 // Compiled code stack frame:
232 // +----------------+
233 // | Return address |
234 // +----------------+ <- Frame pointer
235 // | Frame pointer |
236 // +----------------+
237 // | panda::Method* |
238 // +----------------+
239 const int compiledFrameMethodOffset = BoundaryFrame<FrameKind::COMPILER>::METHOD_OFFSET;
240
241 if (IsInvalidPointer(reinterpret_cast<uintptr_t>(fp))) {
242 return 0;
243 }
244 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
245 uintptr_t pmethod = fp[compiledFrameMethodOffset];
246 if (IsInvalidPointer(pmethod)) {
247 return 0;
248 }
249
250 // it is important for GetCompiledEntryPoint method to have a lock-free implementation
251 auto entrypoint = reinterpret_cast<uintptr_t>((reinterpret_cast<Method *>(pmethod))->GetCompiledEntryPoint());
252 if (IsInvalidPointer(entrypoint)) {
253 return 0;
254 }
255
256 if (!InAllocatedCodeRange(entrypoint)) {
257 LOG(INFO, RUNTIME) << "Runtime SEGV handler: the entrypoint is not from JIT code";
258 return 0;
259 }
260
261 if (!compiler::CodeInfo::VerifyCompiledEntry(entrypoint)) {
262 // what we have found is not a compiled method
263 return 0;
264 }
265
266 return entrypoint;
267 }
268
DetectSEGVFromCompiledCode(int sig,siginfo_t * siginfo,void * context)269 bool DetectSEGVFromCompiledCode(int sig, siginfo_t *siginfo, void *context)
270 {
271 SignalContext signalContext(context);
272 uintptr_t pc = signalContext.GetPC();
273 LOG(DEBUG, RUNTIME) << "Handling SIGSEGV signal. PC:" << std::hex << pc;
274 if (!InAllocatedCodeRange(pc)) {
275 DumpStackTrace(sig, siginfo, context);
276 return true;
277 }
278 return false;
279 }
280
DetectSEGVFromHandler(int sig,siginfo_t * siginfo,void * context)281 bool DetectSEGVFromHandler(int sig, siginfo_t *siginfo, void *context)
282 {
283 SignalContext signalContext(context);
284 uintptr_t pc = signalContext.GetPC();
285 auto func = ToUintPtr(&FindCompilerEntrypoint);
286 const unsigned thisMethodSizeEstimation = 0x1000; // there is no way to find exact compiled method size
287 if (func < pc && pc < func + thisMethodSizeEstimation) {
288 // We have got SEGV from the signal handler itself!
289 // The links must have led us to wrong memory: FP regsiter -> stack -> method -> compilerEntryPoint
290 DumpStackTrace(sig, siginfo, context);
291 return true;
292 }
293 return false;
294 }
295
DetectSEGVFromMemory(int sig,siginfo_t * siginfo,void * context)296 bool DetectSEGVFromMemory(int sig, siginfo_t *siginfo, void *context)
297 {
298 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
299 auto memFaultLocation = ToUintPtr(siginfo->si_addr);
300 const uintptr_t maxObjectSize = 1U << 30U;
301 // the expected fault address is nullptr + offset within the object
302 if (memFaultLocation > maxObjectSize) {
303 DumpStackTrace(sig, siginfo, context);
304 return true;
305 }
306 return false;
307 }
308
DetectSEGVFromCode(int sig,siginfo_t * siginfo,void * context)309 bool DetectSEGVFromCode(int sig, siginfo_t *siginfo, void *context)
310 {
311 SignalContext signalContext(context);
312 uintptr_t pc = signalContext.GetPC();
313 uintptr_t entrypoint = FindCompilerEntrypoint(signalContext.GetFP());
314 if (entrypoint == 0) {
315 DumpStackTrace(sig, siginfo, context);
316 return true;
317 }
318 compiler::CodeInfo codeinfo(compiler::CodeInfo::GetCodeOriginFromEntryPoint(ToVoidPtr(entrypoint)));
319
320 auto codeSize = codeinfo.GetCodeSize();
321 if ((pc < entrypoint) || (pc > entrypoint + codeSize)) {
322 // we are not in a compiled method
323 DumpStackTrace(sig, siginfo, context);
324 return true;
325 }
326 return false;
327 }
328
UpdateReturnAddress(SignalContext & signalContext,uintptr_t newAddress)329 void UpdateReturnAddress(SignalContext &signalContext, uintptr_t newAddress)
330 {
331 #if (defined(PANDA_TARGET_ARM64) || defined(PANDA_TARGET_ARM32))
332 signalContext.SetLR(newAddress);
333 #elif defined(PANDA_TARGET_AMD64)
334 auto *sp = reinterpret_cast<uintptr_t *>(signalContext.GetSP() - sizeof(uintptr_t));
335 *sp = newAddress;
336 signalContext.SetSP(reinterpret_cast<uintptr_t>(sp));
337 #endif
338 }
339
DetectSEGVFromNullCheck(int sig,siginfo_t * siginfo,void * context)340 bool DetectSEGVFromNullCheck(int sig, siginfo_t *siginfo, void *context)
341 {
342 SignalContext signalContext(context);
343 uintptr_t pc = signalContext.GetPC();
344 uintptr_t entrypoint = FindCompilerEntrypoint(signalContext.GetFP());
345 compiler::CodeInfo codeinfo(compiler::CodeInfo::GetCodeOriginFromEntryPoint(ToVoidPtr(entrypoint)));
346 uintptr_t newPc = 0;
347 for (auto icheck : codeinfo.GetImplicitNullChecksTable()) {
348 uintptr_t nullCheckAddr = entrypoint + icheck.GetInstNativePc();
349 auto offset = icheck.GetOffset();
350 // We inserts information about implicit nullcheck after mem instruction,
351 // because encoder can insert emory calculation before the instruction and we don't know real address:
352 // addr | |
353 // | +---------------+ <--- null_check_addr - offset
354 // | | address calc |
355 // | | memory inst | <--- pc
356 // V +---------------+ <--- null_check_addr
357 // | |
358 if (pc < nullCheckAddr && pc + offset >= nullCheckAddr) {
359 newPc = nullCheckAddr;
360 break;
361 }
362 }
363
364 if (newPc == 0) {
365 LOG(INFO, RUNTIME) << "SEGV can't be handled. No matching entry found in the NullCheck table.\n"
366 << "PC: " << std::hex << pc;
367 for (auto icheck : codeinfo.GetImplicitNullChecksTable()) {
368 LOG(INFO, RUNTIME) << "nullcheck: " << std::hex << (entrypoint + icheck.GetInstNativePc());
369 }
370 DumpStackTrace(sig, siginfo, context);
371 return true;
372 }
373 LOG(DEBUG, RUNTIME) << "PC fixup: " << std::hex << newPc;
374
375 UpdateReturnAddress(signalContext, newPc);
376 signalContext.SetPC(reinterpret_cast<uintptr_t>(NullPointerExceptionBridge));
377 EVENT_IMPLICIT_NULLCHECK(newPc);
378
379 return false;
380 }
381
SamplerSigSegvHandler(int sig,siginfo_t * siginfo,void * context)382 void SamplerSigSegvHandler([[maybe_unused]] int sig, [[maybe_unused]] siginfo_t *siginfo,
383 [[maybe_unused]] void *context)
384 {
385 auto mthread = ManagedThread::GetCurrent();
386 ASSERT(mthread != nullptr);
387
388 int numToReturn = 1;
389 // NOLINTNEXTLINE(cert-err52-cpp)
390 longjmp(mthread->GetPtThreadInfo()->GetSamplingInfo()->GetSigSegvJmpEnv(), numToReturn);
391 }
392
DetectSEGVFromSamplingProfilerHandler(int sig,siginfo_t * siginfo,void * context)393 bool DetectSEGVFromSamplingProfilerHandler([[maybe_unused]] int sig, [[maybe_unused]] siginfo_t *siginfo,
394 [[maybe_unused]] void *context)
395 {
396 auto *mthread = ManagedThread::GetCurrent();
397 ASSERT(mthread != nullptr);
398
399 auto *sampler = Runtime::GetCurrent()->GetTools().GetSamplingProfiler();
400 auto *samplingInfo = mthread->GetPtThreadInfo()->GetSamplingInfo();
401 if (sampler == nullptr || samplingInfo == nullptr) {
402 return false;
403 }
404
405 if (sampler->IsSegvHandlerEnable() && samplingInfo->IsThreadSampling()) {
406 SignalContext signalContext(context);
407 signalContext.SetPC(reinterpret_cast<uintptr_t>(&SamplerSigSegvHandler));
408 return true;
409 }
410
411 return false;
412 }
413
RuntimeSEGVHandler(int sig,siginfo_t * siginfo,void * context)414 bool RuntimeSEGVHandler(int sig, siginfo_t *siginfo, void *context)
415 {
416 if (DetectSEGVFromSamplingProfilerHandler(sig, siginfo, context)) {
417 return true;
418 }
419
420 if (DetectSEGVFromCompiledCode(sig, siginfo, context)) {
421 return false;
422 }
423
424 if (DetectSEGVFromHandler(sig, siginfo, context)) {
425 return false;
426 }
427
428 if (DetectSEGVFromMemory(sig, siginfo, context)) {
429 return false;
430 }
431
432 if (DetectSEGVFromCode(sig, siginfo, context)) {
433 return false;
434 }
435
436 if (DetectSEGVFromNullCheck(sig, siginfo, context)) {
437 return false;
438 }
439
440 return true;
441 }
442
Action(int sig,siginfo_t * siginfo,void * context)443 bool NullPointerHandler::Action(int sig, siginfo_t *siginfo, void *context)
444 {
445 if (sig != SIGSEGV) {
446 return false;
447 }
448 if (!RuntimeSEGVHandler(sig, siginfo, context)) {
449 return false;
450 }
451 LOG(DEBUG, RUNTIME) << "NullPointerHandler happen,Throw NullPointerHandler Exception, signal:" << sig;
452 /* NullPointer has been check in aot or here now,then return to interpreter, so exception not build here
453 * issue #1437
454 * panda::ThrowNullPointerException();
455 */
456 return true;
457 }
458
459 NullPointerHandler::~NullPointerHandler() = default;
460
Action(int sig,siginfo_t * siginfo,void * context)461 bool StackOverflowHandler::Action(int sig, [[maybe_unused]] siginfo_t *siginfo, [[maybe_unused]] void *context)
462 {
463 if (sig != SIGSEGV) {
464 return false;
465 }
466 auto *thread = ManagedThread::GetCurrent();
467 ASSERT(thread != nullptr);
468 SignalContext signalContext(context);
469 auto memCheckLocation = signalContext.GetSP() - ManagedThread::GetStackOverflowCheckOffset();
470 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
471 auto memFaultLocation = ToUintPtr(siginfo->si_addr);
472 if (memCheckLocation != memFaultLocation) {
473 return false;
474 }
475
476 LOG(DEBUG, RUNTIME) << "Stack overflow occurred";
477
478 // StackOverflow stackmap has zero address
479 thread->SetNativePc(0);
480 // Set compiler Frame in Thread
481 thread->SetCurrentFrame(reinterpret_cast<Frame *>(signalContext.GetFP()));
482 #ifdef PANDA_TARGET_AMD64
483 signalContext.SetPC(reinterpret_cast<uintptr_t>(StackOverflowExceptionEntrypointTrampoline));
484 #else
485 /* To save/restore callee-saved regs we get into StackOverflowExceptionEntrypoint
486 * by means of StackOverflowExceptionBridge.
487 * The bridge stores LR to ManagedThread.npc, which is used by StackWalker::CreateCFrame,
488 * and it must be 0 in case of StackOverflow.
489 */
490 signalContext.SetLR(0);
491 signalContext.SetPC(reinterpret_cast<uintptr_t>(StackOverflowExceptionBridge));
492 #endif
493
494 return true;
495 }
496 } // namespace panda
497