fix(tng): 防止 SIGABRT 跳 LR 死循环,改 pc+4 与 streak freeze

吞 ABRT 时若跳回近距 LR 会瞬间再 abort 刷屏;改为优先 pc+4,同 tid+pc 超阈值后冻结工作线程,主线程仅 bail。
This commit is contained in:
mars
2026-08-03 10:26:48 +08:00
parent 13e407623b
commit 818b2f4f51
2 changed files with 55 additions and 10 deletions

View File

@@ -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/TRAPpc+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);