1 /* 2 * Copyright (C) 2017 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 import java.lang.reflect.Method; 18 import java.util.Enumeration; 19 20 import java.nio.file.Files; 21 import java.nio.file.Paths; 22 23 /** 24 * DexFile tests (Dalvik-specific). 25 */ 26 public class Main { 27 private static final String CLASS_PATH = 28 System.getenv("DEX_LOCATION") + "/071-dexfile-map-clean-ex.jar"; 29 30 /** 31 * Prep the environment then run the test. 32 */ main(String[] args)33 public static void main(String[] args) throws Exception { 34 // Load the dex file, this is a pre-requisite to mmap-ing it in. 35 Class<?> AnotherClass = testDexFile(); 36 // Check that the memory maps are clean. 37 testDexMemoryMaps(); 38 39 // Prevent garbage collector from collecting our DexFile 40 // (and unmapping too early) by using it after we finish 41 // our verification. 42 AnotherClass.newInstance(); 43 } 44 checkSmapsEntry(String[] smapsLines, int offset)45 private static boolean checkSmapsEntry(String[] smapsLines, int offset) { 46 String nameDescription = smapsLines[offset]; 47 String[] split = nameDescription.split(" "); 48 49 String permissions = split[1]; 50 // Mapped as read-only + anonymous. 51 if (!permissions.startsWith("r--p")) { 52 return false; 53 } 54 55 boolean validated = false; 56 57 // We have the right entry, now make sure it's valid. 58 for (int i = offset; i < smapsLines.length; ++i) { 59 String line = smapsLines[i]; 60 61 if (line.startsWith("Shared_Dirty") || line.startsWith("Private_Dirty")) { 62 String lineTrimmed = line.trim(); 63 String[] lineSplit = lineTrimmed.split(" +"); 64 65 String sizeUsuallyInKb = lineSplit[lineSplit.length - 2]; 66 67 sizeUsuallyInKb = sizeUsuallyInKb.trim(); 68 69 if (!sizeUsuallyInKb.equals("0")) { 70 System.out.println( 71 "ERROR: Memory mapping for " + CLASS_PATH + " is unexpectedly dirty"); 72 System.out.println(line); 73 } else { 74 validated = true; 75 } 76 } 77 78 // VmFlags marks the "end" of an smaps entry. 79 if (line.startsWith("VmFlags")) { 80 break; 81 } 82 } 83 84 if (validated) { 85 System.out.println("Secondary dexfile mmap is clean"); 86 } else { 87 System.out.println("ERROR: Memory mapping is missing Shared_Dirty/Private_Dirty entries"); 88 } 89 90 return true; 91 } 92 93 // This test takes relies on dex2oat being skipped. 94 // (enforced in 'run' file by using '--no-dex2oat' 95 // 96 // This could happen in a non-test situation 97 // if a secondary dex file is loaded (but not yet maintenance-mode compiled) 98 // with JIT. 99 // 100 // Or it could also happen if a secondary dex file is loaded and forced 101 // into running into the interpreter (e.g. duplicate classes). 102 // 103 // Rather than relying on those weird fallbacks, 104 // we force the runtime not to dex2oat the dex file to ensure 105 // this test is repeatable and less brittle. testDexMemoryMaps()106 private static void testDexMemoryMaps() throws Exception { 107 // Ensure that the secondary dex file is mapped clean (directly from JAR file). 108 String smaps = new String(Files.readAllBytes(Paths.get("/proc/self/smaps"))); 109 110 String[] smapsLines = smaps.split("\n"); 111 boolean found = true; 112 for (int i = 0; i < smapsLines.length; ++i) { 113 if (smapsLines[i].contains(CLASS_PATH)) { 114 if (checkSmapsEntry(smapsLines, i)) { 115 return; 116 } // else we found the wrong one, keep going. 117 } 118 } 119 120 // Error case: 121 System.out.println("Could not find " + CLASS_PATH + " RO-anonymous smaps entry"); 122 System.out.println(smaps); 123 } 124 testDexFile()125 private static Class<?> testDexFile() throws Exception { 126 ClassLoader classLoader = Main.class.getClassLoader(); 127 Class<?> DexFile = classLoader.loadClass("dalvik.system.DexFile"); 128 Method DexFile_loadDex = DexFile.getMethod("loadDex", 129 String.class, 130 String.class, 131 Integer.TYPE); 132 Method DexFile_entries = DexFile.getMethod("entries"); 133 Object dexFile = DexFile_loadDex.invoke(null, CLASS_PATH, null, 0); 134 Enumeration<String> e = (Enumeration<String>) DexFile_entries.invoke(dexFile); 135 while (e.hasMoreElements()) { 136 String className = e.nextElement(); 137 System.out.println(className); 138 } 139 140 Method DexFile_loadClass = DexFile.getMethod("loadClass", 141 String.class, 142 ClassLoader.class); 143 Class<?> AnotherClass = (Class<?>)DexFile_loadClass.invoke(dexFile, 144 "Another", Main.class.getClassLoader()); 145 return AnotherClass; 146 } 147 } 148