1 /*
   2  * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8066103
  27  * @summary C2's range check smearing allows out of bound array accesses
  28  * @library /test/lib /
  29  * @modules java.base/jdk.internal.misc
  30  *          java.management
  31  * @build sun.hotspot.WhiteBox
  32  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  33  * @run main/othervm -ea -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  34  *                   -XX:-BackgroundCompilation -XX:-UseOnStackReplacement
  35  *                   -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+EagerJVMCI
  36  *                   compiler.rangechecks.TestRangeCheckSmearing
  37  *
  38  */
  39 
  40 package compiler.rangechecks;
  41 
  42 import compiler.whitebox.CompilerWhiteBoxTest;
  43 import compiler.testlibrary.CompilerUtils;
  44 import jdk.test.lib.Platform;
  45 import sun.hotspot.WhiteBox;
  46 
  47 import java.lang.annotation.Retention;
  48 import java.lang.annotation.RetentionPolicy;
  49 import java.lang.reflect.Method;
  50 import java.lang.reflect.Modifier;
  51 import java.util.Arrays;
  52 import java.util.HashMap;
  53 
  54 public class TestRangeCheckSmearing {
  55     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
  56 
  57     @Retention(RetentionPolicy.RUNTIME)
  58     @interface Args { int[] value(); }
  59 
  60     // first range check is i + max of all constants
  61     @Args({0, 8})
  62     static int m1(int[] array, int i, boolean allaccesses) {
  63         int res = 0;
  64         res += array[i+9];
  65         if (allaccesses) {
  66             res += array[i+8];
  67             res += array[i+7];
  68             res += array[i+6];
  69             res += array[i+5];
  70             res += array[i+4];
  71             res += array[i+3];
  72             res += array[i+2];
  73             res += array[i+1];
  74         }
  75         return res;
  76     }
  77 
  78     // first range check is i + min of all constants
  79     @Args({0, -9})
  80     static int m2(int[] array, int i, boolean allaccesses) {
  81         int res = 0;
  82         res += array[i+1];
  83         if (allaccesses) {
  84             res += array[i+2];
  85             res += array[i+3];
  86             res += array[i+4];
  87             res += array[i+5];
  88             res += array[i+6];
  89             res += array[i+7];
  90             res += array[i+8];
  91             res += array[i+9];
  92         }
  93         return res;
  94     }
  95 
  96     // first range check is not i + min/max of all constants
  97     @Args({0, 8})
  98     static int m3(int[] array, int i, boolean allaccesses) {
  99         int res = 0;
 100         res += array[i+3];
 101         if (allaccesses) {
 102             res += array[i+2];
 103             res += array[i+1];
 104             res += array[i+4];
 105             res += array[i+5];
 106             res += array[i+6];
 107             res += array[i+7];
 108             res += array[i+8];
 109             res += array[i+9];
 110         }
 111         return res;
 112     }
 113 
 114     @Args({0, -9})
 115     static int m4(int[] array, int i, boolean allaccesses) {
 116         int res = 0;
 117         res += array[i+3];
 118         if (allaccesses) {
 119             res += array[i+4];
 120             res += array[i+1];
 121             res += array[i+2];
 122             res += array[i+5];
 123             res += array[i+6];
 124             res += array[i+7];
 125             res += array[i+8];
 126             res += array[i+9];
 127         }
 128         return res;
 129     }
 130 
 131     @Args({0, -3})
 132     static int m5(int[] array, int i, boolean allaccesses) {
 133         int res = 0;
 134         res += array[i+3];
 135         res += array[i+2];
 136         if (allaccesses) {
 137             res += array[i+1];
 138             res += array[i+4];
 139             res += array[i+5];
 140             res += array[i+6];
 141             res += array[i+7];
 142             res += array[i+8];
 143             res += array[i+9];
 144         }
 145         return res;
 146     }
 147 
 148     @Args({0, 6})
 149     static int m6(int[] array, int i, boolean allaccesses) {
 150         int res = 0;
 151         res += array[i+3];
 152         res += array[i+4];
 153         if (allaccesses) {
 154             res += array[i+2];
 155             res += array[i+1];
 156             res += array[i+5];
 157             res += array[i+6];
 158             res += array[i+7];
 159             res += array[i+8];
 160             res += array[i+9];
 161         }
 162         return res;
 163     }
 164 
 165     @Args({0, 6})
 166     static int m7(int[] array, int i, boolean allaccesses) {
 167         int res = 0;
 168         res += array[i+3];
 169         res += array[i+2];
 170         res += array[i+4];
 171         if (allaccesses) {
 172             res += array[i+1];
 173             res += array[i+5];
 174             res += array[i+6];
 175             res += array[i+7];
 176             res += array[i+8];
 177             res += array[i+9];
 178         }
 179         return res;
 180     }
 181 
 182     @Args({0, -3})
 183     static int m8(int[] array, int i, boolean allaccesses) {
 184         int res = 0;
 185         res += array[i+3];
 186         res += array[i+4];
 187         res += array[i+2];
 188         if (allaccesses) {
 189             res += array[i+1];
 190             res += array[i+5];
 191             res += array[i+6];
 192             res += array[i+7];
 193             res += array[i+8];
 194             res += array[i+9];
 195         }
 196         return res;
 197     }
 198 
 199     @Args({6, 15})
 200     static int m9(int[] array, int i, boolean allaccesses) {
 201         int res = 0;
 202         res += array[i+3];
 203         if (allaccesses) {
 204             res += array[i-2];
 205             res += array[i-1];
 206             res += array[i-4];
 207             res += array[i-5];
 208             res += array[i-6];
 209         }
 210         return res;
 211     }
 212 
 213     @Args({3, 12})
 214     static int m10(int[] array, int i, boolean allaccesses) {
 215         int res = 0;
 216         res += array[i+3];
 217         if (allaccesses) {
 218             res += array[i-2];
 219             res += array[i-1];
 220             res += array[i-3];
 221             res += array[i+4];
 222             res += array[i+5];
 223             res += array[i+6];
 224         }
 225         return res;
 226     }
 227 
 228     @Args({3, -3})
 229     static int m11(int[] array, int i, boolean allaccesses) {
 230         int res = 0;
 231         res += array[i+3];
 232         res += array[i-2];
 233         if (allaccesses) {
 234             res += array[i+5];
 235             res += array[i+6];
 236         }
 237         return res;
 238     }
 239 
 240     @Args({3, 6})
 241     static int m12(int[] array, int i, boolean allaccesses) {
 242         int res = 0;
 243         res += array[i+3];
 244         res += array[i+6];
 245         if (allaccesses) {
 246             res += array[i-2];
 247             res += array[i-3];
 248         }
 249         return res;
 250     }
 251 
 252     // check that identical range check is replaced by dominating one
 253     // only when correct
 254     @Args({0})
 255     static int m13(int[] array, int i, boolean ignore) {
 256         int res = 0;
 257         res += array[i+3];
 258         res += array[i+3];
 259         return res;
 260     }
 261 
 262     @Args({2, 0})
 263     static int m14(int[] array, int i, boolean ignore) {
 264         int res = 0;
 265 
 266         res += array[i];
 267         res += array[i-2];
 268         res += array[i]; // If range check below were to be removed first this cannot be considered identical to first range check
 269         res += array[i-1]; // range check removed so i-1 array access depends on previous check
 270 
 271         return res;
 272     }
 273 
 274     static int[] m15_dummy = new int[10];
 275     @Args({2, 0})
 276     static int m15(int[] array, int i, boolean ignore) {
 277         int res = 0;
 278         res += array[i];
 279 
 280         // When the loop is optimized out we don't want the
 281         // array[i-1] access which is dependent on array[i]'s
 282         // range check to become dependent on the identical range
 283         // check above.
 284 
 285         int[] array2 = m15_dummy;
 286         int j = 0;
 287         for (; j < 10; j++);
 288         if (j == 10) {
 289             array2 = array;
 290         }
 291 
 292         res += array2[i-2];
 293         res += array2[i];
 294         res += array2[i-1]; // range check removed so i-1 array access depends on previous check
 295 
 296         return res;
 297     }
 298 
 299     @Args({2, 0})
 300     static int m16(int[] array, int i, boolean ignore) {
 301         int res = 0;
 302 
 303         res += array[i];
 304         res += array[i-1];
 305         res += array[i-1];
 306         res += array[i-2];
 307 
 308         return res;
 309     }
 310 
 311     @Args({2, 0})
 312     static int m17(int[] array, int i, boolean ignore) {
 313         int res = 0;
 314 
 315         res += array[i];
 316         res += array[i-2];
 317         res += array[i-2];
 318         res += array[i+2];
 319         res += array[i+2];
 320         res += array[i-1];
 321         res += array[i-1];
 322 
 323         return res;
 324     }
 325 
 326     static public void main(String[] args) {
 327         if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) {
 328             throw new AssertionError("Background compilation enabled");
 329         }
 330         new TestRangeCheckSmearing().doTests();
 331     }
 332     boolean success = true;
 333     boolean exception = false;
 334     final int[] array = new int[10];
 335     final HashMap<String,Method> tests = new HashMap<>();
 336     {
 337         final Class<?> TEST_PARAM_TYPES[] = { int[].class, int.class, boolean.class };
 338         for (Method m : this.getClass().getDeclaredMethods()) {
 339             if (m.getName().matches("m[0-9]+")) {
 340                 assert(Modifier.isStatic(m.getModifiers())) : m;
 341                 assert(m.getReturnType() == int.class) : m;
 342                 assert(Arrays.equals(m.getParameterTypes(), TEST_PARAM_TYPES)) : m;
 343                 tests.put(m.getName(), m);
 344             }
 345         }
 346     }
 347 
 348     void invokeTest(Method m, int[] array, int index, boolean z) {
 349         try {
 350             m.invoke(null, array, index, z);
 351         } catch (ReflectiveOperationException roe) {
 352             Throwable ex = roe.getCause();
 353             if (ex instanceof ArrayIndexOutOfBoundsException)
 354                 throw (ArrayIndexOutOfBoundsException) ex;
 355             throw new AssertionError(roe);
 356         }
 357     }
 358 
 359     void doTest(String name) {
 360         Method m = tests.get(name);
 361         tests.remove(name);
 362         int[] args = m.getAnnotation(Args.class).value();
 363         int index0 = args[0], index1;
 364         boolean exceptionRequired = true;
 365         if (args.length == 2) {
 366             index1 = args[1];
 367         } else {
 368             // no negative test for this one
 369             assert(args.length == 1);
 370             assert(name.equals("m13"));
 371             exceptionRequired = false;
 372             index1 = index0;
 373         }
 374         // Get the method compiled.
 375         if (!WHITE_BOX.isMethodCompiled(m)) {
 376             // If not, try to compile it with C2
 377             if(!WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) {
 378                 // C2 compiler not available, try to compile with C1
 379                 WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE);
 380             }
 381         }
 382         if (!WHITE_BOX.isMethodCompiled(m)) {
 383             throw new RuntimeException(m + " not compiled");
 384         }
 385 
 386         // valid access
 387         invokeTest(m, array, index0, true);
 388 
 389         if (!WHITE_BOX.isMethodCompiled(m)) {
 390             throw new RuntimeException(m + " deoptimized on valid array access");
 391         }
 392 
 393         exception = false;
 394         boolean test_success = true;
 395         try {
 396             invokeTest(m, array, index1, false);
 397         } catch(ArrayIndexOutOfBoundsException aioob) {
 398             exception = true;
 399             System.out.println("ArrayIndexOutOfBoundsException thrown in "+name);
 400         }
 401         if (!exception) {
 402             System.out.println("ArrayIndexOutOfBoundsException was not thrown in "+name);
 403         }
 404 
 405         if (CompilerUtils.getMaxCompilationLevel() == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) {
 406             if (exceptionRequired == WHITE_BOX.isMethodCompiled(m)) {
 407                 System.out.println((exceptionRequired?"Didn't deoptimized":"deoptimized") + " in "+name);
 408                 test_success = false;
 409             }
 410         }
 411 
 412         if (exception != exceptionRequired) {
 413             System.out.println((exceptionRequired?"exception required but not thrown":"not exception required but thrown") + " in "+name);
 414             test_success = false;
 415         }
 416 
 417         if (!test_success) {
 418             success = false;
 419             System.out.println("TEST FAILED: "+name);
 420         }
 421 
 422     }
 423     void doTests() {
 424         doTest("m1");
 425         doTest("m2");
 426         doTest("m3");
 427         doTest("m4");
 428         doTest("m5");
 429         doTest("m6");
 430         doTest("m7");
 431         doTest("m8");
 432         doTest("m9");
 433         doTest("m10");
 434         doTest("m11");
 435         doTest("m12");
 436         doTest("m13");
 437         doTest("m14");
 438         doTest("m15");
 439         doTest("m16");
 440         doTest("m17");
 441         if (!success) {
 442             throw new RuntimeException("Some tests failed");
 443         }
 444         assert(tests.isEmpty()) : tests;
 445     }
 446 }