Use bitmap to maintain which signal is being handled
The current approach of using a flag for all signals restricts from
having nested loops of different types. For instance, if ART has SISEGV
and SIGBUS handlers installed and the former raises the latter, it
should be allowed but isn't currently.
This CL replaces the flag with a bitmap, one bit per signal. This way we
only skip the same signal type as being handled.
Bug: 160737021
Test: art/test/testrunner/testrunner.py --host -t 2045-uffd
with https://android-review.git.corp.google.com/c/platform/art/+/2411972
Change-Id: I25250e3f83f6258be64b83d9661c89cf2e26d2b5
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 208f7ed..4b8b468 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -27,6 +27,8 @@
#endif
#include <algorithm>
+#include <atomic>
+#include <bitset>
#include <initializer_list>
#include <mutex>
#include <type_traits>
@@ -151,11 +153,17 @@
});
}
+// Use a bitmap to indicate which signal is being handled so that other
+// non-blocked signals are allowed to be handled, if raised.
+static constexpr size_t kSignalSetLength = _NSIG - 1;
+using KeyValueType = std::bitset<kSignalSetLength>;
static pthread_key_t GetHandlingSignalKey() {
static pthread_key_t key;
static std::once_flag once;
std::call_once(once, []() {
- int rc = pthread_key_create(&key, nullptr);
+ int rc = pthread_key_create(&key, [](void* ptr) {
+ delete reinterpret_cast<KeyValueType*>(ptr);
+ });
if (rc != 0) {
fatal("failed to create sigchain pthread key: %s", strerror(rc));
}
@@ -164,25 +172,59 @@
}
static bool GetHandlingSignal() {
- void* result = pthread_getspecific(GetHandlingSignalKey());
- return reinterpret_cast<uintptr_t>(result);
+ pthread_key_t key = GetHandlingSignalKey();
+ void* result = pthread_getspecific(key);
+ if (result == nullptr) {
+ result = new KeyValueType();
+ pthread_setspecific(key, result);
+ return false;
+ }
+ return reinterpret_cast<KeyValueType*>(result)->any();
}
-static void SetHandlingSignal(bool value) {
- pthread_setspecific(GetHandlingSignalKey(),
- reinterpret_cast<void*>(static_cast<uintptr_t>(value)));
+static bool GetHandlingSignal(int signo) {
+ pthread_key_t key = GetHandlingSignalKey();
+ void* result = pthread_getspecific(key);
+ if (result == nullptr) {
+ result = new KeyValueType();
+ pthread_setspecific(key, result);
+ return false;
+ }
+ return reinterpret_cast<KeyValueType*>(result)->test(signo - 1);
+}
+
+static bool SetHandlingSignal(int signo, bool value) {
+ // Use signe-fence to ensure that compiler doesn't reorder generated code
+ // across signal handlers.
+ pthread_key_t key = GetHandlingSignalKey();
+ std::atomic_signal_fence(std::memory_order_seq_cst);
+ void* result = pthread_getspecific(key);
+ size_t idx = signo - 1;
+ bool old;
+ if (result == nullptr) {
+ result = new KeyValueType();
+ pthread_setspecific(key, result);
+ old = false;
+ } else {
+ old = reinterpret_cast<KeyValueType*>(result)->test(idx);
+ }
+ reinterpret_cast<KeyValueType*>(result)->set(idx, value);
+ std::atomic_signal_fence(std::memory_order_seq_cst);
+ return old;
}
class ScopedHandlingSignal {
public:
- ScopedHandlingSignal() : original_value_(GetHandlingSignal()) {
- }
+ ScopedHandlingSignal(int signo, bool set)
+ : signo_(signo),
+ original_value_(set ? SetHandlingSignal(signo, true) : GetHandlingSignal(signo)) {}
~ScopedHandlingSignal() {
- SetHandlingSignal(original_value_);
+ SetHandlingSignal(signo_, original_value_);
}
private:
+ int signo_;
bool original_value_;
};
@@ -336,7 +378,7 @@
// _NSIG is 1 greater than the highest valued signal, but signals start from 1.
// Leave an empty element at index 0 for convenience.
-static SignalChain chains[_NSIG + 1];
+static SignalChain chains[_NSIG];
static bool is_signal_hook_debuggable = false;
@@ -349,7 +391,7 @@
void SignalChain::Handler(int signo, siginfo_t* siginfo, void* ucontext_raw) {
// Try the special handlers first.
// If one of them crashes, we'll reenter this handler and pass that crash onto the user handler.
- if (!GetHandlingSignal()) {
+ if (!GetHandlingSignal(signo)) {
for (const auto& handler : chains[signo].special_handlers_) {
if (handler.sc_sigaction == nullptr) {
break;
@@ -362,10 +404,7 @@
sigset_t previous_mask;
linked_sigprocmask(SIG_SETMASK, &handler.sc_mask, &previous_mask);
- ScopedHandlingSignal restorer;
- if (!handler_noreturn) {
- SetHandlingSignal(true);
- }
+ ScopedHandlingSignal restorer(signo, !handler_noreturn);
if (handler.sc_sigaction(signo, siginfo, ucontext_raw)) {
return;