• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.Field;
18 
19 public class Main {
main(String[] args)20     public static void main(String[] args) throws Exception {
21         if (!isDalvik) {
22           // This test is ART-specific. Just fake the expected output.
23           System.out.println("JNI_OnLoad called");
24           return;
25         }
26         System.loadLibrary(args[0]);
27         if (!hasJit()) {
28           return;
29         }
30         testValueOfArg();
31         testValueOfConst();
32     }
33 
testValueOfArg()34     public static void testValueOfArg() throws Exception {
35         final VolatileFlag start_end = new VolatileFlag();
36         Thread t = new Thread() {
37             @Override
38             public void run() {
39                 try {
40                     Class<?> integerCacheClass = Class.forName("java.lang.Integer$IntegerCache");
41                     Field cacheField = integerCacheClass.getDeclaredField("cache");
42                     cacheField.setAccessible(true);
43 
44                     Integer[] cache = (Integer[]) cacheField.get(integerCacheClass);
45                     Integer[] alt_cache = new Integer[cache.length];
46                     System.arraycopy(cache, 0, alt_cache, 0, cache.length);
47 
48                     // Let the main thread know that everything is set up.
49                     synchronized (start_end) {
50                         start_end.notify();
51                     }
52                     while (!start_end.flag) {
53                         cacheField.set(integerCacheClass, alt_cache);
54                         cacheField.set(integerCacheClass, cache);
55                     }
56                 } catch (Throwable t) {
57                     throw new Error(t);
58                 }
59             }
60         };
61         synchronized (start_end) {
62             t.start();
63             start_end.wait();  // Wait for the thread to start.
64         }
65         // Previously, this may have used an invalid IntegerValueOfInfo (because of seeing
66         // the `alt_cache` which is not in the boot image) when asked to emit code after
67         // using a valid info (using `cache`) when requesting locations.
68         ensureJitCompiled(Main.class, "getAsInteger");
69 
70         start_end.flag = true;
71         t.join();
72 
73         Runtime.getRuntime().gc();  // Collect the `alt_cache`.
74 
75         // If `getAsInteger()` was miscompiled, it shall try to retrieve an Integer reference
76         // from a collected array (low = 0, high = 0 means that this happens only for value 0),
77         // reading from a bogus location. Depending on the GC type, this bogus memory access may
78         // yield SIGSEGV or `null` or even a valid reference.
79         Integer new0 = getAsInteger(0);
80         int value = (int) new0;
81 
82         if (value != 0) {
83             throw new Error("value is " + value);
84         }
85     }
86 
testValueOfConst()87     public static void testValueOfConst() throws Exception {
88         Class<?> integerCacheClass = Class.forName("java.lang.Integer$IntegerCache");
89         Field cacheField = integerCacheClass.getDeclaredField("cache");
90         cacheField.setAccessible(true);
91         Field lowField = integerCacheClass.getDeclaredField("low");
92         lowField.setAccessible(true);
93 
94         Integer[] cache = (Integer[]) cacheField.get(integerCacheClass);
95         int low = (int) lowField.get(integerCacheClass);
96         Integer old42 = cache[42 - low];
97         cache[42 - low] = new Integer(42);
98 
99         // This used to hit
100         //     DCHECK(boxed != nullptr &&
101         //            Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
102         // when compiling the intrinsic.
103         ensureJitCompiled(Main.class, "get42AsInteger");
104 
105         cache[42 - low] = old42;
106         Runtime.getRuntime().gc();
107         Integer new42 = get42AsInteger();
108 
109         // If the DCHECK() was removed, MterpInvokeVirtualQuick() used to crash here.
110         // (Note: Our fault handler on x86-64 then also crashed.)
111         int value = (int) new42;
112 
113         if (value != (int) old42) {
114             throw new Error("value is " + value);
115         }
116     }
117 
118     private static class VolatileFlag {
119         public volatile boolean flag = false;
120     }
121 
get42AsInteger()122     public static Integer get42AsInteger() {
123         return Integer.valueOf(42);
124     }
125 
getAsInteger(int value)126     public static Integer getAsInteger(int value) {
127         return Integer.valueOf(value);
128     }
129 
hasJit()130     private native static boolean hasJit();
ensureJitCompiled(Class<?> itf, String method_name)131     private static native void ensureJitCompiled(Class<?> itf, String method_name);
132 
133     private final static boolean isDalvik = System.getProperty("java.vm.name").equals("Dalvik");
134 }
135