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