1*795d594fSAndroid Build Coastguard Worker /* 2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2017 The Android Open Source Project 3*795d594fSAndroid Build Coastguard Worker * 4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*795d594fSAndroid Build Coastguard Worker * 8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*795d594fSAndroid Build Coastguard Worker * 10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*795d594fSAndroid Build Coastguard Worker * limitations under the License. 15*795d594fSAndroid Build Coastguard Worker */ 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Worker import java.lang.reflect.Method; 18*795d594fSAndroid Build Coastguard Worker import java.util.Enumeration; 19*795d594fSAndroid Build Coastguard Worker 20*795d594fSAndroid Build Coastguard Worker import java.nio.file.Files; 21*795d594fSAndroid Build Coastguard Worker import java.nio.file.Paths; 22*795d594fSAndroid Build Coastguard Worker 23*795d594fSAndroid Build Coastguard Worker /** 24*795d594fSAndroid Build Coastguard Worker * DexFile tests (Dalvik-specific). 25*795d594fSAndroid Build Coastguard Worker */ 26*795d594fSAndroid Build Coastguard Worker public class Main { 27*795d594fSAndroid Build Coastguard Worker private static final String CLASS_PATH = 28*795d594fSAndroid Build Coastguard Worker System.getenv("DEX_LOCATION") + "/071-dexfile-map-clean-ex.jar"; 29*795d594fSAndroid Build Coastguard Worker 30*795d594fSAndroid Build Coastguard Worker /** 31*795d594fSAndroid Build Coastguard Worker * Prep the environment then run the test. 32*795d594fSAndroid Build Coastguard Worker */ main(String[] args)33*795d594fSAndroid Build Coastguard Worker public static void main(String[] args) throws Exception { 34*795d594fSAndroid Build Coastguard Worker // Load the dex file, this is a pre-requisite to mmap-ing it in. 35*795d594fSAndroid Build Coastguard Worker Class<?> AnotherClass = testDexFile(); 36*795d594fSAndroid Build Coastguard Worker // Check that the memory maps are clean. 37*795d594fSAndroid Build Coastguard Worker testDexMemoryMaps(); 38*795d594fSAndroid Build Coastguard Worker 39*795d594fSAndroid Build Coastguard Worker // Prevent garbage collector from collecting our DexFile 40*795d594fSAndroid Build Coastguard Worker // (and unmapping too early) by using it after we finish 41*795d594fSAndroid Build Coastguard Worker // our verification. 42*795d594fSAndroid Build Coastguard Worker AnotherClass.newInstance(); 43*795d594fSAndroid Build Coastguard Worker } 44*795d594fSAndroid Build Coastguard Worker checkSmapsEntry(String[] smapsLines, int offset)45*795d594fSAndroid Build Coastguard Worker private static boolean checkSmapsEntry(String[] smapsLines, int offset) { 46*795d594fSAndroid Build Coastguard Worker String nameDescription = smapsLines[offset]; 47*795d594fSAndroid Build Coastguard Worker String[] split = nameDescription.split(" "); 48*795d594fSAndroid Build Coastguard Worker 49*795d594fSAndroid Build Coastguard Worker String permissions = split[1]; 50*795d594fSAndroid Build Coastguard Worker // Mapped as read-only + anonymous. 51*795d594fSAndroid Build Coastguard Worker if (!permissions.startsWith("r--p")) { 52*795d594fSAndroid Build Coastguard Worker return false; 53*795d594fSAndroid Build Coastguard Worker } 54*795d594fSAndroid Build Coastguard Worker 55*795d594fSAndroid Build Coastguard Worker boolean validated = false; 56*795d594fSAndroid Build Coastguard Worker 57*795d594fSAndroid Build Coastguard Worker // We have the right entry, now make sure it's valid. 58*795d594fSAndroid Build Coastguard Worker for (int i = offset; i < smapsLines.length; ++i) { 59*795d594fSAndroid Build Coastguard Worker String line = smapsLines[i]; 60*795d594fSAndroid Build Coastguard Worker 61*795d594fSAndroid Build Coastguard Worker if (line.startsWith("Shared_Dirty") || line.startsWith("Private_Dirty")) { 62*795d594fSAndroid Build Coastguard Worker String lineTrimmed = line.trim(); 63*795d594fSAndroid Build Coastguard Worker String[] lineSplit = lineTrimmed.split(" +"); 64*795d594fSAndroid Build Coastguard Worker 65*795d594fSAndroid Build Coastguard Worker String sizeUsuallyInKb = lineSplit[lineSplit.length - 2]; 66*795d594fSAndroid Build Coastguard Worker 67*795d594fSAndroid Build Coastguard Worker sizeUsuallyInKb = sizeUsuallyInKb.trim(); 68*795d594fSAndroid Build Coastguard Worker 69*795d594fSAndroid Build Coastguard Worker if (!sizeUsuallyInKb.equals("0")) { 70*795d594fSAndroid Build Coastguard Worker System.out.println( 71*795d594fSAndroid Build Coastguard Worker "ERROR: Memory mapping for " + CLASS_PATH + " is unexpectedly dirty"); 72*795d594fSAndroid Build Coastguard Worker System.out.println(line); 73*795d594fSAndroid Build Coastguard Worker } else { 74*795d594fSAndroid Build Coastguard Worker validated = true; 75*795d594fSAndroid Build Coastguard Worker } 76*795d594fSAndroid Build Coastguard Worker } 77*795d594fSAndroid Build Coastguard Worker 78*795d594fSAndroid Build Coastguard Worker // VmFlags marks the "end" of an smaps entry. 79*795d594fSAndroid Build Coastguard Worker if (line.startsWith("VmFlags")) { 80*795d594fSAndroid Build Coastguard Worker break; 81*795d594fSAndroid Build Coastguard Worker } 82*795d594fSAndroid Build Coastguard Worker } 83*795d594fSAndroid Build Coastguard Worker 84*795d594fSAndroid Build Coastguard Worker if (validated) { 85*795d594fSAndroid Build Coastguard Worker System.out.println("Secondary dexfile mmap is clean"); 86*795d594fSAndroid Build Coastguard Worker } else { 87*795d594fSAndroid Build Coastguard Worker System.out.println("ERROR: Memory mapping is missing Shared_Dirty/Private_Dirty entries"); 88*795d594fSAndroid Build Coastguard Worker } 89*795d594fSAndroid Build Coastguard Worker 90*795d594fSAndroid Build Coastguard Worker return true; 91*795d594fSAndroid Build Coastguard Worker } 92*795d594fSAndroid Build Coastguard Worker testDexMemoryMaps()93*795d594fSAndroid Build Coastguard Worker private static void testDexMemoryMaps() throws Exception { 94*795d594fSAndroid Build Coastguard Worker // Ensure that the secondary dex file is mapped clean (directly from JAR file). 95*795d594fSAndroid Build Coastguard Worker String smaps = new String(Files.readAllBytes(Paths.get("/proc/self/smaps"))); 96*795d594fSAndroid Build Coastguard Worker 97*795d594fSAndroid Build Coastguard Worker String[] smapsLines = smaps.split("\n"); 98*795d594fSAndroid Build Coastguard Worker boolean found = true; 99*795d594fSAndroid Build Coastguard Worker for (int i = 0; i < smapsLines.length; ++i) { 100*795d594fSAndroid Build Coastguard Worker if (smapsLines[i].contains(CLASS_PATH)) { 101*795d594fSAndroid Build Coastguard Worker if (checkSmapsEntry(smapsLines, i)) { 102*795d594fSAndroid Build Coastguard Worker return; 103*795d594fSAndroid Build Coastguard Worker } // else we found the wrong one, keep going. 104*795d594fSAndroid Build Coastguard Worker } 105*795d594fSAndroid Build Coastguard Worker } 106*795d594fSAndroid Build Coastguard Worker 107*795d594fSAndroid Build Coastguard Worker // Error case: 108*795d594fSAndroid Build Coastguard Worker System.out.println("Could not find " + CLASS_PATH + " RO-anonymous smaps entry"); 109*795d594fSAndroid Build Coastguard Worker System.out.println(smaps); 110*795d594fSAndroid Build Coastguard Worker } 111*795d594fSAndroid Build Coastguard Worker testDexFile()112*795d594fSAndroid Build Coastguard Worker private static Class<?> testDexFile() throws Exception { 113*795d594fSAndroid Build Coastguard Worker ClassLoader classLoader = Main.class.getClassLoader(); 114*795d594fSAndroid Build Coastguard Worker Class<?> DexFile = classLoader.loadClass("dalvik.system.DexFile"); 115*795d594fSAndroid Build Coastguard Worker Method DexFile_loadDex = DexFile.getMethod("loadDex", 116*795d594fSAndroid Build Coastguard Worker String.class, 117*795d594fSAndroid Build Coastguard Worker String.class, 118*795d594fSAndroid Build Coastguard Worker Integer.TYPE); 119*795d594fSAndroid Build Coastguard Worker Method DexFile_entries = DexFile.getMethod("entries"); 120*795d594fSAndroid Build Coastguard Worker Object dexFile = DexFile_loadDex.invoke(null, CLASS_PATH, null, 0); 121*795d594fSAndroid Build Coastguard Worker Enumeration<String> e = (Enumeration<String>) DexFile_entries.invoke(dexFile); 122*795d594fSAndroid Build Coastguard Worker while (e.hasMoreElements()) { 123*795d594fSAndroid Build Coastguard Worker String className = e.nextElement(); 124*795d594fSAndroid Build Coastguard Worker System.out.println(className); 125*795d594fSAndroid Build Coastguard Worker } 126*795d594fSAndroid Build Coastguard Worker 127*795d594fSAndroid Build Coastguard Worker Method DexFile_loadClass = DexFile.getMethod("loadClass", 128*795d594fSAndroid Build Coastguard Worker String.class, 129*795d594fSAndroid Build Coastguard Worker ClassLoader.class); 130*795d594fSAndroid Build Coastguard Worker Class<?> AnotherClass = (Class<?>)DexFile_loadClass.invoke(dexFile, 131*795d594fSAndroid Build Coastguard Worker "Another", Main.class.getClassLoader()); 132*795d594fSAndroid Build Coastguard Worker return AnotherClass; 133*795d594fSAndroid Build Coastguard Worker } 134*795d594fSAndroid Build Coastguard Worker } 135