diff options
author | 2020-11-13 10:54:13 -0800 | |
---|---|---|
committer | 2020-11-16 23:09:16 +0000 | |
commit | cfc6e9d55263d49e2d8d19a4a3a7d38567bb4fb9 (patch) | |
tree | 65fa28b14ebe58f52eb5d00e8ebf6c270cbff216 | |
parent | 07b62e3d0808cc0890717a3a589c1f7b1ac51e5c (diff) |
Switch to the new kernel API for obtaining fault address tag bits.
The discussion on LKML is converging on v16 of the fault address tag
bits patch [1]. In this version of the patch the presence of the tag
bits in si_addr is controlled by a sa_flags bit, and a protocol is
introduced to allow userspace to detect kernel support for sa_flags
bits. Update the tombstone signal handler to use this API to read
the tag bits, update the interceptors in libsigchain to implement
the flag support detection protocol and hide the tag bits in si_addr
from chained signal handlers that did not request them to match the
kernel behavior.
[1] https://lore.kernel.org/linux-arm-kernel/cover.1605235762.git.pcc@google.com/
Change-Id: I57f24c07c01ceb3e5b81cfc15edf559ef7dfc740
-rw-r--r-- | sigchainlib/sigchain.cc | 59 | ||||
-rw-r--r-- | sigchainlib/sigchain_test.cc | 29 |
2 files changed, 87 insertions, 1 deletions
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index f58fe213b3..1935d454f8 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -22,6 +22,10 @@ #include <stdlib.h> #include <string.h> +#if defined(__BIONIC__) +#include <bionic/macros.h> +#endif + #include <algorithm> #include <initializer_list> #include <mutex> @@ -39,6 +43,9 @@ #define _XOPEN_SOURCE #endif +#define SA_UNSUPPORTED 0x00000400 +#define SA_EXPOSE_TAGBITS 0x00000800 + #include <ucontext.h> // libsigchain provides an interception layer for signal handlers, to allow ART and others to give @@ -204,13 +211,50 @@ class SignalChain { #endif handler_action.sa_sigaction = SignalChain::Handler; - handler_action.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; + handler_action.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK | + SA_UNSUPPORTED | SA_EXPOSE_TAGBITS; #if defined(__BIONIC__) linked_sigaction64(signo, &handler_action, &action_); + linked_sigaction64(signo, nullptr, &handler_action); #else linked_sigaction(signo, &handler_action, &action_); + linked_sigaction(signo, nullptr, &handler_action); +#endif + + // Newer kernels clear unknown flags from sigaction.sa_flags in order to + // allow userspace to determine which flag bits are supported. We use this + // behavior in turn to implement the same flag bit support detection + // protocol regardless of kernel version. Due to the lack of a flag bit + // support detection protocol in older kernels we assume support for a base + // set of flags that have been supported since at least 2003 [1]. No flags + // were introduced since then until the introduction of SA_EXPOSE_TAGBITS + // handled below. glibc headers do not define SA_RESTORER so we define it + // ourselves. + // + // TODO(pcc): The new kernel behavior has been implemented in a kernel + // patch [2] that has not yet landed. Update the code if necessary once it + // lands. + // + // [1] https://github.com/mpe/linux-fullhistory/commit/c0f806c86fc8b07ad426df023f1a4bb0e53c64f6 + // [2] https://lore.kernel.org/linux-arm-kernel/cover.1605235762.git.pcc@google.com/ +#if !defined(__BIONIC__) +#define SA_RESTORER 0x04000000 #endif + kernel_supported_flags_ = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO | + SA_ONSTACK | SA_RESTART | SA_NODEFER | + SA_RESETHAND | SA_RESTORER; + + // Determine whether the kernel supports SA_EXPOSE_TAGBITS. For newer + // kernels we use the flag support detection protocol described above. In + // order to allow userspace to distinguish old and new kernels, + // SA_UNSUPPORTED has been reserved as an unsupported flag. If the kernel + // did not clear it then we know that we have an old kernel that would not + // support SA_EXPOSE_TAGBITS anyway. + if (!(handler_action.sa_flags & SA_UNSUPPORTED) && + (handler_action.sa_flags & SA_EXPOSE_TAGBITS)) { + kernel_supported_flags_ |= SA_EXPOSE_TAGBITS; + } } template <typename SigactionType> @@ -244,6 +288,7 @@ class SignalChain { memcpy(&action_.sa_mask, &new_action->sa_mask, std::min(sizeof(action_.sa_mask), sizeof(new_action->sa_mask))); } + action_.sa_flags &= kernel_supported_flags_; } void AddSpecialHandler(SigchainAction* sa) { @@ -278,6 +323,7 @@ class SignalChain { private: bool claimed_; + int kernel_supported_flags_; #if defined(__BIONIC__) struct sigaction64 action_; #else @@ -342,6 +388,17 @@ void SignalChain::Handler(int signo, siginfo_t* siginfo, void* ucontext_raw) { #endif if ((handler_flags & SA_SIGINFO)) { + // If the chained handler is not expecting tag bits in the fault address, + // mask them out now. +#if defined(__BIONIC__) + if (!(handler_flags & SA_EXPOSE_TAGBITS) && + (signo == SIGILL || signo == SIGFPE || signo == SIGSEGV || + signo == SIGBUS || signo == SIGTRAP) && + siginfo->si_code > SI_USER && siginfo->si_code < SI_KERNEL && + !(signo == SIGTRAP && siginfo->si_code == TRAP_HWBKPT)) { + siginfo->si_addr = untag_address(siginfo->si_addr); + } +#endif chains[signo].action_.sa_sigaction(signo, siginfo, ucontext_raw); } else { auto handler = chains[signo].action_.sa_handler; diff --git a/sigchainlib/sigchain_test.cc b/sigchainlib/sigchain_test.cc index bb997877a1..4ef98c2b8f 100644 --- a/sigchainlib/sigchain_test.cc +++ b/sigchainlib/sigchain_test.cc @@ -238,3 +238,32 @@ TEST_F(SigchainTest, EnsureFrontOfChain) { ASSERT_EQ(1, called); called = 0; } + +TEST_F(SigchainTest, fault_address_tag) { +#define SA_EXPOSE_TAGBITS 0x00000800 +#if defined(__aarch64__) + struct sigaction action = {}; + action.sa_flags = SA_SIGINFO; + action.sa_sigaction = [](int, siginfo_t* siginfo, void*) { + _exit(reinterpret_cast<uintptr_t>(siginfo->si_addr) >> 56); + }; + ASSERT_EQ(0, sigaction(SIGSEGV, &action, nullptr)); + + auto* tagged_null = reinterpret_cast<int*>(0x2bULL << 56); + EXPECT_EXIT({ volatile int load __attribute__((unused)) = *tagged_null; }, + testing::ExitedWithCode(0), ""); + + // Our sigaction implementation always implements the "clear unknown bits" + // semantics for oldact.sa_flags regardless of kernel version so we rely on it + // here to test for kernel support for SA_EXPOSE_TAGBITS. + action.sa_flags = SA_SIGINFO | SA_EXPOSE_TAGBITS; + ASSERT_EQ(0, sigaction(SIGSEGV, &action, nullptr)); + ASSERT_EQ(0, sigaction(SIGSEGV, nullptr, &action)); + if (action.sa_flags & SA_EXPOSE_TAGBITS) { + EXPECT_EXIT({ volatile int load __attribute__((unused)) = *tagged_null; }, + testing::ExitedWithCode(0x2b), ""); + } +#else + GTEST_SKIP() << "arm64 only"; +#endif +} |