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