* Copyright (c) 2021-2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "dfx_signal_local_handler.h"
#include <securec.h>
#include <csignal>
#include <sigchain.h>
#include <cstdint>
#include <cstdio>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <linux/futex.h>
#include "dfx_allocator.h"
#include "dfx_crash_local_handler.h"
#include "dfx_cutil.h"
#include "dfx_log.h"
#ifdef LOG_DOMAIN
#undef LOG_DOMAIN
#define LOG_DOMAIN 0xD002D11
#endif
#ifdef LOG_TAG
#undef LOG_TAG
#define LOG_TAG "DfxSignalLocalHandler"
#endif
constexpr uint32_t FAULTLOGGERD_UID = 1202;
static CrashFdFunc g_crashFdFn = nullptr;
static SigAlarmFunc g_sigAlarmCallbackFn = nullptr;
#if !defined(__aarch64__) && !defined(__loongarch_lp64)
constexpr uint32_t LOCAL_HANDLER_STACK_SIZE = 128 * 1024;
static void *g_reservedChildStack = nullptr;
#endif
static struct ProcessDumpRequest g_request;
static pthread_mutex_t g_signalHandlerMutex = PTHREAD_MUTEX_INITIALIZER;
static constexpr int CATCHER_STACK_SIGNALS[] = {
SIGABRT, SIGBUS, SIGILL, SIGSEGV,
};
#if !defined(__aarch64__) && !defined(__loongarch_lp64)
static void ReserveChildThreadSignalStack(void)
{
g_reservedChildStack = mmap(nullptr, LOCAL_HANDLER_STACK_SIZE, \
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, 1, 0);
if (g_reservedChildStack == MAP_FAILED) {
DFXLOGE("[%{public}d]: Failed to alloc memory for child stack.", __LINE__);
return;
}
g_reservedChildStack = static_cast<void *>(static_cast<uint8_t *>(g_reservedChildStack) +
LOCAL_HANDLER_STACK_SIZE - 1);
}
#endif
AT_UNUSED static void FutexWait(volatile void* ftx, int value)
{
syscall(__NR_futex, ftx, FUTEX_WAIT, value, NULL, NULL, 0);
}
static int DoCrashHandler(void* arg)
{
int sig = *(static_cast<int *>(arg));
RegisterAllocator();
DFXLOGI("DoCrashHandler::start handle sig(%{public}d)", sig);
if (sig == SIGALRM) {
if (g_sigAlarmCallbackFn != nullptr) {
g_sigAlarmCallbackFn();
}
} else {
if (g_crashFdFn == nullptr) {
CrashLocalHandler(&g_request);
} else {
int fd = g_crashFdFn(&g_request);
CrashLocalHandlerFd(fd, &g_request);
if (fd >= 0) {
close(fd);
}
}
}
DFXLOGI("DoCrashHandler::finish handle sig(%{public}d)", sig);
UnregisterAllocator();
pthread_mutex_unlock(&g_signalHandlerMutex);
_exit(0);
return 0;
}
void DFX_SignalLocalHandler(int sig, siginfo_t *si, void *context)
{
pthread_mutex_lock(&g_signalHandlerMutex);
if (sig != SIGALRM) {
(void)memset_s(&g_request, sizeof(g_request), 0, sizeof(g_request));
g_request.tid = gettid();
g_request.pid = getpid();
g_request.uid = FAULTLOGGERD_UID;
g_request.timeStamp = GetTimeMilliseconds();
DFXLOGI("DFX_SignalLocalHandler :: sig(%{public}d), pid(%{public}d), tid(%{public}d).",
sig, g_request.pid, g_request.tid);
GetThreadNameByTid(g_request.tid, g_request.threadName, sizeof(g_request.threadName));
GetProcessName(g_request.processName, sizeof(g_request.processName));
(void)memcpy_s(&(g_request.siginfo), sizeof(siginfo_t), si, sizeof(siginfo_t));
(void)memcpy_s(&(g_request.context), sizeof(ucontext_t), context, sizeof(ucontext_t));
}
#if defined(__aarch64__) || defined(__loongarch_lp64)
DoCrashHandler(&sig);
#else
int pseudothreadTid = -1;
pid_t childTid = clone(DoCrashHandler, g_reservedChildStack, \
CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID, \
&sig, &pseudothreadTid, NULL, NULL, &pseudothreadTid);
if (childTid == -1) {
DFXLOGE("Failed to create thread for crash local handler");
pthread_mutex_unlock(&g_signalHandlerMutex);
return;
}
FutexWait(&pseudothreadTid, -1);
FutexWait(&pseudothreadTid, childTid);
DFXLOGI("child thread(%{public}d) exit.", childTid);
_exit(0);
#endif
}
void DFX_GetCrashFdFunc(CrashFdFunc fn)
{
g_crashFdFn = fn;
}
void DFX_InstallLocalSignalHandler(void)
{
#if !defined(__aarch64__) && !defined(__loongarch_lp64)
ReserveChildThreadSignalStack();
#endif
sigset_t set;
sigemptyset(&set);
struct sigaction action;
(void)memset_s(&action, sizeof(action), 0, sizeof(action));
sigfillset(&action.sa_mask);
action.sa_sigaction = DFX_SignalLocalHandler;
action.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
for (auto sig : CATCHER_STACK_SIGNALS) {
remove_all_special_handler(sig);
sigaddset(&set, sig);
if (sigaction(sig, &action, nullptr) != 0) {
DFXLOGE("Failed to register signal(%{public}d)", sig);
}
}
sigprocmask(SIG_UNBLOCK, &set, nullptr);
}
void DFX_SetSigAlarmCallBack(SigAlarmFunc func)
{
g_sigAlarmCallbackFn = func;
}