/*
|
* 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;
|
}
|
}
|