1 /*
2  * Copyright (c) 2014, 2021, 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 jdk.test.lib.helpers.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)
value()58     @interface Args { int[] value(); }
59 
60     // first range check is i + max of all constants
61     @Args({0, 8})
m1(int[] array, int i, boolean allaccesses)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})
m2(int[] array, int i, boolean allaccesses)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})
m3(int[] array, int i, boolean allaccesses)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})
m4(int[] array, int i, boolean allaccesses)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})
m5(int[] array, int i, boolean allaccesses)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})
m6(int[] array, int i, boolean allaccesses)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})
m7(int[] array, int i, boolean allaccesses)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})
m8(int[] array, int i, boolean allaccesses)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})
m9(int[] array, int i, boolean allaccesses)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})
m10(int[] array, int i, boolean allaccesses)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})
m11(int[] array, int i, boolean allaccesses)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})
m12(int[] array, int i, boolean allaccesses)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})
m13(int[] array, int i, boolean ignore)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})
m14(int[] array, int i, boolean ignore)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})
m15(int[] array, int i, boolean ignore)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})
m16(int[] array, int i, boolean ignore)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})
m17(int[] array, int i, boolean ignore)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 
main(String[] args)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;
Arrays.equals()342                 assert(Arrays.equals(m.getParameterTypes(), TEST_PARAM_TYPES)) : m;
m.getName()343                 tests.put(m.getName(), m);
344             }
345         }
346     }
347 
invokeTest(Method m, int[] array, int index, boolean z)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 
doTest(String name)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     }
doTests()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 }
447