1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package art; 18 19 import java.lang.reflect.*; 20 import java.util.Base64; 21 import java.nio.ByteBuffer; 22 23 public class Test1949 { 24 private final static boolean isDalvik = System.getProperty("java.vm.name").equals("Dalvik"); 25 26 // This dex file is specifically crafted to have exactly 4 methodIDs in it. They are (in order): 27 // (0) Ljava/lang/Object;-><init>()V 28 // (1) Lxyz/Transform;-><init>()V 29 // (2) Lxyz/Transform;->bar()V 30 // (3) Lxyz/Transform;->foo()V 31 // 32 // In the transformed version of the dex file there is a new method. The new list of methodIDs is: 33 // (0) Lart/Test1949;->doNothing()V 34 // (1) Ljava/lang/Object;-><init>()V 35 // (2) Lxyz/Transform;-><init>()V 36 // (3) Lxyz/Transform;->bar()V 37 // (4) Lxyz/Transform;->foo()V 38 // 39 // This test tries to get the JIT to read out-of-bounds on the initial dex file by getting it to 40 // read the 5th method id of the new file (Lxyz/Transform;->foo()V) from the old dex file (which 41 // only has 4 method ids). 42 // 43 // To do this we need to make sure that the class being transformed is near the end of the 44 // alphabet (package xyz, method foo). If it is further forward than the other method-ids then the 45 // JIT will read an incorrect (but valid) method-id from the old-dex file. This is why the error 46 // wasn't caught in our other tests (package art is always at the front). 47 // 48 // The final method that causes the OOB read needs to be a native method because that is the only 49 // method-type the jit uses dex-file information to keep track of. 50 51 /** 52 * base64 encoded class/dex file for 53 * package xyz; 54 * public class Transform { 55 * public native void foo(); 56 * public void bar() {} 57 * } 58 */ 59 private static final byte[] CLASS_BYTES_INIT = Base64.getDecoder().decode( 60 "yv66vgAAADUADwoAAwAMBwANBwAOAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJU" + 61 "YWJsZQEAA2ZvbwEAA2JhcgEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwABAAFAQANeHl6" + 62 "L1RyYW5zZm9ybQEAEGphdmEvbGFuZy9PYmplY3QAIQACAAMAAAAAAAMAAQAEAAUAAQAGAAAAHQAB" + 63 "AAEAAAAFKrcAAbEAAAABAAcAAAAGAAEAAAACAQEACAAFAAAAAQAJAAUAAQAGAAAAGQAAAAEAAAAB" + 64 "sQAAAAEABwAAAAYAAQAAAAQAAQAKAAAAAgAL"); 65 private static final byte[] DEX_BYTES_INIT = Base64.getDecoder().decode( 66 "ZGV4CjAzNQBDUutFJpeT+okk+aXah8NQ61q2XRtkmChwAgAAcAAAAHhWNBIAAAAAAAAAANwBAAAI" + 67 "AAAAcAAAAAMAAACQAAAAAQAAAJwAAAAAAAAAAAAAAAQAAACoAAAAAQAAAMgAAACIAQAA6AAAABwB" + 68 "AAAkAQAAOAEAAEkBAABZAQAAXAEAAGEBAABmAQAAAQAAAAIAAAAEAAAABAAAAAIAAAAAAAAAAAAA" + 69 "AAAAAAABAAAAAAAAAAEAAAAFAAAAAQAAAAYAAAABAAAAAQAAAAAAAAAAAAAAAwAAAAAAAADDAQAA" + 70 "AAAAAAEAAQABAAAAEgEAAAQAAABwEAAAAAAOAAEAAQAAAAAAFgEAAAEAAAAOAAIADgAEAA4AAAAG" + 71 "PGluaXQ+ABJMamF2YS9sYW5nL09iamVjdDsAD0x4eXovVHJhbnNmb3JtOwAOVHJhbnNmb3JtLmph" + 72 "dmEAAVYAA2JhcgADZm9vAFt+fkQ4eyJtaW4tYXBpIjoxLCJzaGEtMSI6IjkwZWYyMjkwNWMzZmVj" + 73 "Y2FiMjMwMzBhNmJkYzU2NTcwYTMzNWVmMDUiLCJ2ZXJzaW9uIjoidjEuMS44LWRldiJ9AAAAAQIB" + 74 "gYAE6AECAYACAYECAAAAAAAAAAAMAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAMAAACQ" + 75 "AAAAAwAAAAEAAACcAAAABQAAAAQAAACoAAAABgAAAAEAAADIAAAAASAAAAIAAADoAAAAAyAAAAIA" + 76 "AAASAQAAAiAAAAgAAAAcAQAAACAAAAEAAADDAQAAAxAAAAEAAADYAQAAABAAAAEAAADcAQAA"); 77 78 /** 79 * base64 encoded class/dex file for 80 * package xyz; 81 * public class Transform { 82 * public native void foo(); 83 * public void bar() { 84 * // Make sure the methodID is before any of the ones in Transform 85 * art.Test1949.doNothing(); 86 * } 87 * } 88 */ 89 private static final byte[] CLASS_BYTES_FINAL = Base64.getDecoder().decode( 90 "yv66vgAAADUAFAoABAANCgAOAA8HABAHABEBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51" + 91 "bWJlclRhYmxlAQADZm9vAQADYmFyAQAKU291cmNlRmlsZQEADlRyYW5zZm9ybS5qYXZhDAAFAAYH" + 92 "ABIMABMABgEADXh5ei9UcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAMYXJ0L1Rlc3QxOTQ5" + 93 "AQAJZG9Ob3RoaW5nACEAAwAEAAAAAAADAAEABQAGAAEABwAAAB0AAQABAAAABSq3AAGxAAAAAQAI" + 94 "AAAABgABAAAAAgEBAAkABgAAAAEACgAGAAEABwAAABwAAAABAAAABLgAArEAAAABAAgAAAAGAAEA" + 95 "AAAEAAEACwAAAAIADA=="); 96 private static final byte[] DEX_BYTES_FINAL = Base64.getDecoder().decode( 97 "ZGV4CjAzNQBHXBiw7Hso1vnmaXE1VCV41f4+0aECixOgAgAAcAAAAHhWNBIAAAAAAAAAAAwCAAAK" + 98 "AAAAcAAAAAQAAACYAAAAAQAAAKgAAAAAAAAAAAAAAAUAAAC0AAAAAQAAANwAAACkAQAA/AAAADQB" + 99 "AAA8AQAATAEAAGABAABxAQAAgQEAAIQBAACJAQAAlAEAAJkBAAABAAAAAgAAAAMAAAAFAAAABQAA" + 100 "AAMAAAAAAAAAAAAAAAcAAAABAAAAAAAAAAIAAAAAAAAAAgAAAAYAAAACAAAACAAAAAIAAAABAAAA" + 101 "AQAAAAAAAAAEAAAAAAAAAPYBAAAAAAAAAQABAAEAAAAsAQAABAAAAHAQAQAAAA4AAQABAAAAAAAw" + 102 "AQAABAAAAHEAAAAAAA4AAgAOAAQADgAGPGluaXQ+AA5MYXJ0L1Rlc3QxOTQ5OwASTGphdmEvbGFu" + 103 "Zy9PYmplY3Q7AA9MeHl6L1RyYW5zZm9ybTsADlRyYW5zZm9ybS5qYXZhAAFWAANiYXIACWRvTm90" + 104 "aGluZwADZm9vAFt+fkQ4eyJtaW4tYXBpIjoxLCJzaGEtMSI6IjkwZWYyMjkwNWMzZmVjY2FiMjMw" + 105 "MzBhNmJkYzU2NTcwYTMzNWVmMDUiLCJ2ZXJzaW9uIjoidjEuMS44LWRldiJ9AAAAAQICgYAE/AED" + 106 "AZQCAYECAAAAAAAMAAAAAAAAAAEAAAAAAAAAAQAAAAoAAABwAAAAAgAAAAQAAACYAAAAAwAAAAEA" + 107 "AACoAAAABQAAAAUAAAC0AAAABgAAAAEAAADcAAAAASAAAAIAAAD8AAAAAyAAAAIAAAAsAQAAAiAA" + 108 "AAoAAAA0AQAAACAAAAEAAAD2AQAAAxAAAAEAAAAIAgAAABAAAAEAAAAMAgAA"); 109 run()110 public static void run() throws Exception { 111 Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); 112 doTest(); 113 } 114 115 // A method with a methodID before anything in Transform. doNothing()116 public static void doNothing() {} 117 CreateClassLoader(byte[] clz, byte[] dex)118 private static ClassLoader CreateClassLoader(byte[] clz, byte[] dex) throws Exception { 119 if (isDalvik) { 120 Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader"); 121 Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class); 122 /* on Dalvik, this is a DexFile; otherwise, it's null */ 123 return (ClassLoader)ctor.newInstance(ByteBuffer.wrap(dex), Test1949.class.getClassLoader()); 124 } else { 125 return new ClassLoader() { 126 public Class<?> findClass(String name) throws ClassNotFoundException { 127 if (name.equals("xyz.Transform")) { 128 return defineClass(name, clz, 0, clz.length); 129 } else { 130 throw new ClassNotFoundException("Couldn't find class: " + name); 131 } 132 } 133 }; 134 } 135 } 136 137 public static void doTest() throws Exception { 138 Class c = CreateClassLoader(CLASS_BYTES_INIT, DEX_BYTES_INIT).loadClass("xyz.Transform"); 139 Redefinition.doCommonClassRedefinition(c, CLASS_BYTES_FINAL, DEX_BYTES_FINAL); 140 System.out.println("Passed"); 141 } 142 } 143