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