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 testDexMemoryMaps()93 private static void testDexMemoryMaps() throws Exception { 94 // Ensure that the secondary dex file is mapped clean (directly from JAR file). 95 String smaps = new String(Files.readAllBytes(Paths.get("/proc/self/smaps"))); 96 97 String[] smapsLines = smaps.split("\n"); 98 boolean found = true; 99 for (int i = 0; i < smapsLines.length; ++i) { 100 if (smapsLines[i].contains(CLASS_PATH)) { 101 if (checkSmapsEntry(smapsLines, i)) { 102 return; 103 } // else we found the wrong one, keep going. 104 } 105 } 106 107 // Error case: 108 System.out.println("Could not find " + CLASS_PATH + " RO-anonymous smaps entry"); 109 System.out.println(smaps); 110 } 111 testDexFile()112 private static Class<?> testDexFile() throws Exception { 113 ClassLoader classLoader = Main.class.getClassLoader(); 114 Class<?> DexFile = classLoader.loadClass("dalvik.system.DexFile"); 115 Method DexFile_loadDex = DexFile.getMethod("loadDex", 116 String.class, 117 String.class, 118 Integer.TYPE); 119 Method DexFile_entries = DexFile.getMethod("entries"); 120 Object dexFile = DexFile_loadDex.invoke(null, CLASS_PATH, null, 0); 121 Enumeration<String> e = (Enumeration<String>) DexFile_entries.invoke(dexFile); 122 while (e.hasMoreElements()) { 123 String className = e.nextElement(); 124 System.out.println(className); 125 } 126 127 Method DexFile_loadClass = DexFile.getMethod("loadClass", 128 String.class, 129 ClassLoader.class); 130 Class<?> AnotherClass = (Class<?>)DexFile_loadClass.invoke(dexFile, 131 "Another", Main.class.getClassLoader()); 132 return AnotherClass; 133 } 134 } 135