diff options
| -rw-r--r-- | runtime/class_linker.cc | 33 | ||||
| -rw-r--r-- | runtime/class_linker.h | 7 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_redefine.cc | 30 | ||||
| -rw-r--r-- | test/988-redefine-use-after-free/expected.txt | 0 | ||||
| -rw-r--r-- | test/988-redefine-use-after-free/info.txt | 13 | ||||
| -rwxr-xr-x | test/988-redefine-use-after-free/run | 17 | ||||
| -rw-r--r-- | test/988-redefine-use-after-free/src-ex/DexCacheSmash.java | 155 | ||||
| -rw-r--r-- | test/988-redefine-use-after-free/src-ex/art/Redefinition.java | 91 | ||||
| -rw-r--r-- | test/988-redefine-use-after-free/src/Main.java | 54 |
9 files changed, 398 insertions, 2 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index c3a8fc5383..aab98395d0 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3535,6 +3535,39 @@ ObjPtr<mirror::DexCache> ClassLinker::EnsureSameClassLoader( return dex_cache; } +void ClassLinker::RegisterExistingDexCache(ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) { + Thread* self = Thread::Current(); + StackHandleScope<2> hs(self); + Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(dex_cache)); + Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader)); + const DexFile* dex_file = dex_cache->GetDexFile(); + DCHECK(dex_file != nullptr) << "Attempt to register uninitialized dex_cache object!"; + if (kIsDebugBuild) { + DexCacheData old_data; + { + ReaderMutexLock mu(self, *Locks::dex_lock_); + old_data = FindDexCacheDataLocked(*dex_file); + } + ObjPtr<mirror::DexCache> old_dex_cache = DecodeDexCache(self, old_data); + DCHECK(old_dex_cache.IsNull()) << "Attempt to manually register a dex cache thats already " + << "been registered on dex file " << dex_file->GetLocation(); + } + ClassTable* table; + { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + table = InsertClassTableForClassLoader(h_class_loader.Get()); + } + WriterMutexLock mu(self, *Locks::dex_lock_); + RegisterDexFileLocked(*dex_file, h_dex_cache.Get(), h_class_loader.Get()); + table->InsertStrongRoot(h_dex_cache.Get()); + if (h_class_loader.Get() != nullptr) { + // Since we added a strong root to the class table, do the write barrier as required for + // remembered sets and generational GCs. + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(h_class_loader.Get()); + } +} + ObjPtr<mirror::DexCache> ClassLinker::RegisterDexFile(const DexFile& dex_file, ObjPtr<mirror::ClassLoader> class_loader) { Thread* self = Thread::Current(); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 205ea1e496..fad6e9ebbf 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -385,6 +385,13 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); + // Directly register an already existing dex cache. RegisterDexFile should be preferred since that + // reduplicates DexCaches when possible. The DexCache given to this function must already be fully + // initialized and not already registered. + void RegisterExistingDexCache(ObjPtr<mirror::DexCache> cache, + ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES(!Locks::dex_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr<mirror::DexCache> RegisterDexFile(const DexFile& dex_file, ObjPtr<mirror::ClassLoader> class_loader) REQUIRES(!Locks::dex_lock_) diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index ca3a0e631a..b382a3e7c3 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -48,6 +48,7 @@ #include "gc/allocation_listener.h" #include "gc/heap.h" #include "instrumentation.h" +#include "intern_table.h" #include "jdwp/jdwp.h" #include "jdwp/jdwp_constants.h" #include "jdwp/jdwp_event.h" @@ -452,7 +453,30 @@ art::mirror::ClassLoader* Redefiner::ClassRedefinition::GetClassLoader() { art::mirror::DexCache* Redefiner::ClassRedefinition::CreateNewDexCache( art::Handle<art::mirror::ClassLoader> loader) { - return driver_->runtime_->GetClassLinker()->RegisterDexFile(*dex_file_, loader.Get()).Ptr(); + art::StackHandleScope<2> hs(driver_->self_); + art::ClassLinker* cl = driver_->runtime_->GetClassLinker(); + art::Handle<art::mirror::DexCache> cache(hs.NewHandle( + art::ObjPtr<art::mirror::DexCache>::DownCast( + cl->GetClassRoot(art::ClassLinker::kJavaLangDexCache)->AllocObject(driver_->self_)))); + if (cache.IsNull()) { + driver_->self_->AssertPendingOOMException(); + return nullptr; + } + art::Handle<art::mirror::String> location(hs.NewHandle( + cl->GetInternTable()->InternStrong(dex_file_->GetLocation().c_str()))); + if (location.IsNull()) { + driver_->self_->AssertPendingOOMException(); + return nullptr; + } + art::WriterMutexLock mu(driver_->self_, *art::Locks::dex_lock_); + art::mirror::DexCache::InitializeDexCache(driver_->self_, + cache.Get(), + location.Get(), + dex_file_.get(), + loader.IsNull() ? driver_->runtime_->GetLinearAlloc() + : loader->GetAllocator(), + art::kRuntimePointerSize); + return cache.Get(); } void Redefiner::RecordFailure(jvmtiError result, @@ -1293,8 +1317,10 @@ jvmtiError Redefiner::Run() { // At this point we can no longer fail without corrupting the runtime state. for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) { + art::ClassLinker* cl = runtime_->GetClassLinker(); + cl->RegisterExistingDexCache(data.GetNewDexCache(), data.GetSourceClassLoader()); if (data.GetSourceClassLoader() == nullptr) { - runtime_->GetClassLinker()->AppendToBootClassPath(self_, data.GetRedefinition().GetDexFile()); + cl->AppendToBootClassPath(self_, data.GetRedefinition().GetDexFile()); } } UnregisterAllBreakpoints(); diff --git a/test/988-redefine-use-after-free/expected.txt b/test/988-redefine-use-after-free/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/988-redefine-use-after-free/expected.txt diff --git a/test/988-redefine-use-after-free/info.txt b/test/988-redefine-use-after-free/info.txt new file mode 100644 index 0000000000..2b683dd75e --- /dev/null +++ b/test/988-redefine-use-after-free/info.txt @@ -0,0 +1,13 @@ +Regression test for b/62237378 + +It was possible for the JVMTI class redefinition to encounter a use-after-free +bug if there had been an attempted redefinition that failed due to a +verification error in the same class loader. Actually encountering the bug +required that a later redefinition happen to get the same native pointer for its +dex-file as the failed redefinition. + +Hitting this use-after-free can cause many strange outcomes, from CHECK failures +to segfaults to incorrect redefinition failures (for example on buggy builds +this test will fail a DCHECK on debug builds, segfault on x86_64 hosts and have +redefinition of LDexCacheSmash$Transform; erroneously fail with +JVMTI_ERROR_FAILS_VERIFICATION on 32 bit hosts). diff --git a/test/988-redefine-use-after-free/run b/test/988-redefine-use-after-free/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/988-redefine-use-after-free/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 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. + +./default-run "$@" --jvmti diff --git a/test/988-redefine-use-after-free/src-ex/DexCacheSmash.java b/test/988-redefine-use-after-free/src-ex/DexCacheSmash.java new file mode 100644 index 0000000000..2193a631cd --- /dev/null +++ b/test/988-redefine-use-after-free/src-ex/DexCacheSmash.java @@ -0,0 +1,155 @@ +/* + * 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 art.Redefinition; +import java.util.Base64; + +public class DexCacheSmash { + static class Transform { + public void foo() {} + public void bar() {} + public String getId() { + return "TRANSFORM_INITIAL"; + } + } + + static class Transform2 { + public String getId() { + return "TRANSFORM2_INITIAL"; + } + } + + /** + * A base64 encoding of the dex/class file of the Transform class above. + */ + static final Redefinition.CommonClassDefinition TRANSFORM_INITIAL = + new Redefinition.CommonClassDefinition(Transform.class, + Base64.getDecoder().decode( + "yv66vgAAADQAFwoABAAPCAAQBwASBwAVAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1i" + + "ZXJUYWJsZQEAA2ZvbwEAA2JhcgEABWdldElkAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3Vy" + + "Y2VGaWxlAQASRGV4Q2FjaGVTbWFzaC5qYXZhDAAFAAYBABFUUkFOU0ZPUk1fSU5JVElBTAcAFgEA" + + "F0RleENhY2hlU21hc2gkVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2" + + "YS9sYW5nL09iamVjdAEADURleENhY2hlU21hc2gAIAADAAQAAAAAAAQAAAAFAAYAAQAHAAAAHQAB" + + "AAEAAAAFKrcAAbEAAAABAAgAAAAGAAEAAAATAAEACQAGAAEABwAAABkAAAABAAAAAbEAAAABAAgA" + + "AAAGAAEAAAAUAAEACgAGAAEABwAAABkAAAABAAAAAbEAAAABAAgAAAAGAAEAAAAVAAEACwAMAAEA" + + "BwAAABsAAQABAAAAAxICsAAAAAEACAAAAAYAAQAAABcAAgANAAAAAgAOABQAAAAKAAEAAwARABMA" + + "CA=="), + Base64.getDecoder().decode( + "ZGV4CjAzNQDhg9CfghG1SRlLClguRuFYsqihr4F7NsGQAwAAcAAAAHhWNBIAAAAAAAAAAOQCAAAS" + + "AAAAcAAAAAcAAAC4AAAAAgAAANQAAAAAAAAAAAAAAAUAAADsAAAAAQAAABQBAABcAgAANAEAAKgB" + + "AACwAQAAxAEAAMcBAADiAQAA8wEAABcCAAA3AgAASwIAAF8CAAByAgAAfQIAAIACAACNAgAAkgIA" + + "AJcCAACeAgAApAIAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAsAAAACAAAABQAAAAAAAAALAAAA" + + "BgAAAAAAAAAAAAEAAAAAAAAAAQANAAAAAAABAA4AAAAAAAAADwAAAAQAAQAAAAAAAAAAAAAAAAAE" + + "AAAAAAAAAAEAAACYAQAAzgIAAAAAAAACAAAAvwIAAMUCAAABAAEAAQAAAKsCAAAEAAAAcBAEAAAA" + + "DgABAAEAAAAAALACAAABAAAADgAAAAEAAQAAAAAAtQIAAAEAAAAOAAAAAgABAAAAAAC6AgAAAwAA" + + "ABoACQARAAAANAEAAAAAAAAAAAAAAAAAAAY8aW5pdD4AEkRleENhY2hlU21hc2guamF2YQABTAAZ" + + "TERleENhY2hlU21hc2gkVHJhbnNmb3JtOwAPTERleENhY2hlU21hc2g7ACJMZGFsdmlrL2Fubm90" + + "YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJDbGFzczsAEkxq" + + "YXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABFUUkFOU0ZPUk1fSU5JVElBTAAJ" + + "VHJhbnNmb3JtAAFWAAthY2Nlc3NGbGFncwADYmFyAANmb28ABWdldElkAARuYW1lAAV2YWx1ZQAT" + + "AAcOABUABw4AFAAHDgAXAAcOAAICAREYAQIDAgwECBAXCgAAAQMAgIAEwAIBAdgCAQHsAgEBgAMO" + + "AAAAAAAAAAEAAAAAAAAAAQAAABIAAABwAAAAAgAAAAcAAAC4AAAAAwAAAAIAAADUAAAABQAAAAUA" + + "AADsAAAABgAAAAEAAAAUAQAAAxAAAAEAAAA0AQAAASAAAAQAAABAAQAABiAAAAEAAACYAQAAAiAA" + + "ABIAAACoAQAAAyAAAAQAAACrAgAABCAAAAIAAAC/AgAAACAAAAEAAADOAgAAABAAAAEAAADkAgAA")); + + /** + * A base64 encoding of the following (invalid) class. + * + * .class LDexCacheSmash$Transform2; + * .super Ljava/lang/Object; + * .source "DexCacheSmash.java" + * + * # annotations + * .annotation system Ldalvik/annotation/EnclosingClass; + * value = LDexCacheSmash; + * .end annotation + * + * .annotation system Ldalvik/annotation/InnerClass; + * accessFlags = 0x8 + * name = "Transform2" + * .end annotation + * + * + * # direct methods + * .method constructor <init>()V + * .registers 1 + * + * .prologue + * .line 26 + * invoke-direct {p0}, Ljava/lang/Object;-><init>()V + * + * return-void + * .end method + * + * + * # virtual methods + * .method public getId()Ljava/lang/String; + * .registers 2 + * + * .prologue + * .line 28 + * # NB Fails verification due to this function not returning a String. + * return-void + * .end method + */ + static final Redefinition.CommonClassDefinition TRANSFORM2_INVALID = + new Redefinition.CommonClassDefinition(Transform2.class, + Base64.getDecoder().decode( + "yv66vgAAADQAEwcAEgcAEQEABjxpbml0PgEAAygpVgEABENvZGUKAAIAEAEAD0xpbmVOdW1iZXJU" + + "YWJsZQEABWdldElkAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQASRGV4Q2Fj" + + "aGVTbWFzaC5qYXZhAQAMSW5uZXJDbGFzc2VzBwAPAQAKVHJhbnNmb3JtMgEADURleENhY2hlU21h" + + "c2gMAAMABAEAEGphdmEvbGFuZy9PYmplY3QBABhEZXhDYWNoZVNtYXNoJFRyYW5zZm9ybTIAIAAB" + + "AAIAAAAAAAIAAAADAAQAAQAFAAAAHQABAAEAAAAFKrcABrEAAAABAAcAAAAGAAEAAAAaAAEACAAJ" + + "AAEABQAAABkAAQABAAAAAbEAAAABAAcAAAAGAAEAAAAcAAIACgAAAAIACwAMAAAACgABAAEADQAO" + + "AAg="), + Base64.getDecoder().decode( + "ZGV4CjAzNQCFcegr6Ns+I7iEF4uLRkUX4yGrLhP6soEgAwAAcAAAAHhWNBIAAAAAAAAAAHQCAAAP" + + "AAAAcAAAAAcAAACsAAAAAgAAAMgAAAAAAAAAAAAAAAMAAADgAAAAAQAAAPgAAAAIAgAAGAEAABgB" + + "AAAgAQAANAEAADcBAABTAQAAZAEAAIgBAACoAQAAvAEAANABAADcAQAA3wEAAOwBAADzAQAA+QEA" + + "AAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAoAAAACAAAABQAAAAAAAAAKAAAABgAAAAAAAAAAAAEA" + + "AAAAAAAAAAAMAAAABAABAAAAAAAAAAAAAAAAAAQAAAAAAAAAAQAAACACAABmAgAAAAAAAAY8aW5p" + + "dD4AEkRleENhY2hlU21hc2guamF2YQABTAAaTERleENhY2hlU21hc2gkVHJhbnNmb3JtMjsAD0xE" + + "ZXhDYWNoZVNtYXNoOwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZp" + + "ay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcv" + + "U3RyaW5nOwAKVHJhbnNmb3JtMgABVgALYWNjZXNzRmxhZ3MABWdldElkAARuYW1lAAV2YWx1ZQAC" + + "AwILBAgNFwkCAgEOGAEAAAAAAAIAAAAJAgAAAAIAABQCAAAAAAAAAAAAAAAAAAAaAAcOABwABw4A" + + "AAABAAEAAQAAADACAAAEAAAAcBACAAAADgACAAEAAAAAADUCAAABAAAADgAAAAEBAICABLwEAQHU" + + "BA4AAAAAAAAAAQAAAAAAAAABAAAADwAAAHAAAAACAAAABwAAAKwAAAADAAAAAgAAAMgAAAAFAAAA" + + "AwAAAOAAAAAGAAAAAQAAAPgAAAACIAAADwAAABgBAAAEIAAAAgAAAAACAAADEAAAAgAAABACAAAG" + + "IAAAAQAAACACAAADIAAAAgAAADACAAABIAAAAgAAADwCAAAAIAAAAQAAAGYCAAAAEAAAAQAAAHQC" + + "AAA=")); + + public static void run() throws Exception { + try { + Redefinition.doMultiClassRedefinition(TRANSFORM2_INVALID); + } catch (Exception e) { + if (!e.getMessage().endsWith("JVMTI_ERROR_FAILS_VERIFICATION")) { + throw new Error( + "Unexpected error: Expected failure due to JVMTI_ERROR_FAILS_VERIFICATION", e); + } + } + // Doing this redefinition after a redefinition that failed due to FAILS_VERIFICATION could + // cause a use-after-free of the Transform2's DexCache by the redefinition code if it happens + // that the native pointer of the art::DexFile created for the Transform redefinition aliases + // the one created for Transform2's failed redefinition. + // + // Due to the order of checks performed by the redefinition code FAILS_VERIFICATION is the only + // failure mode that can cause Use-after-frees in this way. + // + // This should never throw any exceptions (except perhaps OOME in very strange circumstances). + Redefinition.doMultiClassRedefinition(TRANSFORM_INITIAL); + } +} diff --git a/test/988-redefine-use-after-free/src-ex/art/Redefinition.java b/test/988-redefine-use-after-free/src-ex/art/Redefinition.java new file mode 100644 index 0000000000..56d2938a01 --- /dev/null +++ b/test/988-redefine-use-after-free/src-ex/art/Redefinition.java @@ -0,0 +1,91 @@ +/* + * 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. + */ + +package art; + +import java.util.ArrayList; +// Common Redefinition functions. Placed here for use by CTS +public class Redefinition { + public static final class CommonClassDefinition { + public final Class<?> target; + public final byte[] class_file_bytes; + public final byte[] dex_file_bytes; + + public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) { + this.target = target; + this.class_file_bytes = class_file_bytes; + this.dex_file_bytes = dex_file_bytes; + } + } + + // A set of possible test configurations. Test should set this if they need to. + // This must be kept in sync with the defines in ti-agent/common_helper.cc + public static enum Config { + COMMON_REDEFINE(0), + COMMON_RETRANSFORM(1), + COMMON_TRANSFORM(2); + + private final int val; + private Config(int val) { + this.val = val; + } + } + + public static void setTestConfiguration(Config type) { + nativeSetTestConfiguration(type.val); + } + + private static native void nativeSetTestConfiguration(int type); + + // Transforms the class + public static native void doCommonClassRedefinition(Class<?> target, + byte[] classfile, + byte[] dexfile); + + public static void doMultiClassRedefinition(CommonClassDefinition... defs) { + ArrayList<Class<?>> classes = new ArrayList<>(); + ArrayList<byte[]> class_files = new ArrayList<>(); + ArrayList<byte[]> dex_files = new ArrayList<>(); + + for (CommonClassDefinition d : defs) { + classes.add(d.target); + class_files.add(d.class_file_bytes); + dex_files.add(d.dex_file_bytes); + } + doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]), + class_files.toArray(new byte[0][]), + dex_files.toArray(new byte[0][])); + } + + public static void addMultiTransformationResults(CommonClassDefinition... defs) { + for (CommonClassDefinition d : defs) { + addCommonTransformationResult(d.target.getCanonicalName(), + d.class_file_bytes, + d.dex_file_bytes); + } + } + + public static native void doCommonMultiClassRedefinition(Class<?>[] targets, + byte[][] classfiles, + byte[][] dexfiles); + public static native void doCommonClassRetransformation(Class<?>... target); + public static native void setPopRetransformations(boolean pop); + public static native void popTransformationFor(String name); + public static native void enableCommonRetransformation(boolean enable); + public static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/988-redefine-use-after-free/src/Main.java b/test/988-redefine-use-after-free/src/Main.java new file mode 100644 index 0000000000..d88c471a07 --- /dev/null +++ b/test/988-redefine-use-after-free/src/Main.java @@ -0,0 +1,54 @@ +/* + * 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.lang.reflect.*; + +public class Main { + public static final String TEST_NAME = "988-redefine-use-after-free"; + public static final int REPS = 1000; + public static final int STEP = 100; + + public static void main(String[] args) throws Exception { + for (int i = 0; i < REPS; i += STEP) { + runSeveralTimes(STEP); + } + } + + public static ClassLoader getClassLoaderFor(String location) throws Exception { + try { + Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader"); + Constructor<?> ctor = class_loader_class.getConstructor(String.class, ClassLoader.class); + return (ClassLoader)ctor.newInstance(location + "/" + TEST_NAME + "-ex.jar", + Main.class.getClassLoader()); + } catch (ClassNotFoundException e) { + // Running on RI. Use URLClassLoader. + return new java.net.URLClassLoader( + new java.net.URL[] { new java.net.URL("file://" + location + "/classes-ex/") }); + } + } + + // Run the redefinition several times on a single class-loader to try to trigger the + // Use-after-free bug b/62237378 + public static void runSeveralTimes(int times) throws Exception { + ClassLoader c = getClassLoaderFor(System.getenv("DEX_LOCATION")); + + Class<?> klass = (Class<?>)c.loadClass("DexCacheSmash"); + Method m = klass.getDeclaredMethod("run"); + for (int i = 0 ; i < times; i++) { + m.invoke(null); + } + } +} |