Skip to content
Browse files
8262295: C2: Out-of-Bounds Array Load from Clone Source
Reviewed-by: kvn, roland, neliasso, thartmann
  • Loading branch information
reinrich committed Mar 25, 2021
1 parent a678a38 commit 9689863ac0bac8c542162d4af30fec078e9c91b4
@@ -533,7 +533,30 @@ bool MemNode::detect_ptr_independence(Node* p1, AllocateNode* a1,
Node* LoadNode::find_previous_arraycopy(PhaseTransform* phase, Node* ld_alloc, Node*& mem, bool can_see_stored_value) const {
ArrayCopyNode* ac = find_array_copy_clone(phase, ld_alloc, mem);
if (ac != NULL) {
return ac;
Node* ld_addp = in(MemNode::Address);
Node* src = ac->in(ArrayCopyNode::Src);
const TypeAryPtr* ary_t = phase->type(src)->isa_aryptr();

// This is a load from a cloned array. The corresponding arraycopy ac must
// have set the value for the load and we can return ac but only if the load
// is known to be within bounds. This is checked below.
if (ary_t != NULL && ld_addp->is_AddP()) {
Node* ld_offs = ld_addp->in(AddPNode::Offset);
BasicType ary_elem = ary_t->klass()->as_array_klass()->element_type()->basic_type();
jlong header = arrayOopDesc::base_offset_in_bytes(ary_elem);
jlong elemsize = type2aelembytes(ary_elem);

const TypeX* ld_offs_t = phase->type(ld_offs)->isa_intptr_t();
const TypeInt* sizetype = ary_t->size();

if (ld_offs_t->_lo >= header && ld_offs_t->_hi < (sizetype->_lo * elemsize + header)) {
// The load is known to be within bounds. It receives its value from ac.
return ac;
// The load is known to be out-of-bounds.
// The load could be out-of-bounds. It must not be hoisted but must remain
// dependent on the runtime range check. This is achieved by returning NULL.
} else if (mem->is_Proj() && mem->in(0) != NULL && mem->in(0)->is_ArrayCopy()) {
ArrayCopyNode* ac = mem->in(0)->as_ArrayCopy();

@@ -0,0 +1,96 @@
* Copyright (c) 2021 SAP SE. All rights reserved.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit if you need additional information or have any
* questions.

* @test
* @requires vm.gc.Serial
* @bug 8262295
* @library /test/lib /
* @summary Out of bounds array load on clone source crashes GC which
* interpretes the loaded value as oop. A small heap is configured to
* get a lot of GCs.
* @comment C2 generates the out of bounds load with serial, parallel and
* shenandoah gc but not with g1 and z gc. For simplicity serial gc is
* configured.
* @build sun.hotspot.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -XX:+UseSerialGC -Xmx128m
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* -XX:-BackgroundCompilation
* -XX:CompileCommand=dontinline,*::*_dontinline
* compiler.arraycopy.TestOutOfBoundsArrayLoad

package compiler.arraycopy;

import compiler.whitebox.CompilerWhiteBoxTest;

public class TestOutOfBoundsArrayLoad {

public static Object escape1;
public static Object escape2;

public static void main(String[] args_ignored) {
try {
Object[] arrNotEmpty = {null, null, null, null, null, };

// Warm-up
for (int i = CompilerWhiteBoxTest.THRESHOLD; i > 0; i--) {
// Call testmethod with empty array often enough to trigger GC.
// GC is assumed to crash.
for (int i = 20_000_000; i > 0; i--) {
// Trick for ParallelGC: empty[4] will be loaded in the testmethod
// (out of bounds!) and interpreted as oop (or
// narrowOop). PSScavenge::should_scavenge() will skip the loaded
// value if it is before the young generation. So before calling the
// test method we allocate the empty array and an array of -1 values
// right behind it. So empty[4] will likely result in
// 0xffffffffffffffff Which is not before the young generation.
Object[] empty = new Object[0];
long[] l = new long[4];
l[0] = -1L; l[1] = -1L; l[2] = -1L; l[3] = -1L;
escape2 = l;
} catch (Throwable t) {

public static void testMethod_dontinline(Object[] src) throws Exception {
Object[] clone = src.clone();
// Load L below is executed speculatively at this point from src without range check.
// The result is put into the OopMap of the allocation in the next line.
// If src.length is 0 then the loaded value is no heap reference and GC crashes.
escape1 = new Object();
if (src.length > 4) {
escape2 = clone[4]; // Load L

1 comment on commit 9689863


This comment has been minimized.

Copy link

@openjdk-notifier openjdk-notifier bot commented on 9689863 Mar 25, 2021

Please sign in to comment.