• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 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 ark {
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     ark::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     // | ark::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     static constexpr uint32_t LLVM_HANDLER_TAG = 1U << 31U;
348     auto handlerPc = reinterpret_cast<uintptr_t>(NullPointerExceptionBridge);
349     bool llvm = false;
350     for (auto icheck : codeinfo.GetImplicitNullChecksTable()) {
351         uintptr_t nullCheckAddr = entrypoint + icheck.GetInstNativePc();
352         auto offset = icheck.GetOffset();
353         if (pc == nullCheckAddr && (LLVM_HANDLER_TAG & offset) != 0) {
354             // Code was compiled by LLVM.
355             // We jump to the handler pc, which will call NullPointerException on its own.
356             // We do not jump to NullPointerExceptionBridge directly here because LLVM code does not have stackmap on
357             // memory instructions
358             handlerPc = (~LLVM_HANDLER_TAG & offset) + entrypoint;
359             newPc = nullCheckAddr;
360             llvm = true;
361             break;
362         }
363         // We insert information about implicit nullcheck after mem instruction,
364         // because encoder can insert memory calculation before the instruction, and we don't know real address:
365         //   addr |               |
366         //    |   +---------------+  <--- nullCheckAddr - offset
367         //    |   | address calc  |
368         //    |   | memory inst   |  <--- pc
369         //    V   +---------------+  <--- nullCheckAddr
370         //        |               |
371         if (pc < nullCheckAddr && pc + offset >= nullCheckAddr) {
372             newPc = nullCheckAddr;
373             break;
374         }
375     }
376 
377     if (newPc == 0) {
378         LOG(INFO, RUNTIME) << "SEGV can't be handled. No matching entry found in the NullCheck table.\n"
379                            << "PC: " << std::hex << pc;
380         for (auto icheck : codeinfo.GetImplicitNullChecksTable()) {
381             LOG(INFO, RUNTIME) << "nullcheck: " << std::hex << (entrypoint + icheck.GetInstNativePc());
382         }
383         DumpStackTrace(sig, siginfo, context);
384         return true;
385     }
386     LOG(DEBUG, RUNTIME) << "PC fixup: " << std::hex << newPc;
387 
388     if (!llvm) {
389         UpdateReturnAddress(signalContext, newPc);
390     }
391     signalContext.SetPC(handlerPc);
392     EVENT_IMPLICIT_NULLCHECK(newPc);
393 
394     return false;
395 }
396 
SamplerSigSegvHandler(int sig,siginfo_t * siginfo,void * context)397 void SamplerSigSegvHandler([[maybe_unused]] int sig, [[maybe_unused]] siginfo_t *siginfo,
398                            [[maybe_unused]] void *context)
399 {
400     auto mthread = ManagedThread::GetCurrent();
401     ASSERT(mthread != nullptr);
402 
403     int numToReturn = 1;
404     // NOLINTNEXTLINE(cert-err52-cpp)
405     longjmp(mthread->GetPtThreadInfo()->GetSamplingInfo()->GetSigSegvJmpEnv(), numToReturn);
406 }
407 
DetectSEGVFromSamplingProfilerHandler(int sig,siginfo_t * siginfo,void * context)408 bool DetectSEGVFromSamplingProfilerHandler([[maybe_unused]] int sig, [[maybe_unused]] siginfo_t *siginfo,
409                                            [[maybe_unused]] void *context)
410 {
411     auto *mthread = ManagedThread::GetCurrent();
412     ASSERT(mthread != nullptr);
413 
414     auto *sampler = Runtime::GetCurrent()->GetTools().GetSamplingProfiler();
415     auto *samplingInfo = mthread->GetPtThreadInfo()->GetSamplingInfo();
416     if (sampler == nullptr || samplingInfo == nullptr) {
417         return false;
418     }
419 
420     if (sampler->IsSegvHandlerEnable() && samplingInfo->IsThreadSampling()) {
421         SignalContext signalContext(context);
422         signalContext.SetPC(reinterpret_cast<uintptr_t>(&SamplerSigSegvHandler));
423         return true;
424     }
425 
426     return false;
427 }
428 
RuntimeSEGVHandler(int sig,siginfo_t * siginfo,void * context)429 bool RuntimeSEGVHandler(int sig, siginfo_t *siginfo, void *context)
430 {
431     if (DetectSEGVFromSamplingProfilerHandler(sig, siginfo, context)) {
432         return true;
433     }
434 
435     if (DetectSEGVFromCompiledCode(sig, siginfo, context)) {
436         return false;
437     }
438 
439     if (DetectSEGVFromHandler(sig, siginfo, context)) {
440         return false;
441     }
442 
443     if (DetectSEGVFromMemory(sig, siginfo, context)) {
444         return false;
445     }
446 
447     if (DetectSEGVFromCode(sig, siginfo, context)) {
448         return false;
449     }
450 
451     if (DetectSEGVFromNullCheck(sig, siginfo, context)) {
452         return false;
453     }
454 
455     return true;
456 }
457 
Action(int sig,siginfo_t * siginfo,void * context)458 bool NullPointerHandler::Action(int sig, siginfo_t *siginfo, void *context)
459 {
460     if (sig != SIGSEGV) {
461         return false;
462     }
463     if (!RuntimeSEGVHandler(sig, siginfo, context)) {
464         return false;
465     }
466     LOG(DEBUG, RUNTIME) << "NullPointerHandler happen,Throw NullPointerHandler Exception, signal:" << sig;
467     /* NullPointer has been check in aot or here now,then return to interpreter, so exception not build here
468      * issue #1437
469      * ark::ThrowNullPointerException();
470      */
471     return true;
472 }
473 
474 NullPointerHandler::~NullPointerHandler() = default;
475 
Action(int sig,siginfo_t * siginfo,void * context)476 bool StackOverflowHandler::Action(int sig, [[maybe_unused]] siginfo_t *siginfo, [[maybe_unused]] void *context)
477 {
478     if (sig != SIGSEGV) {
479         return false;
480     }
481     auto *thread = ManagedThread::GetCurrent();
482     ASSERT(thread != nullptr);
483     SignalContext signalContext(context);
484     auto memCheckLocation = signalContext.GetSP() - ManagedThread::GetStackOverflowCheckOffset();
485     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
486     auto memFaultLocation = ToUintPtr(siginfo->si_addr);
487     if (memCheckLocation != memFaultLocation) {
488         return false;
489     }
490 
491     LOG(DEBUG, RUNTIME) << "Stack overflow occurred";
492 
493     // StackOverflow stackmap has zero address
494     thread->SetNativePc(0);
495     // Set compiler Frame in Thread
496     thread->SetCurrentFrame(reinterpret_cast<Frame *>(signalContext.GetFP()));
497 #ifdef PANDA_TARGET_AMD64
498     signalContext.SetPC(reinterpret_cast<uintptr_t>(StackOverflowExceptionEntrypointTrampoline));
499 #else
500     /* To save/restore callee-saved regs we get into StackOverflowExceptionEntrypoint
501      * by means of StackOverflowExceptionBridge.
502      * The bridge stores LR to ManagedThread.npc, which is used by StackWalker::CreateCFrame,
503      * and it must be 0 in case of StackOverflow.
504      */
505     signalContext.SetLR(0);
506     signalContext.SetPC(reinterpret_cast<uintptr_t>(StackOverflowExceptionBridge));
507 #endif
508 
509     return true;
510 }
511 }  // namespace ark
512