sigchain: fix EnsureFrontOfChain on 32-bit.

On 32-bit, bionic's sigaction forwards to sigaction64, which gets
interposed by libsigchain's sigaction64, which returns the user's
signal handler, not the actual one. This leads to EnsureFrontOfChain's
check for whether it should reregister the signal handler to falsely
return true, which leads to explosions.

Bug: http://b/112677822
Test: /data/nativetest/art/arm/art_sigchain_tests/sigchain_test
Test: /data/nativetest64/art/arm64/art_sigchain_tests/sigchain_test
Change-Id: Ifbaa4448580d763f5ffd575fa2e77113a8108de6
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 2e5f46c..cbc3ff8 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -490,8 +490,13 @@
   }
 
   // Read the current action without looking at the chain, it should be the expected action.
+#if defined(__BIONIC__)
+  struct sigaction64 current_action;
+  linked_sigaction64(signal, nullptr, &current_action);
+#else
   struct sigaction current_action;
   linked_sigaction(signal, nullptr, &current_action);
+#endif
 
   // If the sigactions don't match then we put the current action on the chain and make ourself as
   // the main action.
diff --git a/sigchainlib/sigchain_test.cc b/sigchainlib/sigchain_test.cc
index 9584ded..53e1e40 100644
--- a/sigchainlib/sigchain_test.cc
+++ b/sigchainlib/sigchain_test.cc
@@ -26,7 +26,8 @@
  * SUCH DAMAGE.
  */
 
-
+#include <dlfcn.h>
+#include <pthread.h>
 #include <signal.h>
 #include <sys/syscall.h>
 
@@ -63,10 +64,25 @@
   }
 
   art::SigchainAction action = {
-      .sc_sigaction = [](int, siginfo_t*, void*) { return true; },
+      .sc_sigaction = [](int, siginfo_t* info, void*) -> bool {
+        return info->si_value.sival_ptr;
+      },
       .sc_mask = {},
       .sc_flags = 0,
   };
+
+ protected:
+  void RaiseHandled() {
+      sigval_t value;
+      value.sival_ptr = &value;
+      pthread_sigqueue(pthread_self(), SIGSEGV, value);
+  }
+
+  void RaiseUnhandled() {
+      sigval_t value;
+      value.sival_ptr = nullptr;
+      pthread_sigqueue(pthread_self(), SIGSEGV, value);
+  }
 };
 
 
@@ -185,3 +201,40 @@
 }
 
 #endif
+
+// Make sure that we properly put ourselves back in front if we get circumvented.
+TEST_F(SigchainTest, EnsureFrontOfChain) {
+#if defined(__BIONIC__)
+  constexpr char kLibcSoName[] = "libc.so";
+#elif defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ == 6
+  constexpr char kLibcSoName[] = "libc.so.6";
+#else
+  #error Unknown libc
+#endif
+  void* libc = dlopen(kLibcSoName, RTLD_LAZY | RTLD_NOLOAD);
+  ASSERT_TRUE(libc);
+
+  static sig_atomic_t called = 0;
+  struct sigaction action = {};
+  action.sa_flags = SA_SIGINFO;
+  action.sa_sigaction = [](int, siginfo_t*, void*) { called = 1; };
+
+  ASSERT_EQ(0, sigaction(SIGSEGV, &action, nullptr));
+
+  // Try before EnsureFrontOfChain.
+  RaiseHandled();
+  ASSERT_EQ(0, called);
+
+  RaiseUnhandled();
+  ASSERT_EQ(1, called);
+  called = 0;
+
+  // ...and after.
+  art::EnsureFrontOfChain(SIGSEGV);
+  ASSERT_EQ(0, called);
+  called = 0;
+
+  RaiseUnhandled();
+  ASSERT_EQ(1, called);
+  called = 0;
+}