ART: Add option to behave fatally on unmarked kThrow

Add a runtime option that makes the verifier abort when a runtime
exception is signaled for an instruction that isn't marked as such.

Bug: 121245951
Test: m test-art-host
Change-Id: Id953fa25fbcc12c1e6a7d74b30b28b81df57e427
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index afcf40d..fb1e6b7 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -371,6 +371,10 @@
           .WithType<bool>()
           .WithValueMap({{"true", true}, {"false", false}})
           .IntoKey(M::OpaqueJniIds)
+      .Define("-XX:VerifierMissingKThrowFatal=_")
+          .WithType<bool>()
+          .WithValueMap({{"false", false}, {"true", true}})
+          .IntoKey(M::VerifierMissingKThrowFatal)
       .Ignore({
           "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
           "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index fe64b8c..8d3fc4e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -294,7 +294,8 @@
       // Initially assume we perceive jank in case the process state is never updated.
       process_state_(kProcessStateJankPerceptible),
       zygote_no_threads_(false),
-      verifier_logging_threshold_ms_(100) {
+      verifier_logging_threshold_ms_(100),
+      verifier_missing_kthrow_fatal_(false) {
   static_assert(Runtime::kCalleeSaveSize ==
                     static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
   CheckConstants();
@@ -1149,6 +1150,8 @@
 
   MemMap::Init();
 
+  verifier_missing_kthrow_fatal_ = runtime_options.GetOrDefault(Opt::VerifierMissingKThrowFatal);
+
   // Try to reserve a dedicated fault page. This is allocated for clobbered registers and sentinels.
   // If we cannot reserve it, log a warning.
   // Note: We allocate this first to have a good chance of grabbing the page. The address (0xebad..)
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 0b880c9..82f7d57 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -895,6 +895,10 @@
     return image_space_loading_order_;
   }
 
+  bool IsVerifierMissingKThrowFatal() const {
+    return verifier_missing_kthrow_fatal_;
+  }
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -1240,6 +1244,8 @@
   gc::space::ImageSpaceLoadingOrder image_space_loading_order_ =
       gc::space::ImageSpaceLoadingOrder::kSystemFirst;
 
+  bool verifier_missing_kthrow_fatal_;
+
   // Note: See comments on GetFaultMessage.
   friend std::string GetFaultMessageForAbortLogging();
   friend class ScopedThreadPoolUsage;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index d2594b2..a00bb14 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -169,5 +169,6 @@
                      gc::space::ImageSpaceLoadingOrder::kSystemFirst)
 
 RUNTIME_OPTIONS_KEY (bool,                FastClassNotFoundException,     true)
+RUNTIME_OPTIONS_KEY (bool,                VerifierMissingKThrowFatal,     false)
 
 #undef RUNTIME_OPTIONS_KEY
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 744dc27..26de306 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -5570,6 +5570,10 @@
           if ((Instruction::FlagsOf(opcode) & Instruction::kThrow) == 0 &&
               !impl::IsCompatThrow(opcode) &&
               GetInstructionFlags(work_insn_idx_).IsInTry()) {
+            if (Runtime::Current()->IsVerifierMissingKThrowFatal()) {
+              LOG(FATAL) << "Unexpected throw: " << std::hex << work_insn_idx_ << " " << opcode;
+              UNREACHABLE();
+            }
             // We need to save the work_line if the instruction wasn't throwing before. Otherwise
             // we'll try to merge garbage.
             // Note: this assumes that Fail is called before we do any work_line modifications.