Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8262295: C2: Out-of-Bounds Array Load from Clone Source #2708

Closed

Conversation

reinrich
Copy link
Member

@reinrich reinrich commented Feb 24, 2021

This c2 fix makes the optimization of loads from the result array of a
Object.clone() call dependent on a compile time range check in order to prevent
out-of-bounds array loads described in JDK-8262295.

Testing: The included reproducer test. The fix passed also our CI testing: JCK
and JTREG, also in Xcomp mode, SPECjvm2008, SPECjbb2015, SAP specific tests with
fastdebug and release builds on all platforms.

Alternatively the transformed load could be made dependent on a range check at
runtime. Based on our automated benchmarking it wouldn't be worth
it. Our benchmark results include quite a bit of noise though.


Progress

  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed

Issue

  • JDK-8262295: C2: Out-of-Bounds Array Load from Clone Source

Reviewers

Download

To checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/2708/head:pull/2708
$ git checkout pull/2708

To update a local copy of the PR:
$ git checkout pull/2708
$ git pull https://git.openjdk.java.net/jdk pull/2708/head

@bridgekeeper
Copy link

bridgekeeper bot commented Feb 24, 2021

👋 Welcome back rrich! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Feb 24, 2021

@reinrich The following label will be automatically applied to this pull request:

  • hotspot-compiler

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command.

@openjdk openjdk bot added the hotspot-compiler hotspot-compiler-dev@openjdk.org label Feb 24, 2021
@reinrich reinrich force-pushed the 8262295_C2__Out_of_Bounds_Array_Load branch 2 times, most recently from c193fd9 to bba51bb Compare March 16, 2021 10:38
@reinrich reinrich force-pushed the 8262295_C2__Out_of_Bounds_Array_Load branch from bba51bb to a56f8d2 Compare March 16, 2021 11:07
@reinrich reinrich marked this pull request as ready for review March 17, 2021 11:03
@openjdk openjdk bot added the rfr Pull request is ready for review label Mar 17, 2021
@mlbridge
Copy link

mlbridge bot commented Mar 17, 2021

Webrevs

*
* @build sun.hotspot.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -XX:+UseSerialGC -Xmx128m
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add * @requires vm.gc.Serial to avoid conflict when testing env specifies different GC.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Good you spotted this.

@@ -533,7 +533,23 @@ 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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to check (ld_addp->is_AddP()) as at line #560.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I've added the check.

Node* src = ac->in(ArrayCopyNode::Src);
const TypeAryPtr* ary_t = phase->type(src)->isa_aryptr();

if (ary_t != NULL) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs Comment explaining what code does.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I've added a few lines of comment. Let me know if you think something's still missing.

Copy link
Contributor

@vnkozlov vnkozlov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice.

@openjdk
Copy link

openjdk bot commented Mar 18, 2021

@reinrich This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8262295: C2: Out-of-Bounds Array Load from Clone Source

Reviewed-by: kvn, roland, neliasso, thartmann

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 141 new commits pushed to the master branch:

  • fd3a33a: 8263189: C2: assert(!had_error) failed: bad dominance
  • 7b81f8e: 8263915: runtime/cds/appcds/MismatchedPathTriggerMemoryRelease.java fails when UseCompressedClassPointers is off
  • 2da882c: 8262465: Very long compilation times and high memory consumption in C2 debug builds
  • 0b03d04: 8167015: compiler/codecache/jmx/PoolsIndependenceTest.java timeout
  • df01b15: 8263977: GTK L&F: Cleanup duplicate checks in GTKStyle and GTKLookAndFeel
  • 57d8f1d: 8263985: BCEscapeAnalyzer::invoke checks target->is_loaded() twice
  • 4ef7c67: 8263979: Cleanup duplicate check in Unicode.contains
  • 289d48a: 8261673: Move javadoc for the lookup mechanism to module-info
  • 7b6efd3: 8263904: compiler/intrinsics/bmi/verifycode/BzhiTestI2L.java fails on x86_32
  • 036ae0e: 8225438: javax/net/ssl/TLSCommon/TestSessionLocalPrincipal.java failed with Read timed out
  • ... and 131 more: https://git.openjdk.java.net/jdk/compare/8afec70c283ee549795996031e3a53a3212bf35a...master

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Mar 18, 2021
@reinrich
Copy link
Member Author

Thank you for your review Vladimir.

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't there a risk of overflow with sizetype->_lo * elemsize + header?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. What about using jlong for elemsize and header?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. What about using jlong for elemsize and header?

Sounds ok to me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, done.

Copy link

@neliasso neliasso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

Copy link
Contributor

@rwestrel rwestrel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

@reinrich
Copy link
Member Author

Thanks for your reviews @neliasso and @rwestrel.

Copy link
Member

@TobiHartmann TobiHartmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Just wondering, is the load still hoisted if the source array is a constant array?

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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add parentheses around sizetype->_lo * elemsize + header.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@reinrich
Copy link
Member Author

Looks good to me. Just wondering, is the load still hoisted if the source array is a constant array?

In the example below the load from clone in L6 was hoisted to SRC.

   1	    public static final Integer[] SRC = new Integer[4096];
   2	    public static void testMethod_dontinline() throws Exception {
   3	        Object[] clone = SRC.clone();
   4	        escape1 = new Object();
   5	        if (SRC.length > 4) {
   6	            escape2 = clone[4]; // Load L
   7	        }
   8	    }

Actually the allocation + arraycopy in L3 could be eliminated if it wasn't for the possible deoptimization in L4 and the reference in L6. Would be cool to do the cloning lazily during deoptimization. But probably the gain isn't worth the effort/complexity.

@reinrich
Copy link
Member Author

Thanks for your review Tobias!

@TobiHartmann
Copy link
Member

In the example below the load from clone in L6 was hoisted to SRC.

   1	    public static final Integer[] SRC = new Integer[4096];
   2	    public static void testMethod_dontinline() throws Exception {
   3	        Object[] clone = SRC.clone();
   4	        escape1 = new Object();
   5	        if (SRC.length > 4) {
   6	            escape2 = clone[4]; // Load L
   7	        }
   8	    }

Actually the allocation + arraycopy in L3 could be eliminated if it wasn't for the possible deoptimization in L4 and the reference in L6. Would be cool to do the cloning lazily during deoptimization. But probably the gain isn't worth the effort/complexity.

Okay, thanks for verifying!

@vnkozlov
Copy link
Contributor

Looks good to me. Just wondering, is the load still hoisted if the source array is a constant array?

In the example below the load from clone in L6 was hoisted to SRC.

   1	    public static final Integer[] SRC = new Integer[4096];
   2	    public static void testMethod_dontinline() throws Exception {
   3	        Object[] clone = SRC.clone();
   4	        escape1 = new Object();
   5	        if (SRC.length > 4) {
   6	            escape2 = clone[4]; // Load L
   7	        }
   8	    }

Actually the allocation + arraycopy in L3 could be eliminated if it wasn't for the possible deoptimization in L4 and the reference in L6. Would be cool to do the cloning lazily during deoptimization. But probably the gain isn't worth the effort/complexity.

EA already does that - for small arrays (EliminateAllocationArraySizeLimit = 64). The issue is size because we have to put all SRC's elements on stack for reallocation during deoptimization. Changing size new Integer[16]; allows to eliminate new array allocation:

======== Connection graph for  TestOutOfBoundsArrayLoad::testMethod
JavaObject NoEscape(NoEscape) [ 87cp 314F [ 76 81 ]]   59  AllocateArray  ===  23  6  24  8  1 ( 57  36  31  42  1  21 ) [[ 60  61  62  74  75  76 ]]  rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, int ) TestOutOfBoundsArrayLoad::testMethod @ bci:3 (line 98) reexecute !jvms: TestOutOfBoundsArrayLoad::testMethod @ bci:3 (line 98)
LocalVar [ 59P [ 81 ]]   76  Proj  ===  59  [[ 77  81 ]] #5 !jvms: TestOutOfBoundsArrayLoad::testMethod @ bci:3 (line 98)
LocalVar [ 76 87cp 59P [ 314b ]]   81  CheckCastPP  ===  78  76  [[ 87  314  314  100 ]]  #narrowoop: java/lang/Integer:exact *[int:16]:NotNull:exact * !jvms: TestOutOfBoundsArrayLoad::testMethod @ bci:3 (line 98)

@reinrich
Copy link
Member Author

Looks good to me. Just wondering, is the load still hoisted if the source array is a constant array?

In the example below the load from clone in L6 was hoisted to SRC.

   1	    public static final Integer[] SRC = new Integer[4096];
   2	    public static void testMethod_dontinline() throws Exception {
   3	        Object[] clone = SRC.clone();
   4	        escape1 = new Object();
   5	        if (SRC.length > 4) {
   6	            escape2 = clone[4]; // Load L
   7	        }
   8	    }

Actually the allocation + arraycopy in L3 could be eliminated if it wasn't for the possible deoptimization in L4 and the reference in L6. Would be cool to do the cloning lazily during deoptimization. But probably the gain isn't worth the effort/complexity.

EA already does that - for small arrays (EliminateAllocationArraySizeLimit = 64). The issue is size because we have to put all SRC's elements on stack for reallocation during deoptimization. Changing size new Integer[16]; allows to eliminate new array allocation:

======== Connection graph for  TestOutOfBoundsArrayLoad::testMethod
JavaObject NoEscape(NoEscape) [ 87cp 314F [ 76 81 ]]   59  AllocateArray  ===  23  6  24  8  1 ( 57  36  31  42  1  21 ) [[ 60  61  62  74  75  76 ]]  rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, int ) TestOutOfBoundsArrayLoad::testMethod @ bci:3 (line 98) reexecute !jvms: TestOutOfBoundsArrayLoad::testMethod @ bci:3 (line 98)
LocalVar [ 59P [ 81 ]]   76  Proj  ===  59  [[ 77  81 ]] #5 !jvms: TestOutOfBoundsArrayLoad::testMethod @ bci:3 (line 98)
LocalVar [ 76 87cp 59P [ 314b ]]   81  CheckCastPP  ===  78  76  [[ 87  314  314  100 ]]  #narrowoop: java/lang/Integer:exact *[int:16]:NotNull:exact * !jvms: TestOutOfBoundsArrayLoad::testMethod @ bci:3 (line 98)

Yes, EA does it for small arrays.

I though it could be done for large ones too. What I meant was doing the allocation and then the arraycopy too on deoptimization but my thinking was a little naive of course :) as the src array might get modified before.

Sorry for that and thanks for pointing out that EA can do that for smaller arrays.

@reinrich
Copy link
Member Author

/integrate

@openjdk openjdk bot closed this Mar 25, 2021
@openjdk openjdk bot added integrated Pull request has been integrated and removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Mar 25, 2021
@openjdk
Copy link

openjdk bot commented Mar 25, 2021

@reinrich Since your change was applied there have been 194 commits pushed to the master branch:

  • a678a38: 8263743: redundant lock in SSLSocketImpl
  • 3fcb499: 8263768: JFormattedTextField.AbstractFormatter.getDocumentFilter()/getNavigationFilter() spec doesn't mention what the default impls return and what does it mean
  • 4155533: 8258753: StartTlsResponse.close() hangs due to synchronization issues
  • 3e18330: 8264018: AArch64: NEON loadV2 and storeV2 addressing is wrong
  • 0ff8168: 8258957: DocLint: check for HTML start element at end of body
  • 3d7f912: 8220266: add support for additional metadata in add/remove programs
  • 37f494c: 8260619: Add final modifier to several DataFlavor static fields
  • cfc9aa3: 8264002: Delete outdated assumptions about ColorSpace initialization
  • 623f0b6: 8262235: Remove unnecessary logic in hugetlbfs_sanity_check()
  • 1a13c9e: 8263473: Update annotation terminology (2)
  • ... and 184 more: https://git.openjdk.java.net/jdk/compare/8afec70c283ee549795996031e3a53a3212bf35a...master

Your commit was automatically rebased without conflicts.

Pushed as commit 9689863.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

@reinrich reinrich deleted the 8262295_C2__Out_of_Bounds_Array_Load branch April 1, 2021 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hotspot-compiler hotspot-compiler-dev@openjdk.org integrated Pull request has been integrated
5 participants