summaryrefslogtreecommitdiffstats
path: root/runtime/fault_handler.cc
diff options
context:
space:
mode:
authorDave Allison <dallison@google.com>2014-02-20 16:06:36 -0800
committerDave Allison <dallison@google.com>2014-03-13 12:21:15 -0700
commitb373e091eac39b1a79c11f2dcbd610af01e9e8a9 (patch)
tree034d820c4829e0dcf6161473cc39f7250123bfaa /runtime/fault_handler.cc
parent9545a446e99b22248099fe66f5f9431530c20851 (diff)
downloadart-b373e091eac39b1a79c11f2dcbd610af01e9e8a9.tar.gz
art-b373e091eac39b1a79c11f2dcbd610af01e9e8a9.tar.bz2
art-b373e091eac39b1a79c11f2dcbd610af01e9e8a9.zip
Implicit null/suspend checks (oat version bump)
This adds the ability to use SEGV signals to throw NullPointerException exceptions from Java code rather than having the compiler generate explicit comparisons and branches. It does this by using sigaction to trap SIGSEGV and when triggered makes sure it's in compiled code and if so, sets the return address to the entry point to throw the exception. It also uses this signal mechanism to determine whether to check for thread suspension. Instead of the compiler generating calls to a function to check for threads being suspended, the compiler will now load indirect via an address in the TLS area. To trigger a suspend, the contents of this address are changed from something valid to 0. A SIGSEGV will occur and the handler will check for a valid instruction pattern before invoking the thread suspension check code. If a user program taps SIGSEGV it will prevent our signal handler working. This will cause a failure in the runtime. There are two signal handlers at present. You can control them individually using the flags -implicit-checks: on the runtime command line. This takes a string parameter, a comma separated set of strings. Each can be one of: none switch off null null pointer checks suspend suspend checks all all checks So to switch only suspend checks on, pass: -implicit-checks:suspend There is also -explicit-checks to provide the reverse once we change the default. For dalvikvm, pass --runtime-arg -implicit-checks:foo,bar The default is -implicit-checks:none There is also a property 'dalvik.vm.implicit_checks' whose value is the same string as the command option. The default is 'none'. For example to switch on null checks using the option: setprop dalvik.vm.implicit_checks null It only works for ARM right now. Bumps OAT version number due to change to Thread offsets. Bug: 13121132 Change-Id: If743849138162f3c7c44a523247e413785677370
Diffstat (limited to 'runtime/fault_handler.cc')
-rw-r--r--runtime/fault_handler.cc181
1 files changed, 181 insertions, 0 deletions
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
new file mode 100644
index 0000000000..6399c0d321
--- /dev/null
+++ b/runtime/fault_handler.cc
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2008 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 "fault_handler.h"
+#include <sys/mman.h>
+#include <sys/ucontext.h>
+#include "base/macros.h"
+#include "globals.h"
+#include "base/logging.h"
+#include "base/hex_dump.h"
+#include "thread.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/dex_cache.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/object-inl.h"
+#include "object_utils.h"
+#include "scoped_thread_state_change.h"
+#include "verify_object-inl.h"
+
+namespace art {
+// Static fault manger object accessed by signal handler.
+FaultManager fault_manager;
+
+// Signal handler called on SIGSEGV.
+static void art_fault_handler(int sig, siginfo_t* info, void* context) {
+ fault_manager.HandleFault(sig, info, context);
+}
+
+FaultManager::FaultManager() {
+ sigaction(SIGSEGV, nullptr, &oldaction_);
+}
+
+FaultManager::~FaultManager() {
+ sigaction(SIGSEGV, &oldaction_, nullptr); // Restore old handler.
+}
+
+void FaultManager::Init() {
+ struct sigaction action;
+ action.sa_sigaction = art_fault_handler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+ action.sa_restorer = nullptr;
+ sigaction(SIGSEGV, &action, &oldaction_);
+}
+
+void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
+ bool handled = false;
+ if (IsInGeneratedCode(context)) {
+ for (auto& handler : handlers_) {
+ handled = handler->Action(sig, info, context);
+ if (handled) {
+ return;
+ }
+ }
+ }
+
+ if (!handled) {
+ LOG(INFO)<< "Caught unknown SIGSEGV in ART fault handler";
+ oldaction_.sa_sigaction(sig, info, context);
+ }
+}
+
+void FaultManager::AddHandler(FaultHandler* handler) {
+ handlers_.push_back(handler);
+}
+
+void FaultManager::RemoveHandler(FaultHandler* handler) {
+ for (Handlers::iterator i = handlers_.begin(); i != handlers_.end(); ++i) {
+ FaultHandler* h = *i;
+ if (h == handler) {
+ handlers_.erase(i);
+ return;
+ }
+ }
+}
+
+
+// This function is called within the signal handler. It checks that
+// the mutator_lock is held (shared). No annotalysis is done.
+bool FaultManager::IsInGeneratedCode(void *context) {
+ // We can only be running Java code in the current thread if it
+ // is in Runnable state.
+ Thread* thread = Thread::Current();
+ if (thread == nullptr) {
+ return false;
+ }
+
+ ThreadState state = thread->GetState();
+ if (state != kRunnable) {
+ return false;
+ }
+
+ // Current thread is runnable.
+ // Make sure it has the mutator lock.
+ if (!Locks::mutator_lock_->IsSharedHeld(thread)) {
+ return false;
+ }
+
+ uintptr_t potential_method = 0;
+ uintptr_t return_pc = 0;
+
+ // Get the architecture specific method address and return address. These
+ // are in architecture specific files in arch/<arch>/fault_handler_<arch>.cc
+ GetMethodAndReturnPC(context, /*out*/potential_method, /*out*/return_pc);
+
+ // If we don't have a potential method, we're outta here.
+ if (potential_method == 0) {
+ return false;
+ }
+
+ // Verify that the potential method is indeed a method.
+ // TODO: check the GC maps to make sure it's an object.
+
+ mirror::Object* method_obj =
+ reinterpret_cast<mirror::Object*>(potential_method);
+
+ // Check that the class pointer inside the object is not null and is aligned.
+ mirror::Class* cls = method_obj->GetClass<kVerifyNone>();
+ if (cls == nullptr) {
+ return false;
+ }
+ if (!IsAligned<kObjectAlignment>(cls)) {
+ return false;
+ }
+
+
+ if (!VerifyClassClass(cls)) {
+ return false;
+ }
+
+ // Now make sure the class is a mirror::ArtMethod.
+ if (!cls->IsArtMethodClass()) {
+ return false;
+ }
+
+ // We can be certain that this is a method now. Check if we have a GC map
+ // at the return PC address.
+ mirror::ArtMethod* method =
+ reinterpret_cast<mirror::ArtMethod*>(potential_method);
+ return method->ToDexPc(return_pc, false) != DexFile::kDexNoIndex;
+}
+
+//
+// Null pointer fault handler
+//
+
+NullPointerHandler::NullPointerHandler(FaultManager* manager) {
+ manager->AddHandler(this);
+}
+
+//
+// Suspension fault handler
+//
+
+SuspensionHandler::SuspensionHandler(FaultManager* manager) {
+ manager->AddHandler(this);
+}
+
+//
+// Stack overflow fault handler
+//
+
+StackOverflowHandler::StackOverflowHandler(FaultManager* manager) {
+ manager->AddHandler(this);
+}
+} // namespace art
+