dart-sdk/runtime/vm/signal_handler_android.cc
Vyacheslav Egorov 18bdb28ef6 [vm/profiler] On Android use alternative stack for handling SIGPROF.
Bionic implementation of setjmp mangles[1] stack pointer - which means
it is unsafe to handle signals on the thread stack (see b/152210274).

Thread interrupter is constantly sending SIGPROF to the Dart thread -
which means with a small probability it might hit the case when we are
inside setjmp. If SP is mangled it might point to random writable memory
or to non-writable region. In the first case we will get a very obscure
memory corruption, and in the second case kernel would send us SIGSEGV
because it fails to deliver original signal.

This bug is the source of the numerous mysterious crashes reported for Flutter,
looking like this:

  F/libc    (11547): Fatal signal 11 (SIGSEGV), code 128, fault addr 0x0 in tid 11572 (1.ui), pid 11547 (ectivity_change)
  ...
  signal 11 (SIGSEGV), code 128 (SI_KERNEL), fault addr 0x0
  ...
  backtrace:
      #00 pc 00018abc  /system/lib/libc.so (sigsetjmp+120)

Note the following key points: SIGSEGV has code SI_KERNEL (meaning it
was triggered by kernel - rather than by a hardware fault) and the first
and only frame is inside sigsetjmp (unwinding is obviously also broken
because SP is mangled).

Fixes https://github.com/flutter/flutter/issues/27077

[1] https://android.googlesource.com/platform/bionic/+/refs/heads/master/libc/arch-x86/bionic/setjmp.S#132

Change-Id: I91afa42dbf6575db0cce8e223368b857a49b39b8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/140643
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Vyacheslav Egorov <vegorov@google.com>
2020-03-23 21:49:37 +00:00

142 lines
4.1 KiB
C++

// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/globals.h"
#include "vm/instructions.h"
#include "vm/signal_handler.h"
#include "vm/simulator.h"
#if defined(HOST_OS_ANDROID)
namespace dart {
uintptr_t SignalHandler::GetProgramCounter(const mcontext_t& mcontext) {
uintptr_t pc = 0;
#if defined(HOST_ARCH_IA32)
pc = static_cast<uintptr_t>(mcontext.gregs[REG_EIP]);
#elif defined(HOST_ARCH_X64)
pc = static_cast<uintptr_t>(mcontext.gregs[REG_RIP]);
#elif defined(HOST_ARCH_ARM)
pc = static_cast<uintptr_t>(mcontext.arm_pc);
#elif defined(HOST_ARCH_ARM64)
pc = static_cast<uintptr_t>(mcontext.pc);
#else
#error Unsupported architecture.
#endif // HOST_ARCH_...
return pc;
}
uintptr_t SignalHandler::GetFramePointer(const mcontext_t& mcontext) {
uintptr_t fp = 0;
#if defined(HOST_ARCH_IA32)
fp = static_cast<uintptr_t>(mcontext.gregs[REG_EBP]);
#elif defined(HOST_ARCH_X64)
fp = static_cast<uintptr_t>(mcontext.gregs[REG_RBP]);
#elif defined(HOST_ARCH_ARM)
// B1.3.3 Program Status Registers (PSRs)
if ((mcontext.arm_cpsr & (1 << 5)) != 0) {
// Thumb mode.
fp = static_cast<uintptr_t>(mcontext.arm_r7);
} else {
// ARM mode.
fp = static_cast<uintptr_t>(mcontext.arm_fp);
}
#elif defined(HOST_ARCH_ARM64)
fp = static_cast<uintptr_t>(mcontext.regs[29]);
#else
#error Unsupported architecture.
#endif // HOST_ARCH_...
return fp;
}
uintptr_t SignalHandler::GetCStackPointer(const mcontext_t& mcontext) {
uintptr_t sp = 0;
#if defined(HOST_ARCH_IA32)
sp = static_cast<uintptr_t>(mcontext.gregs[REG_ESP]);
#elif defined(HOST_ARCH_X64)
sp = static_cast<uintptr_t>(mcontext.gregs[REG_RSP]);
#elif defined(HOST_ARCH_ARM)
sp = static_cast<uintptr_t>(mcontext.arm_sp);
#elif defined(HOST_ARCH_ARM64)
sp = static_cast<uintptr_t>(mcontext.sp);
#else
#error Unsupported architecture.
#endif // HOST_ARCH_...
return sp;
}
uintptr_t SignalHandler::GetDartStackPointer(const mcontext_t& mcontext) {
#if defined(TARGET_ARCH_ARM64) && !defined(USING_SIMULATOR)
return static_cast<uintptr_t>(mcontext.regs[SPREG]);
#else
return GetCStackPointer(mcontext);
#endif
}
uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) {
uintptr_t lr = 0;
#if defined(HOST_ARCH_IA32)
lr = 0;
#elif defined(HOST_ARCH_X64)
lr = 0;
#elif defined(HOST_ARCH_ARM)
lr = static_cast<uintptr_t>(mcontext.arm_lr);
#elif defined(HOST_ARCH_ARM64)
lr = static_cast<uintptr_t>(mcontext.regs[30]);
#else
#error Unsupported architecture.
#endif // HOST_ARCH_...
return lr;
}
void SignalHandler::InstallImpl(SignalAction action) {
// Bionic implementation of setjmp temporary mangles SP register
// in place which breaks signal delivery on the thread stack - when
// kernel tries to deliver SIGPROF and we are in the middle of
// setjmp SP value is invalid - might be pointing to random memory
// or outside of writable space at all. In the first case we
// get memory corruption and in the second case kernel would send
// SIGSEGV to the process. See b/152210274 for details.
// To work around this issue we are using alternative signal stack
// to handle SIGPROF signals.
stack_t ss;
ss.ss_size = SIGSTKSZ;
ss.ss_sp = malloc(ss.ss_size);
ss.ss_flags = 0;
int r = sigaltstack(&ss, NULL);
ASSERT(r == 0);
struct sigaction act = {};
act.sa_sigaction = action;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
r = sigaction(SIGPROF, &act, NULL);
ASSERT(r == 0);
}
void SignalHandler::Remove() {
// Ignore future SIGPROF signals because by default SIGPROF will terminate
// the process and we may have some signals in flight.
struct sigaction act = {};
act.sa_handler = SIG_IGN;
sigemptyset(&act.sa_mask);
int r = sigaction(SIGPROF, &act, NULL);
ASSERT(r == 0);
// Disable and delete alternative signal stack.
stack_t ss, old_ss;
ss.ss_flags = SS_DISABLE;
r = sigaltstack(&ss, &old_ss);
ASSERT(r == 0);
free(old_ss.ss_sp);
}
} // namespace dart
#endif // defined(HOST_OS_ANDROID)