diff options
author | 2019-05-20 10:04:44 -0700 | |
---|---|---|
committer | 2019-05-22 00:29:38 +0000 | |
commit | e48fd0b4780efadc6b3433fe7a56aa5be2a84325 (patch) | |
tree | 5056001e02d8c4494b643c8d53fd32621e127c1c /runtime | |
parent | abdb4592fa28d6e75f1160f01cde58ad7c3fef37 (diff) |
Add verifier fallback for JVMTI Get/SetLocalVariable
The JVMTI Get/SetLocalVariable functions used to rely entirely on the
Dex DebugInfo to determine the types of each of the registers. This
could lead to problems since, to prevent possible stack corruption, we
would not allow stack modification if the data was not present.
In order to remove this restriction we will instead make use of the
method verifier to ensure the modification is sensible when the
DebugInfo is not present. Since reconstructing this information using
the verifier is quite slow (compared to reading it from a table) we
will only do this when the table is missing.
Since the verifier lacks some of the information available when
creating the DebugLocalInfo table some semantics will change depending
on if the table is present or not.
- When the DebugLocalInfo table is not present we cannot always
distinguish between floats, ints, and other single-register
primitive types. For simplicity all single-register primitive
types can be modified and read by both the Float and Int versions
of the local variable functions.
- Similarly we cannot always distinguish between long and double
variables.
- Reference types are checked against what the verifier thinks they
need to be according to type unification. This might be more or
less specific than the types recorded in the functions source code.
- Constant int/float '0' values and 'null' cannot always be
differentiated by the verifier. Therefore, one may not always be
able to modify some null or constant 0 registers.
Test: ./test.py --host
Bug: 131711256
Change-Id: I1c9d857ccdec752bfd4ebad76cc9ad96e143866c
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/verifier/method_verifier.cc | 48 | ||||
-rw-r--r-- | runtime/verifier/method_verifier.h | 10 |
2 files changed, 57 insertions, 1 deletions
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 73ae8e85f3..ff448283e2 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -63,6 +63,7 @@ #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "vdex_file.h" +#include "verifier/method_verifier.h" #include "verifier_compiler_binding.h" #include "verifier_deps.h" @@ -152,6 +153,7 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier { bool need_precise_constants, bool verify_to_dump, bool allow_thread_suspension, + bool fill_register_lines_, uint32_t api_level) REQUIRES_SHARED(Locks::mutator_lock_); @@ -630,6 +632,9 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier { // Note: this flag is only valid once Verify() has started. bool is_constructor_; + // Whether to attempt to fill all register lines for (ex) debugger use. + bool fill_register_lines_; + // API level, for dependent checks. Note: we do not use '0' for unset here, to simplify checks. // Instead, unset level should correspond to max(). const uint32_t api_level_; @@ -695,6 +700,7 @@ MethodVerifier<kVerifierDebug>::MethodVerifier(Thread* self, bool need_precise_constants, bool verify_to_dump, bool allow_thread_suspension, + bool fill_register_lines, uint32_t api_level) : art::verifier::MethodVerifier(self, dex_file, @@ -716,6 +722,7 @@ MethodVerifier<kVerifierDebug>::MethodVerifier(Thread* self, verify_to_dump_(verify_to_dump), allow_thread_suspension_(allow_thread_suspension), is_constructor_(false), + fill_register_lines_(fill_register_lines), api_level_(api_level == 0 ? std::numeric_limits<uint32_t>::max() : api_level) { } @@ -1578,7 +1585,7 @@ bool MethodVerifier<kVerifierDebug>::VerifyCodeFlow() { const uint16_t registers_size = code_item_accessor_.RegistersSize(); /* Create and initialize table holding register status */ - reg_table_.Init(kTrackCompilerInterestPoints, + reg_table_.Init(fill_register_lines_ ? kTrackRegsAll : kTrackCompilerInterestPoints, insn_flags_.get(), code_item_accessor_.InsnsSizeInCodeUnits(), registers_size, @@ -5200,6 +5207,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, need_precise_constants, /* verify to dump */ false, /* allow_thread_suspension= */ true, + /* fill_register_lines= */ false, api_level); if (verifier.Verify()) { // Verification completed, however failures may be pending that didn't cause the verification @@ -5322,6 +5330,41 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, return result; } +MethodVerifier* MethodVerifier::CalculateVerificationInfo( + Thread* self, + ArtMethod* method, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) { + std::unique_ptr<impl::MethodVerifier<false>> verifier( + new impl::MethodVerifier<false>(self, + method->GetDexFile(), + dex_cache, + class_loader, + *method->GetDeclaringClass()->GetClassDef(), + method->GetCodeItem(), + method->GetDexMethodIndex(), + method, + method->GetAccessFlags(), + /* can_load_classes= */ false, + /* allow_soft_failures= */ true, + /* need_precise_constants= */ true, + /* verify_to_dump= */ false, + /* allow_thread_suspension= */ false, + /* fill_register_lines= */ true, + /* api_level = */ 0)); + verifier->Verify(); + if (VLOG_IS_ON(verifier)) { + verifier->DumpFailures(VLOG_STREAM(verifier)); + VLOG(verifier) << verifier->info_messages_.str(); + verifier->Dump(VLOG_STREAM(verifier)); + } + if (verifier->have_pending_hard_failure_) { + return nullptr; + } else { + return verifier.release(); + } +} + MethodVerifier* MethodVerifier::VerifyMethodAndDump(Thread* self, VariableIndentationOutputStream* vios, uint32_t dex_method_idx, @@ -5348,6 +5391,7 @@ MethodVerifier* MethodVerifier::VerifyMethodAndDump(Thread* self, /* need_precise_constants= */ true, /* verify_to_dump= */ true, /* allow_thread_suspension= */ true, + /* fill_register_lines= */ false, api_level); verifier->Verify(); verifier->DumpFailures(vios->Stream()); @@ -5385,6 +5429,7 @@ void MethodVerifier::FindLocksAtDexPc( /* need_precise_constants= */ false, /* verify_to_dump= */ false, /* allow_thread_suspension= */ false, + /* fill_register_lines= */ false, api_level); verifier.interesting_dex_pc_ = dex_pc; verifier.monitor_enter_dex_pcs_ = monitor_enter_dex_pcs; @@ -5420,6 +5465,7 @@ MethodVerifier* MethodVerifier::CreateVerifier(Thread* self, need_precise_constants, verify_to_dump, allow_thread_suspension, + /* fill_register_lines= */ false, api_level); } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 0af09c32c7..bd320ce2a2 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -118,6 +118,16 @@ class MethodVerifier { uint32_t api_level) REQUIRES_SHARED(Locks::mutator_lock_); + // Calculates the verification information for every instruction of the given method. The given + // dex-cache and class-loader will be used for lookups. No classes will be loaded. If verification + // fails hard nullptr will be returned. This should only be used if one needs to examine what the + // verifier believes about the registers of a given method. + static MethodVerifier* CalculateVerificationInfo(Thread* self, + ArtMethod* method, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); + const DexFile& GetDexFile() const { DCHECK(dex_file_ != nullptr); return *dex_file_; |