/*
* 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<String> e = (Enumeration<String>) 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;
}
}