Make use of sigchainlib for signal chaining for implicit checks

This adds a preload library that overrides sigaction and sigprocmask
to implement signal chaining.  Signal chaining allows us to chain
any signal so that the ART runtime receives it before any signal
handler registered in native code by an application.  If the
ART signal handler doesn't want it, it will pass it on to the
user's handler.

ART uses signals for null pointer checks, stack overflow checks and
suspend points.

Also adds an OAT test to test this in isolation.

Change-Id: I9545f9f7343774c091410eb810504d9855fd399f
diff --git a/Android.mk b/Android.mk
index b329a5a..a30c090 100644
--- a/Android.mk
+++ b/Android.mk
@@ -90,6 +90,8 @@
 include $(art_path)/dalvikvm/Android.mk
 include $(art_path)/tools/Android.mk
 include $(art_build_path)/Android.oat.mk
+include $(art_path)/sigchainlib/Android.mk
+
 
 
 
diff --git a/build/Android.libarttest.mk b/build/Android.libarttest.mk
index 9e5f3d6..c080928 100644
--- a/build/Android.libarttest.mk
+++ b/build/Android.libarttest.mk
@@ -16,6 +16,7 @@
 
 LIBARTTEST_COMMON_SRC_FILES := \
 	test/JniTest/jni_test.cc \
+	test/SignalTest/signaltest.cc \
 	test/ReferenceMap/stack_walk_refmap_jni.cc \
 	test/StackWalk/stack_walk_jni.cc \
 	test/UnsafeTest/unsafe_test.cc
diff --git a/runtime/Android.mk b/runtime/Android.mk
index c2507b1..a0648b0 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -402,11 +402,13 @@
     endif
   endif
   LOCAL_C_INCLUDES += $(ART_C_INCLUDES)
+  LOCAL_C_INCLUDES += art/sigchainlib
+
   LOCAL_SHARED_LIBRARIES += liblog libnativehelper
   include external/libcxx/libcxx.mk
   LOCAL_SHARED_LIBRARIES += libbacktrace_libc++
   ifeq ($$(art_target_or_host),target)
-    LOCAL_SHARED_LIBRARIES += libcutils libdl libselinux libutils
+    LOCAL_SHARED_LIBRARIES += libcutils libdl libselinux libutils libsigchain
     LOCAL_STATIC_LIBRARIES := libziparchive libz
   else # host
     LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz libutils
@@ -459,3 +461,4 @@
 ifeq ($(ART_BUILD_TARGET_DEBUG),true)
   $(eval $(call build-libart,target,debug,$(ART_TARGET_CLANG)))
 endif
+
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 8d750c5..15c38c1 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -29,12 +29,22 @@
 #include "mirror/object-inl.h"
 #include "object_utils.h"
 #include "scoped_thread_state_change.h"
+#ifdef HAVE_ANDROID_OS
+#include "sigchain.h"
+#endif
 #include "verify_object-inl.h"
 
 namespace art {
 // Static fault manger object accessed by signal handler.
 FaultManager fault_manager;
 
+extern "C" {
+void art_sigsegv_fault() {
+  // Set a breakpoint here to be informed when a SIGSEGV is unhandled by ART.
+  VLOG(signals)<< "Caught unknown SIGSEGV in ART fault handler - chaining to next handler.";
+}
+}
+
 // Signal handler called on SIGSEGV.
 static void art_fault_handler(int sig, siginfo_t* info, void* context) {
   fault_manager.HandleFault(sig, info, context);
@@ -45,9 +55,13 @@
 }
 
 FaultManager::~FaultManager() {
+#ifdef HAVE_ANDROID_OS
+  UnclaimSignalChain(SIGSEGV);
+#endif
   sigaction(SIGSEGV, &oldaction_, nullptr);   // Restore old handler.
 }
 
+
 void FaultManager::Init() {
   struct sigaction action;
   action.sa_sigaction = art_fault_handler;
@@ -56,7 +70,13 @@
 #if !defined(__mips__)
   action.sa_restorer = nullptr;
 #endif
+
+  // Set our signal handler now.
   sigaction(SIGSEGV, &action, &oldaction_);
+#ifdef HAVE_ANDROID_OS
+  // Make sure our signal handler is called before any user handlers.
+  ClaimSignalChain(SIGSEGV, &oldaction_);
+#endif
 }
 
 void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
@@ -79,8 +99,13 @@
       return;
     }
   }
-  VLOG(signals)<< "Caught unknown SIGSEGV in ART fault handler";
+  art_sigsegv_fault();
+
+#ifdef HAVE_ANDROID_OS
+  InvokeUserSignalHandler(sig, info, context);
+#else
   oldaction_.sa_sigaction(sig, info, context);
+#endif
 }
 
 void FaultManager::AddHandler(FaultHandler* handler, bool generated_code) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 23a49cb..361070c 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -562,9 +562,20 @@
     GetInstrumentation()->ForceInterpretOnly();
   }
 
-  if (options->explicit_checks_ != (ParsedOptions::kExplicitSuspendCheck |
-        ParsedOptions::kExplicitNullCheck |
-        ParsedOptions::kExplicitStackOverflowCheck) || kEnableJavaStackTraceHandler) {
+  bool implicit_checks_supported = false;
+  switch (kRuntimeISA) {
+    case kArm:
+    case kThumb2:
+      implicit_checks_supported = true;
+      break;
+    default:
+      break;
+  }
+
+  if (implicit_checks_supported &&
+      (options->explicit_checks_ != (ParsedOptions::kExplicitSuspendCheck |
+          ParsedOptions::kExplicitNullCheck |
+          ParsedOptions::kExplicitStackOverflowCheck) || kEnableJavaStackTraceHandler)) {
     fault_manager.Init();
 
     // These need to be in a specific order.  The null point check handler must be
diff --git a/runtime/thread.h b/runtime/thread.h
index 9a7cb48..08bbcae 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -121,7 +121,7 @@
   // of the stack (lowest memory).  The higher portion of the memory
   // is protected against reads and the lower is available for use while
   // throwing the StackOverflow exception.
-  static constexpr size_t kStackOverflowProtectedSize = 32 * KB;
+  static constexpr size_t kStackOverflowProtectedSize = 16 * KB;
   static constexpr size_t kStackOverflowImplicitCheckSize = kStackOverflowProtectedSize +
     kStackOverflowReservedBytes;
 
diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk
new file mode 100644
index 0000000..cb1778d
--- /dev/null
+++ b/sigchainlib/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include art/build/Android.common.mk
+
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
+LOCAL_SRC_FILES := sigchain.cc
+LOCAL_MODULE:= libsigchain
+LOCAL_SHARED_LIBRARIES += liblog libdl
+LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+include $(BUILD_SHARED_LIBRARY)
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
new file mode 100644
index 0000000..26e7d31
--- /dev/null
+++ b/sigchainlib/sigchain.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/log.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace art {
+
+class SignalAction {
+ public:
+  SignalAction() : claimed_(false) {
+  }
+
+  // Claim the signal and keep the action specified.
+  void Claim(const struct sigaction& action) {
+    action_ = action;
+    claimed_ = true;
+  }
+
+  // Unclaim the signal and restore the old action.
+  void Unclaim(int signal) {
+    claimed_ = false;
+    sigaction(signal, &action_, NULL);        // Restore old action.
+  }
+
+  // Get the action associated with this signal.
+  const struct sigaction& GetAction() const {
+    return action_;
+  }
+
+  // Is the signal claimed?
+  bool IsClaimed() const {
+    return claimed_;
+  }
+
+  // Change the recorded action to that specified.
+  void SetAction(const struct sigaction& action) {
+    action_ = action;
+  }
+
+ private:
+  struct sigaction action_;     // Action to be performed.
+  bool claimed_;                // Whether signal is claimed or not.
+};
+
+// User's signal handlers
+static SignalAction user_sigactions[_NSIG];
+
+static void log(const char* format, ...) {
+  char buf[256];
+  va_list ap;
+  va_start(ap, format);
+  vsnprintf(buf, sizeof(buf), format, ap);
+  __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
+  va_end(ap);
+}
+
+static void CheckSignalValid(int signal) {
+  if (signal <= 0 || signal >= _NSIG) {
+    log("Invalid signal %d", signal);
+    abort();
+  }
+}
+
+// Claim a signal chain for a particular signal.
+void ClaimSignalChain(int signal, struct sigaction* oldaction) {
+  CheckSignalValid(signal);
+  user_sigactions[signal].Claim(*oldaction);
+}
+
+void UnclaimSignalChain(int signal) {
+  CheckSignalValid(signal);
+
+  user_sigactions[signal].Unclaim(signal);
+}
+
+// Invoke the user's signal handler.
+void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
+  // Check the arguments.
+  CheckSignalValid(sig);
+
+  // The signal must have been claimed in order to get here.  Check it.
+  if (!user_sigactions[sig].IsClaimed()) {
+    abort();
+  }
+
+  const struct sigaction& action = user_sigactions[sig].GetAction();
+
+  // Only deliver the signal if the signal was not masked out.
+  if (sigismember(&action.sa_mask, sig)) {
+     return;
+  }
+  if ((action.sa_flags & SA_SIGINFO) == 0) {
+    if (action.sa_handler != NULL) {
+      action.sa_handler(sig);
+    }
+  } else {
+    if (action.sa_sigaction != NULL) {
+      action.sa_sigaction(sig, info, context);
+    }
+  }
+}
+
+extern "C" {
+// These functions are C linkage since they replace the functions in libc.
+
+int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
+  // If this signal has been claimed as a signal chain, record the user's
+  // action but don't pass it on to the kernel.
+  // Note that we check that the signal number is in range here.  An out of range signal
+  // number should behave exactly as the libc sigaction.
+  if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
+    if (old_action != NULL) {
+      *old_action = user_sigactions[signal].GetAction();
+    }
+    if (new_action != NULL) {
+      user_sigactions[signal].SetAction(*new_action);
+    }
+    return 0;
+  }
+
+  // Will only get here if the signal chain has not been claimed.  We want
+  // to pass the sigaction on to the kernel via the real sigaction in libc.
+
+  void* linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
+  if (linked_sigaction_sym == nullptr) {
+    log("Unable to find next sigaction in signal chain");
+    abort();
+  }
+
+  typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
+  SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
+  return linked_sigaction(signal, new_action, old_action);
+}
+
+
+int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
+  const sigset_t* new_set_ptr = bionic_new_set;
+  sigset_t tmpset;
+  if (bionic_new_set != NULL) {
+    tmpset = *bionic_new_set;
+
+    if (how == SIG_BLOCK) {
+      // Don't allow claimed signals in the mask.  If a signal chain has been claimed
+      // we can't allow the user to block that signal.
+      for (int i = 0 ; i < _NSIG; ++i) {
+        if (user_sigactions[i].IsClaimed() && sigismember(&tmpset, i)) {
+            sigdelset(&tmpset, i);
+        }
+      }
+    }
+    new_set_ptr = &tmpset;
+  }
+
+  void* linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
+  if (linked_sigprocmask_sym == nullptr) {
+    log("Unable to find next sigprocmask in signal chain");
+    abort();
+  }
+
+  typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
+  SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
+  return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
+}
+}   // extern "C"
+}   // namespace art
+
diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h
new file mode 100644
index 0000000..f6f2253
--- /dev/null
+++ b/sigchainlib/sigchain.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SIGCHAINLIB_SIGCHAIN_H_
+#define ART_SIGCHAINLIB_SIGCHAIN_H_
+
+#include <signal.h>
+namespace art {
+
+void ClaimSignalChain(int signal, struct sigaction* oldaction);
+void UnclaimSignalChain(int signal);
+void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context);
+
+}   // namespace art
+
+#endif  // ART_SIGCHAINLIB_SIGCHAIN_H_
diff --git a/test/Android.mk b/test/Android.mk
index 8caa033..c15259c 100644
--- a/test/Android.mk
+++ b/test/Android.mk
@@ -46,6 +46,7 @@
 	HelloWorld \
 	InterfaceTest \
 	JniTest \
+	SignalTest \
 	NativeAllocations \
 	ParallelGC \
 	ReferenceMap \
diff --git a/test/SignalTest/SignalTest.java b/test/SignalTest/SignalTest.java
new file mode 100644
index 0000000..7f15aea
--- /dev/null
+++ b/test/SignalTest/SignalTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class SignalTest {
+    private static native void initSignalTest();
+    private static native void terminateSignalTest();
+    private static native int testSignal();
+
+    private static void stackOverflow() {
+       stackOverflow();
+    }
+
+    public static void main(String[] args) {
+        System.loadLibrary("arttest");
+
+        System.out.println("init signal test");
+        initSignalTest();
+        try {
+            Object o = null;
+            int hash = o.hashCode();
+
+            // Should never get here.
+            System.out.println("hash: " + hash);
+            throw new AssertionError();
+        } catch (NullPointerException e) {
+            System.out.println("Caught NullPointerException");
+        }
+        try {
+            stackOverflow();
+
+            // Should never get here.
+            throw new AssertionError();
+        } catch (StackOverflowError e) {
+            System.out.println("Caught StackOverflowError");
+        }
+
+        // Test that a signal in native code works.  This will return
+        // the value 1234 if the signal is caught.
+        int x = testSignal();
+        if (x != 1234) {
+            throw new AssertionError();
+        }
+
+        terminateSignalTest();
+        System.out.println("Signal test OK");
+    }
+}
diff --git a/test/SignalTest/signaltest.cc b/test/SignalTest/signaltest.cc
new file mode 100644
index 0000000..b84e395
--- /dev/null
+++ b/test/SignalTest/signaltest.cc
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "jni.h"
+
+#ifdef __arm__
+#include <sys/ucontext.h>
+#endif
+
+static void signalhandler(int sig, siginfo_t* info, void* context) {
+  printf("signal caught\n");
+#ifdef __arm__
+  // On ARM we do a more exhaustive test to make sure the signal
+  // context is OK.
+  // We can do this because we know that the instruction causing
+  // the signal is 2 bytes long (thumb mov instruction).  On
+  // other architectures this is more difficult.
+  // TODO: we could do this on other architectures too if necessary, it's just harder.
+  struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
+  struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+  sc->arm_pc += 2;          // Skip instruction causing segv.
+#endif
+}
+
+static struct sigaction oldaction;
+
+extern "C" JNIEXPORT void JNICALL Java_SignalTest_initSignalTest(JNIEnv*, jclass) {
+  struct sigaction action;
+  action.sa_sigaction = signalhandler;
+  sigemptyset(&action.sa_mask);
+  action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+#if !defined(__mips__)
+  action.sa_restorer = nullptr;
+#endif
+
+  sigaction(SIGSEGV, &action, &oldaction);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_SignalTest_terminateSignalTest(JNIEnv*, jclass) {
+  sigaction(SIGSEGV, &oldaction, nullptr);
+}
+
+// Prevent the compiler being a smart-alec and optimizing out the assignment
+// to nullptr.
+char *p = nullptr;
+
+extern "C" JNIEXPORT jint JNICALL Java_SignalTest_testSignal(JNIEnv*, jclass) {
+#ifdef __arm__
+  // On ARM we cause a real SEGV.
+  *p = 'a';
+#else
+  // On other architectures we simulate SEGV.
+  kill(getpid(), SIGSEGV);
+#endif
+  return 1234;
+}
+