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
Contributor

@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 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 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.

@reinrich reinrich force-pushed the 8262295_C2__Out_of_Bounds_Array_Load branch 2 times, most recently from c193fd9 to bba51bb Mar 16, 2021
@reinrich reinrich force-pushed the 8262295_C2__Out_of_Bounds_Array_Load branch from bba51bb to a56f8d2 Mar 16, 2021
@reinrich reinrich marked this pull request as ready for review Mar 17, 2021
@openjdk openjdk bot added the rfr label Mar 17, 2021
@mlbridge
Copy link

@mlbridge mlbridge bot commented Mar 17, 2021

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

@vnkozlov vnkozlov Mar 17, 2021

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

Copy link
Contributor Author

@reinrich reinrich Mar 18, 2021

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

@vnkozlov vnkozlov Mar 17, 2021

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

Copy link
Contributor Author

@reinrich reinrich Mar 18, 2021

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

@vnkozlov vnkozlov Mar 17, 2021

Needs Comment explaining what code does.

Copy link
Contributor Author

@reinrich reinrich Mar 18, 2021

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

Nice.

@openjdk
Copy link

@openjdk 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 label Mar 18, 2021
@reinrich
Copy link
Contributor Author

@reinrich reinrich commented Mar 18, 2021

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

@rwestrel rwestrel Mar 19, 2021

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

Copy link
Contributor Author

@reinrich reinrich Mar 19, 2021

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

Copy link
Contributor

@rwestrel rwestrel Mar 19, 2021

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

Sounds ok to me.

Copy link
Contributor Author

@reinrich reinrich Mar 19, 2021

Ok, done.

Copy link
Contributor

@neliasso neliasso left a comment

Looks good!

Copy link
Contributor

@rwestrel rwestrel left a comment

Looks good to me.

@reinrich
Copy link
Contributor Author

@reinrich reinrich commented Mar 22, 2021

Thanks for your reviews @neliasso and @rwestrel.

Copy link
Member

@TobiHartmann TobiHartmann left a comment

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

@TobiHartmann TobiHartmann Mar 23, 2021

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

Copy link
Contributor Author

@reinrich reinrich Mar 23, 2021

Done.

@reinrich
Copy link
Contributor Author

@reinrich reinrich commented Mar 23, 2021

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
Contributor Author

@reinrich reinrich commented Mar 23, 2021

Thanks for your review Tobias!

@TobiHartmann
Copy link
Member

@TobiHartmann TobiHartmann commented Mar 23, 2021

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

@vnkozlov vnkozlov commented Mar 23, 2021

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
Contributor Author

@reinrich reinrich commented Mar 23, 2021

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
Contributor Author

@reinrich reinrich commented Mar 25, 2021

/integrate

@openjdk openjdk bot closed this Mar 25, 2021
@openjdk openjdk bot added integrated and removed ready rfr labels Mar 25, 2021
@openjdk
Copy link

@openjdk 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 Apr 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
5 participants