summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc62
-rw-r--r--runtime/openjdkjvmti/ti_redefine.h7
-rw-r--r--test/921-hello-failure/expected.txt3
-rw-r--r--test/921-hello-failure/src/Main.java1
-rw-r--r--test/921-hello-failure/src/Verification.java82
5 files changed, 144 insertions, 11 deletions
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 058b93a042..59cb8763de 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -56,6 +56,8 @@
#include "ScopedLocalRef.h"
#include "ti_class_loader.h"
#include "transform.h"
+#include "verifier/method_verifier.h"
+#include "verifier/verifier_log_mode.h"
namespace openjdkjvmti {
@@ -703,7 +705,6 @@ bool Redefiner::ClassRedefinition::CheckClass() {
}
}
LOG(WARNING) << "No verification is done on annotations of redefined classes.";
- LOG(WARNING) << "Bytecodes of redefinitions are not verified.";
return true;
}
@@ -766,26 +767,28 @@ class RedefinitionDataHolder {
}
// TODO Maybe make an iterable view type to simplify using this.
- art::mirror::ClassLoader* GetSourceClassLoader(jint klass_index)
+ art::mirror::ClassLoader* GetSourceClassLoader(jint klass_index) const
REQUIRES_SHARED(art::Locks::mutator_lock_) {
return art::down_cast<art::mirror::ClassLoader*>(GetSlot(klass_index, kSlotSourceClassLoader));
}
- art::mirror::Object* GetJavaDexFile(jint klass_index) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::mirror::Object* GetJavaDexFile(jint klass_index) const
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
return GetSlot(klass_index, kSlotJavaDexFile);
}
- art::mirror::LongArray* GetNewDexFileCookie(jint klass_index)
+ art::mirror::LongArray* GetNewDexFileCookie(jint klass_index) const
REQUIRES_SHARED(art::Locks::mutator_lock_) {
return art::down_cast<art::mirror::LongArray*>(GetSlot(klass_index, kSlotNewDexFileCookie));
}
- art::mirror::DexCache* GetNewDexCache(jint klass_index)
+ art::mirror::DexCache* GetNewDexCache(jint klass_index) const
REQUIRES_SHARED(art::Locks::mutator_lock_) {
return art::down_cast<art::mirror::DexCache*>(GetSlot(klass_index, kSlotNewDexCache));
}
- art::mirror::Class* GetMirrorClass(jint klass_index) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::mirror::Class* GetMirrorClass(jint klass_index) const
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
return art::down_cast<art::mirror::Class*>(GetSlot(klass_index, kSlotMirrorClass));
}
- art::mirror::ByteArray* GetOriginalDexFileBytes(jint klass_index)
+ art::mirror::ByteArray* GetOriginalDexFileBytes(jint klass_index) const
REQUIRES_SHARED(art::Locks::mutator_lock_) {
return art::down_cast<art::mirror::ByteArray*>(GetSlot(klass_index, kSlotOrigDexFile));
}
@@ -815,15 +818,15 @@ class RedefinitionDataHolder {
SetSlot(klass_index, kSlotOrigDexFile, bytes);
}
- int32_t Length() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ int32_t Length() const REQUIRES_SHARED(art::Locks::mutator_lock_) {
return arr_->GetLength() / kNumSlots;
}
private:
- art::Handle<art::mirror::ObjectArray<art::mirror::Object>> arr_;
+ mutable art::Handle<art::mirror::ObjectArray<art::mirror::Object>> arr_;
art::mirror::Object* GetSlot(jint klass_index,
- DataSlot slot) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ DataSlot slot) const REQUIRES_SHARED(art::Locks::mutator_lock_) {
DCHECK_LT(klass_index, Length());
return arr_->Get((kNumSlots * klass_index) + slot);
}
@@ -839,6 +842,31 @@ class RedefinitionDataHolder {
DISALLOW_COPY_AND_ASSIGN(RedefinitionDataHolder);
};
+// TODO Stash and update soft failure state
+bool Redefiner::ClassRedefinition::CheckVerification(int32_t klass_index,
+ const RedefinitionDataHolder& holder) {
+ DCHECK_EQ(dex_file_->NumClassDefs(), 1u);
+ art::StackHandleScope<2> hs(driver_->self_);
+ std::string error;
+ // TODO Make verification log level lower
+ art::verifier::MethodVerifier::FailureKind failure =
+ art::verifier::MethodVerifier::VerifyClass(driver_->self_,
+ dex_file_.get(),
+ hs.NewHandle(holder.GetNewDexCache(klass_index)),
+ hs.NewHandle(GetClassLoader()),
+ dex_file_->GetClassDef(0), /*class_def*/
+ nullptr, /*compiler_callbacks*/
+ false, /*allow_soft_failures*/
+ /*log_level*/
+ art::verifier::HardFailLogMode::kLogWarning,
+ &error);
+ bool passes = failure == art::verifier::MethodVerifier::kNoFailure;
+ if (!passes) {
+ RecordFailure(ERR(FAILS_VERIFICATION), "Failed to verify class. Error was: " + error);
+ }
+ return passes;
+}
+
// Looks through the previously allocated cookies to see if we need to update them with another new
// dexfile. This is so that even if multiple classes with the same classloader are redefined at
// once they are all added to the classloader.
@@ -978,6 +1006,17 @@ void Redefiner::ReleaseAllDexFiles() {
}
}
+bool Redefiner::CheckAllClassesAreVerified(const RedefinitionDataHolder& holder) {
+ int32_t cnt = 0;
+ for (Redefiner::ClassRedefinition& redef : redefinitions_) {
+ if (!redef.CheckVerification(cnt, holder)) {
+ return false;
+ }
+ cnt++;
+ }
+ return true;
+}
+
jvmtiError Redefiner::Run() {
art::StackHandleScope<1> hs(self_);
// Allocate an array to hold onto all java temporary objects associated with this redefinition.
@@ -997,7 +1036,8 @@ jvmtiError Redefiner::Run() {
// try loop.
if (!CheckAllRedefinitionAreValid() ||
!EnsureAllClassAllocationsFinished() ||
- !FinishAllRemainingAllocations(holder)) {
+ !FinishAllRemainingAllocations(holder) ||
+ !CheckAllClassesAreVerified(holder)) {
// TODO Null out the ClassExt fields we allocated (if possible, might be racing with another
// redefineclass call which made it even bigger. Leak shouldn't be huge (2x array of size
// declared_methods_.length) but would be good to get rid of. All other allocations should be
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 3209abbe64..421d22ef4c 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -165,6 +165,11 @@ class Redefiner {
// data has not been modified in an incompatible manner.
bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ // Checks that the contained class can be successfully verified.
+ bool CheckVerification(int32_t klass_index,
+ const RedefinitionDataHolder& holder)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
// Preallocates all needed allocations in klass so that we can pause execution safely.
// TODO We should be able to free the arrays if they end up not being used. Investigate doing
// this in the future. For now we will just take the memory hit.
@@ -239,6 +244,8 @@ class Redefiner {
jvmtiError Run() REQUIRES_SHARED(art::Locks::mutator_lock_);
bool CheckAllRedefinitionAreValid() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ bool CheckAllClassesAreVerified(const RedefinitionDataHolder& holder)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
bool EnsureAllClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_);
bool FinishAllRemainingAllocations(RedefinitionDataHolder& holder)
REQUIRES_SHARED(art::Locks::mutator_lock_);
diff --git a/test/921-hello-failure/expected.txt b/test/921-hello-failure/expected.txt
index e9b6a20cd6..a5dc10d59c 100644
--- a/test/921-hello-failure/expected.txt
+++ b/test/921-hello-failure/expected.txt
@@ -1,3 +1,6 @@
+hello - Verification
+Transformation error : java.lang.Exception(Failed to redefine class <LTransform;> due to JVMTI_ERROR_FAILS_VERIFICATION)
+hello - Verification
hello - NewName
Transformation error : java.lang.Exception(Failed to redefine class <LTransform;> due to JVMTI_ERROR_NAMES_DONT_MATCH)
hello - NewName
diff --git a/test/921-hello-failure/src/Main.java b/test/921-hello-failure/src/Main.java
index 61d69e7396..5bbe2b5479 100644
--- a/test/921-hello-failure/src/Main.java
+++ b/test/921-hello-failure/src/Main.java
@@ -18,6 +18,7 @@ import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
+ Verification.doTest(new Transform());
NewName.doTest(new Transform());
DifferentAccess.doTest(new Transform());
NewInterface.doTest(new Transform2());
diff --git a/test/921-hello-failure/src/Verification.java b/test/921-hello-failure/src/Verification.java
new file mode 100644
index 0000000000..242b5d2b44
--- /dev/null
+++ b/test/921-hello-failure/src/Verification.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import java.util.Base64;
+
+class Verification {
+ // Jasmin program:
+ //
+ // .source Transform.java
+ // .class Transform
+ // .super java/lang/Object
+ // .method <init>()V
+ // .limit stack 1
+ // .limit locals 1
+ // aload_0
+ // invokespecial java/lang/Object/<init>()V
+ // return
+ // .end method
+ // .method sayHi(Ljava/lang/String;)V
+ // .limit stack 1
+ // .limit locals 2
+ // aload_1
+ // areturn
+ // .end method
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgADAC0ADgoADQAHBwAIAQAQamF2YS9sYW5nL09iamVjdAEAClNvdXJjZUZpbGUBAAY8aW5p" +
+ "dD4BAAVzYXlIaQwABQAKAQAJVHJhbnNmb3JtAQAEQ29kZQEAAygpVgEADlRyYW5zZm9ybS5qYXZh" +
+ "AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWBwADACAAAgANAAAAAAACAAAABQAKAAEACQAAABEAAQAB" +
+ "AAAABSq3AAGxAAAAAAABAAYADAABAAkAAAAOAAEAAgAAAAIrsAAAAAAAAQAEAAAAAgAL");
+
+ // Smali program:
+ //
+ // .class LTransform;
+ // .super Ljava/lang/Object;
+ // .source "Transform.java"
+ // # direct methods
+ // .method constructor <init>()V
+ // .registers 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // return-void
+ // .end method
+ // # virtual methods
+ // .method public sayHi(Ljava/lang/String;)V
+ // .registers 2
+ // return-object p1
+ // .end method
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQClOAc4ZDMXaHMezhYcqZxcjUeVCWRYUkooAgAAcAAAAHhWNBIAAAAAAAAAAJQBAAAI" +
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAA4AQAA8AAAAPAA" +
+ "AAD4AAAABQEAABkBAAAtAQAAPQEAAEABAABEAQAAAQAAAAIAAAADAAAABQAAAAUAAAADAAAAAAAA" +
+ "AAYAAAADAAAATAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAEAAAA" +
+ "AAAAAIYBAAAAAAAABjxpbml0PgALTFRyYW5zZm9ybTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGph" +
+ "dmEvbGFuZy9TdHJpbmc7AA5UcmFuc2Zvcm0uamF2YQABVgACVkwABXNheUhpAAABAAAAAgAAAAAA" +
+ "AAAAAAAAAQABAAEAAAAAAAAABAAAAHAQAgAAAA4AAgACAAAAAAAAAAAAAQAAABEBAAABAQCAgATc" +
+ "AgEB9AIMAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAQAAACQAAAAAwAAAAIAAACgAAAA" +
+ "BQAAAAMAAAC4AAAABgAAAAEAAADQAAAAAiAAAAgAAADwAAAAARAAAAEAAABMAQAAAxAAAAIAAABU" +
+ "AQAAASAAAAIAAABcAQAAACAAAAEAAACGAQAAABAAAAEAAACUAQAA");
+
+ public static void doTest(Transform t) {
+ t.sayHi("Verification");
+ try {
+ Main.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ } catch (Exception e) {
+ System.out.println(
+ "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+ }
+ t.sayHi("Verification");
+ }
+}