fix(tng): 防止 SIGABRT 跳 LR 死循环,改 pc+4 与 streak freeze
吞 ABRT 时若跳回近距 LR 会瞬间再 abort 刷屏;改为优先 pc+4,同 tid+pc 超阈值后冻结工作线程,主线程仅 bail。
This commit is contained in:
@@ -123,21 +123,64 @@ static void promon_segv_handler(int sig, siginfo_t *info, void *ctx) {
|
||||
raise(SIGSEGV);
|
||||
}
|
||||
|
||||
/** libc++abi __cxa_guard_acquire 递归初始化 → abort;跳回 LR 继续而非杀进程。 */
|
||||
static std::atomic<pid_t> g_main_tid{0};
|
||||
static std::atomic<int> g_abrt_swallow{0};
|
||||
static std::atomic<pid_t> g_abrt_last_tid{0};
|
||||
static std::atomic<uintptr_t> g_abrt_last_pc{0};
|
||||
static std::atomic<int> g_abrt_streak{0};
|
||||
static constexpr int kMaxAbrtStreak = 3;
|
||||
|
||||
/** ABRT/TRAP:pc+4 或远距 LR;同 tid+pc 连触发则 freeze,避免 LR 自旋死循环。 */
|
||||
static void fatal_skip_handler(int sig, siginfo_t *info, void *ctx) {
|
||||
(void)info;
|
||||
ucontext_t *uc = reinterpret_cast<ucontext_t *>(ctx);
|
||||
#if defined(__aarch64__)
|
||||
uintptr_t pc = uc->uc_mcontext.pc;
|
||||
uintptr_t lr = uc->uc_mcontext.regs[30];
|
||||
LOGI("swallowed signal %d tid=%d pc=%lx lr=%lx", sig, (int)gettid(),
|
||||
(unsigned long)pc, (unsigned long)lr);
|
||||
if (lr != 0) {
|
||||
uc->uc_mcontext.pc = lr;
|
||||
pid_t tid = gettid();
|
||||
|
||||
if (sig == SIGABRT) {
|
||||
int streak = 1;
|
||||
if (g_abrt_last_tid.load() == tid && g_abrt_last_pc.load() == pc) {
|
||||
streak = g_abrt_streak.fetch_add(1) + 1;
|
||||
} else {
|
||||
g_abrt_last_tid.store(tid);
|
||||
g_abrt_last_pc.store(pc);
|
||||
g_abrt_streak.store(1);
|
||||
}
|
||||
if (streak > kMaxAbrtStreak) {
|
||||
if (tid == g_main_tid.load()) {
|
||||
LOGI("ABRT main-thread streak cap tid=%d pc=%lx — pc+4 bail", (int)tid,
|
||||
(unsigned long)pc);
|
||||
uc->uc_mcontext.pc = pc + 4;
|
||||
g_abrt_streak.store(0);
|
||||
return;
|
||||
}
|
||||
LOGI("ABRT streak cap tid=%d pc=%lx n=%d — freeze thread", (int)tid,
|
||||
(unsigned long)pc, streak);
|
||||
freeze_forever();
|
||||
}
|
||||
uintptr_t delta = (pc > lr) ? (pc - lr) : (lr - pc);
|
||||
uintptr_t target = pc + 4;
|
||||
/* lr 距 pc 很近时仍在 abort/epilogue 内,跳 LR 会 instant 再 ABRT */
|
||||
if (lr != 0 && delta > 64) {
|
||||
target = lr;
|
||||
}
|
||||
int n = ++g_abrt_swallow;
|
||||
if (n <= 3 || n % 100 == 0) {
|
||||
LOGI("ABRT skip tid=%d pc=%lx lr=%lx streak=%d -> %lx", (int)tid,
|
||||
(unsigned long)pc, (unsigned long)lr, streak,
|
||||
(unsigned long)target);
|
||||
}
|
||||
uc->uc_mcontext.pc = target;
|
||||
return;
|
||||
}
|
||||
if (pc != 0) {
|
||||
uc->uc_mcontext.pc = pc + 4;
|
||||
|
||||
if (sig == SIGTRAP) {
|
||||
uintptr_t target = pc != 0 ? pc + 4 : lr;
|
||||
LOGI("TRAP skip tid=%d pc=%lx -> %lx", (int)tid, (unsigned long)pc,
|
||||
(unsigned long)target);
|
||||
uc->uc_mcontext.pc = target;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -151,7 +194,7 @@ static void install_fatal_skip_handlers() {
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGABRT, &sa, nullptr);
|
||||
sigaction(SIGTRAP, &sa, nullptr);
|
||||
LOGI("fatal skip handlers (ABRT+TRAP→LR)");
|
||||
LOGI("fatal skip handlers (ABRT pc+4/streak-freeze + TRAP pc+4)");
|
||||
}
|
||||
|
||||
static void install_promon_segv_handler() {
|
||||
@@ -177,7 +220,7 @@ static void install_soft_signals() {
|
||||
sigaction(SIGABRT, &sa, nullptr);
|
||||
sigaction(SIGTRAP, &sa, nullptr);
|
||||
if (g_soft_sig_logged.fetch_add(1) == 0) {
|
||||
LOGI("soft signals (ABRT+TRAP skip→LR)");
|
||||
LOGI("soft signals (ABRT streak-freeze + TRAP pc+4)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +385,9 @@ static void *phase_thread(void *) {
|
||||
}
|
||||
|
||||
static void install_all(zygisk::Api *api) {
|
||||
LOGI("install pid=%d (PLT+ABRT/TRAP-skip+pc==lr-SEGV+exit_group@400ms)", getpid());
|
||||
g_main_tid.store(gettid());
|
||||
LOGI("install pid=%d main_tid=%d (PLT+ABRT-streak-freeze+TRAP+pc==lr-SEGV+exit_group@400ms)",
|
||||
getpid(), (int)g_main_tid.load());
|
||||
install_fatal_skip_handlers();
|
||||
install_soft_signals();
|
||||
install_plt(api);
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user