Work around a kernel bug on Android.

Kernel does not clear If-Then execution state bits when entering ARM signal handler which violates requirements imposed by ARM architecture reference. Some CPUs look at these bits even while in ARM mode which causes them
to skip some instructions in the prologue of the signal handler.

To work around the issue we insert enough NOPs in the prologue to ensure that no actual instructions are skipped and then branch to the actual signal handler.

For the kernel patch that fixes the issue see: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6ecf830e5029598732e04067e325d946097519cb

This causes sporadic crashes with SIGILL on some testing devices (e.g. Nexus 7).

R=fschneider@google.com
BUG=

Review URL: https://codereview.chromium.org/1940883002 .
This commit is contained in:
Vyacheslav Egorov 2016-05-02 19:12:48 +02:00
parent fcf97b6c3d
commit b089d4f004
8 changed files with 73 additions and 9 deletions

View file

@ -36,6 +36,17 @@ struct sigset_t {
};
#endif
// Old linux kernels on ARM might require a trampoline to
// work around incorrect Thumb -> ARM transitions. See SignalHandlerTrampoline
// below for more details.
#if defined(HOST_ARCH_ARM) && \
(defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID)) && \
!defined(__thumb__)
#define USE_SIGNAL_HANDLER_TRAMPOLINE
#endif
namespace dart {
typedef void (*SignalAction)(int signal, siginfo_t* info,
@ -43,17 +54,68 @@ typedef void (*SignalAction)(int signal, siginfo_t* info,
class SignalHandler : public AllStatic {
public:
static void Install(SignalAction action);
template<SignalAction action>
static void Install() {
#if defined(USE_SIGNAL_HANDLER_TRAMPOLINE)
InstallImpl(SignalHandlerTrampoline<action>);
#else
InstallImpl(action);
#endif // defined(USE_SIGNAL_HANDLER_TRAMPOLINE)
}
static void Remove();
static uintptr_t GetProgramCounter(const mcontext_t& mcontext);
static uintptr_t GetFramePointer(const mcontext_t& mcontext);
static uintptr_t GetCStackPointer(const mcontext_t& mcontext);
static uintptr_t GetDartStackPointer(const mcontext_t& mcontext);
static uintptr_t GetLinkRegister(const mcontext_t& mcontext);
private:
static void InstallImpl(SignalAction action);
#if defined(USE_SIGNAL_HANDLER_TRAMPOLINE)
// Work around for a bug in old kernels (only fixed in 3.18 Android kernel):
//
// Kernel does not clear If-Then execution state bits when entering ARM signal
// handler which violates requirements imposed by ARM architecture reference.
// Some CPUs look at these bits even while in ARM mode which causes them
// to skip some instructions in the prologue of the signal handler.
//
// To work around the issue we insert enough NOPs in the prologue to ensure
// that no actual instructions are skipped and then branch to the actual
// signal handler.
//
// For the kernel patch that fixes the issue see: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6ecf830e5029598732e04067e325d946097519cb
//
// Note: this function is marked "naked" because we must guarantee that
// our NOPs occur before any compiler generated prologue.
template <SignalAction action>
static __attribute__((naked)) void SignalHandlerTrampoline(int signal,
siginfo_t* info,
void* context_) {
// IT (If-Then) instruction makes up to four instructions that follow it
// conditional.
asm volatile("nop; nop; nop; nop" : : : "memory");
// Tail-call into the actual signal handler.
// Note: this code is split into a separate inline assembly block because
// any code that compiler generates to satisfy register constraints must
// be generated after four NOPs.
register int arg0 asm("r0") = signal;
register siginfo_t* arg1 asm("r1") = info;
register void* arg2 asm("r2") = context_;
asm volatile("bx %3"
:
: "r"(arg0), "r"(arg1), "r"(arg2),
"r"(action)
: "memory");
}
#endif // defined(USE_SIGNAL_HANDLER_TRAMPOLINE)
};
#undef USE_SIGNAL_HANDLER_TRAMPOLINE
} // namespace dart
#endif // VM_SIGNAL_HANDLER_H_

View file

@ -100,7 +100,7 @@ uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) {
}
void SignalHandler::Install(SignalAction action) {
void SignalHandler::InstallImpl(SignalAction action) {
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_sigaction = action;

View file

@ -99,7 +99,7 @@ uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) {
}
void SignalHandler::Install(SignalAction action) {
void SignalHandler::InstallImpl(SignalAction action) {
struct sigaction act;
act.sa_handler = NULL;
act.sa_sigaction = action;

View file

@ -102,7 +102,7 @@ uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) {
}
void SignalHandler::Install(SignalAction action) {
void SignalHandler::InstallImpl(SignalAction action) {
struct sigaction act;
act.sa_handler = NULL;
act.sa_sigaction = action;

View file

@ -38,7 +38,7 @@ uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) {
}
void SignalHandler::Install(SignalAction action) {
void SignalHandler::InstallImpl(SignalAction action) {
UNIMPLEMENTED();
}

View file

@ -57,8 +57,8 @@ void ThreadInterrupter::InterruptThread(OSThread* thread) {
void ThreadInterrupter::InstallSignalHandler() {
SignalHandler::Install(
ThreadInterrupterAndroid::ThreadInterruptSignalHandler);
SignalHandler::Install<
ThreadInterrupterAndroid::ThreadInterruptSignalHandler>();
}

View file

@ -54,7 +54,8 @@ void ThreadInterrupter::InterruptThread(OSThread* thread) {
void ThreadInterrupter::InstallSignalHandler() {
SignalHandler::Install(ThreadInterrupterLinux::ThreadInterruptSignalHandler);
SignalHandler::Install<
ThreadInterrupterLinux::ThreadInterruptSignalHandler>();
}

View file

@ -55,7 +55,8 @@ void ThreadInterrupter::InterruptThread(OSThread* thread) {
void ThreadInterrupter::InstallSignalHandler() {
SignalHandler::Install(ThreadInterrupterMacOS::ThreadInterruptSignalHandler);
SignalHandler::Install<
ThreadInterrupterMacOS::ThreadInterruptSignalHandler>();
}