Home | History | Annotate | Download | only in src
      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 {
     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 
     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 
     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 
    122     public static Integer get42AsInteger() {
    123         return Integer.valueOf(42);
    124     }
    125 
    126     public static Integer getAsInteger(int value) {
    127         return Integer.valueOf(value);
    128     }
    129 
    130     private native static boolean hasJit();
    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