/* * 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.Method; import java.util.Enumeration; import java.nio.file.Files; import java.nio.file.Paths; /** * DexFile tests (Dalvik-specific). */ public class Main { private static final String CLASS_PATH = System.getenv("DEX_LOCATION") + "/071-dexfile-map-clean-ex.jar"; /** * Prep the environment then run the test. */ public static void main(String[] args) throws Exception { // Load the dex file, this is a pre-requisite to mmap-ing it in. Class AnotherClass = testDexFile(); // Check that the memory maps are clean. testDexMemoryMaps(); // Prevent garbage collector from collecting our DexFile // (and unmapping too early) by using it after we finish // our verification. AnotherClass.newInstance(); } private static boolean checkSmapsEntry(String[] smapsLines, int offset) { String nameDescription = smapsLines[offset]; String[] split = nameDescription.split(" "); String permissions = split[1]; // Mapped as read-only + anonymous. if (!permissions.startsWith("r--p")) { return false; } boolean validated = false; // We have the right entry, now make sure it's valid. for (int i = offset; i < smapsLines.length; ++i) { String line = smapsLines[i]; if (line.startsWith("Shared_Dirty") || line.startsWith("Private_Dirty")) { String lineTrimmed = line.trim(); String[] lineSplit = lineTrimmed.split(" +"); String sizeUsuallyInKb = lineSplit[lineSplit.length - 2]; sizeUsuallyInKb = sizeUsuallyInKb.trim(); if (!sizeUsuallyInKb.equals("0")) { System.out.println( "ERROR: Memory mapping for " + CLASS_PATH + " is unexpectedly dirty"); System.out.println(line); } else { validated = true; } } // VmFlags marks the "end" of an smaps entry. if (line.startsWith("VmFlags")) { break; } } if (validated) { System.out.println("Secondary dexfile mmap is clean"); } else { System.out.println("ERROR: Memory mapping is missing Shared_Dirty/Private_Dirty entries"); } return true; } private static void testDexMemoryMaps() throws Exception { // Ensure that the secondary dex file is mapped clean (directly from JAR file). String smaps = new String(Files.readAllBytes(Paths.get("/proc/self/smaps"))); String[] smapsLines = smaps.split("\n"); boolean found = true; for (int i = 0; i < smapsLines.length; ++i) { if (smapsLines[i].contains(CLASS_PATH)) { if (checkSmapsEntry(smapsLines, i)) { return; } // else we found the wrong one, keep going. } } // Error case: System.out.println("Could not find " + CLASS_PATH + " RO-anonymous smaps entry"); System.out.println(smaps); } private static Class testDexFile() throws Exception { ClassLoader classLoader = Main.class.getClassLoader(); Class DexFile = classLoader.loadClass("dalvik.system.DexFile"); Method DexFile_loadDex = DexFile.getMethod("loadDex", String.class, String.class, Integer.TYPE); Method DexFile_entries = DexFile.getMethod("entries"); Object dexFile = DexFile_loadDex.invoke(null, CLASS_PATH, null, 0); Enumeration e = (Enumeration) DexFile_entries.invoke(dexFile); while (e.hasMoreElements()) { String className = e.nextElement(); System.out.println(className); } Method DexFile_loadClass = DexFile.getMethod("loadClass", String.class, ClassLoader.class); Class AnotherClass = (Class)DexFile_loadClass.invoke(dexFile, "Another", Main.class.getClassLoader()); return AnotherClass; } }