Fri, 14 Dec 2018 11:22:26 +0100
8215265: C2: range check elimination may allow illegal out of bound access
Reviewed-by: thartmann, kvn
1.1 --- a/src/share/vm/opto/loopTransform.cpp Fri Sep 28 14:24:22 2018 +0200 1.2 +++ b/src/share/vm/opto/loopTransform.cpp Fri Dec 14 11:22:26 2018 +0100 1.3 @@ -1537,13 +1537,20 @@ 1.4 1.5 //------------------------------adjust_limit----------------------------------- 1.6 // Helper function for add_constraint(). 1.7 -Node* PhaseIdealLoop::adjust_limit(int stride_con, Node * scale, Node *offset, Node *rc_limit, Node *loop_limit, Node *pre_ctrl) { 1.8 +Node* PhaseIdealLoop::adjust_limit(int stride_con, Node * scale, Node *offset, Node *rc_limit, Node *loop_limit, Node *pre_ctrl, bool round_up) { 1.9 // Compute "I :: (limit-offset)/scale" 1.10 Node *con = new (C) SubINode(rc_limit, offset); 1.11 register_new_node(con, pre_ctrl); 1.12 Node *X = new (C) DivINode(0, con, scale); 1.13 register_new_node(X, pre_ctrl); 1.14 1.15 + // When the absolute value of scale is greater than one, the integer 1.16 + // division may round limit down so add one to the limit. 1.17 + if (round_up) { 1.18 + X = new (C) AddINode(X, _igvn.intcon(1)); 1.19 + register_new_node(X, pre_ctrl); 1.20 + } 1.21 + 1.22 // Adjust loop limit 1.23 loop_limit = (stride_con > 0) 1.24 ? (Node*)(new (C) MinINode(loop_limit, X)) 1.25 @@ -1584,7 +1591,7 @@ 1.26 // (upper_limit-offset) may overflow or underflow. 1.27 // But it is fine since main loop will either have 1.28 // less iterations or will be skipped in such case. 1.29 - *main_limit = adjust_limit(stride_con, scale, offset, upper_limit, *main_limit, pre_ctrl); 1.30 + *main_limit = adjust_limit(stride_con, scale, offset, upper_limit, *main_limit, pre_ctrl, false); 1.31 1.32 // The underflow limit: low_limit <= scale*I+offset. 1.33 // For pre-loop compute 1.34 @@ -1620,7 +1627,8 @@ 1.35 // max(pre_limit, original_limit) is used in do_range_check(). 1.36 } 1.37 // Pass (-stride) to indicate pre_loop_cond = NOT(main_loop_cond); 1.38 - *pre_limit = adjust_limit((-stride_con), scale, offset, low_limit, *pre_limit, pre_ctrl); 1.39 + *pre_limit = adjust_limit((-stride_con), scale, offset, low_limit, *pre_limit, pre_ctrl, 1.40 + scale_con > 1 && stride_con > 0); 1.41 1.42 } else { // stride_con*scale_con < 0 1.43 // For negative stride*scale pre-loop checks for overflow and 1.44 @@ -1646,7 +1654,8 @@ 1.45 Node *plus_one = new (C) AddINode(offset, one); 1.46 register_new_node( plus_one, pre_ctrl ); 1.47 // Pass (-stride) to indicate pre_loop_cond = NOT(main_loop_cond); 1.48 - *pre_limit = adjust_limit((-stride_con), scale, plus_one, upper_limit, *pre_limit, pre_ctrl); 1.49 + *pre_limit = adjust_limit((-stride_con), scale, plus_one, upper_limit, *pre_limit, pre_ctrl, 1.50 + scale_con < -1 && stride_con > 0); 1.51 1.52 if (low_limit->get_int() == -max_jint) { 1.53 if (!RangeLimitCheck) return; 1.54 @@ -1681,7 +1690,8 @@ 1.55 // I > (low_limit-(offset+1))/scale 1.56 // ) 1.57 1.58 - *main_limit = adjust_limit(stride_con, scale, plus_one, low_limit, *main_limit, pre_ctrl); 1.59 + *main_limit = adjust_limit(stride_con, scale, plus_one, low_limit, *main_limit, pre_ctrl, 1.60 + false); 1.61 } 1.62 } 1.63
2.1 --- a/src/share/vm/opto/loopnode.hpp Fri Sep 28 14:24:22 2018 +0200 2.2 +++ b/src/share/vm/opto/loopnode.hpp Fri Dec 14 11:22:26 2018 +0100 2.3 @@ -959,7 +959,7 @@ 2.4 // loop. Scale_con, offset and limit are all loop invariant. 2.5 void add_constraint( int stride_con, int scale_con, Node *offset, Node *low_limit, Node *upper_limit, Node *pre_ctrl, Node **pre_limit, Node **main_limit ); 2.6 // Helper function for add_constraint(). 2.7 - Node* adjust_limit( int stride_con, Node * scale, Node *offset, Node *rc_limit, Node *loop_limit, Node *pre_ctrl ); 2.8 + Node* adjust_limit(int stride_con, Node * scale, Node *offset, Node *rc_limit, Node *loop_limit, Node *pre_ctrl, bool round_up); 2.9 2.10 // Partially peel loop up through last_peel node. 2.11 bool partial_peel( IdealLoopTree *loop, Node_List &old_new );
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/test/compiler/rangechecks/RangeCheckEliminationScaleNotOne.java Fri Dec 14 11:22:26 2018 +0100 3.3 @@ -0,0 +1,100 @@ 3.4 +/* 3.5 + * Copyright (c) 2018, Red Hat, Inc. All rights reserved. 3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 + * 3.8 + * This code is free software; you can redistribute it and/or modify it 3.9 + * under the terms of the GNU General Public License version 2 only, as 3.10 + * published by the Free Software Foundation. 3.11 + * 3.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.15 + * version 2 for more details (a copy is included in the LICENSE file that 3.16 + * accompanied this code). 3.17 + * 3.18 + * You should have received a copy of the GNU General Public License version 3.19 + * 2 along with this work; if not, write to the Free Software Foundation, 3.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.21 + * 3.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 3.23 + * or visit www.oracle.com if you need additional information or have any 3.24 + * questions. 3.25 + */ 3.26 + 3.27 +/* 3.28 + * @test 3.29 + * @bug 8215265 3.30 + * @summary C2: range check elimination may allow illegal out of bound access 3.31 + * 3.32 + * @run main/othervm -XX:-TieredCompilation -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:-UseLoopPredicate RangeCheckEliminationScaleNotOne 3.33 + * 3.34 + */ 3.35 + 3.36 +import java.util.Arrays; 3.37 + 3.38 +public class RangeCheckEliminationScaleNotOne { 3.39 + public static void main(String[] args) { 3.40 + { 3.41 + int[] array = new int[199]; 3.42 + boolean[] flags = new boolean[100]; 3.43 + Arrays.fill(flags, true); 3.44 + flags[0] = false; 3.45 + flags[1] = false; 3.46 + for (int i = 0; i < 20_000; i++) { 3.47 + test1(100, array, 0, flags); 3.48 + } 3.49 + boolean ex = false; 3.50 + try { 3.51 + test1(100, array, -5, flags); 3.52 + } catch (ArrayIndexOutOfBoundsException aie) { 3.53 + ex = true; 3.54 + } 3.55 + if (!ex) { 3.56 + throw new RuntimeException("no AIOOB exception"); 3.57 + } 3.58 + } 3.59 + 3.60 + { 3.61 + int[] array = new int[199]; 3.62 + boolean[] flags = new boolean[100]; 3.63 + Arrays.fill(flags, true); 3.64 + flags[0] = false; 3.65 + flags[1] = false; 3.66 + for (int i = 0; i < 20_000; i++) { 3.67 + test2(100, array, 198, flags); 3.68 + } 3.69 + boolean ex = false; 3.70 + try { 3.71 + test2(100, array, 203, flags); 3.72 + } catch (ArrayIndexOutOfBoundsException aie) { 3.73 + ex = true; 3.74 + } 3.75 + if (!ex) { 3.76 + throw new RuntimeException("no AIOOB exception"); 3.77 + } 3.78 + } 3.79 + } 3.80 + 3.81 + private static int test1(int stop, int[] array, int offset, boolean[] flags) { 3.82 + if (array == null) {} 3.83 + int res = 0; 3.84 + for (int i = 0; i < stop; i++) { 3.85 + if (flags[i]) { 3.86 + res += array[2 * i + offset]; 3.87 + } 3.88 + } 3.89 + return res; 3.90 + } 3.91 + 3.92 + 3.93 + private static int test2(int stop, int[] array, int offset, boolean[] flags) { 3.94 + if (array == null) {} 3.95 + int res = 0; 3.96 + for (int i = 0; i < stop; i++) { 3.97 + if (flags[i]) { 3.98 + res += array[-2 * i + offset]; 3.99 + } 3.100 + } 3.101 + return res; 3.102 + } 3.103 +}