From c0d0292a210ff3c604747fb73c77008a3d94214c Mon Sep 17 00:00:00 2001 From: nibjen Date: Tue, 23 Jul 2024 11:06:45 +0000 Subject: [PATCH 01/14] 8336363: Update the release version after forking Oct CPU24_10 Reviewed-by: coffeys, rreddy --- .jcheck/conf | 2 +- make/conf/version-numbers.conf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.jcheck/conf b/.jcheck/conf index b4cdc92ddbf..72708949e2e 100644 --- a/.jcheck/conf +++ b/.jcheck/conf @@ -1,7 +1,7 @@ [general] project=jdk-updates jbs=JDK -version=23.0.1 +version=23.0.2 [checks] error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index 472c89cdab2..aec48255d03 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -28,12 +28,12 @@ DEFAULT_VERSION_FEATURE=23 DEFAULT_VERSION_INTERIM=0 -DEFAULT_VERSION_UPDATE=1 +DEFAULT_VERSION_UPDATE=2 DEFAULT_VERSION_PATCH=0 DEFAULT_VERSION_EXTRA1=0 DEFAULT_VERSION_EXTRA2=0 DEFAULT_VERSION_EXTRA3=0 -DEFAULT_VERSION_DATE=2024-10-15 +DEFAULT_VERSION_DATE=2025-01-21 DEFAULT_VERSION_CLASSFILE_MAJOR=67 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 From 4e22d8c050a697d2de8f818df2ff1786c0ea0766 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 23 Jul 2024 11:46:58 +0000 Subject: [PATCH 02/14] 8335713: Enhance vectorization analysis Co-authored-by: Roland Westrelin Reviewed-by: kvn, thartmann Backport-of: 3c05ad2290936ec9abc3f271cb6bf89e18c3eea7 --- src/hotspot/share/opto/vectorization.cpp | 76 ++++++++++++++++++++---- src/hotspot/share/opto/vectorization.hpp | 1 + 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/hotspot/share/opto/vectorization.cpp b/src/hotspot/share/opto/vectorization.cpp index cd34e5a9b74..153877e8065 100644 --- a/src/hotspot/share/opto/vectorization.cpp +++ b/src/hotspot/share/opto/vectorization.cpp @@ -944,11 +944,40 @@ bool VPointer::scaled_iv_plus_offset(Node* n) { } } else if (opc == Op_SubI || opc == Op_SubL) { if (offset_plus_k(n->in(2), true) && scaled_iv_plus_offset(n->in(1))) { + // (offset1 + invar1 + scale * iv) - (offset2 + invar2) + // Subtraction handled via "negate" flag of "offset_plus_k". NOT_PRODUCT(_tracer.scaled_iv_plus_offset_6(n);) return true; } - if (offset_plus_k(n->in(1)) && scaled_iv_plus_offset(n->in(2))) { - _scale *= -1; + VPointer tmp(this); + if (offset_plus_k(n->in(1)) && tmp.scaled_iv_plus_offset(n->in(2))) { + // (offset1 + invar1) - (offset2 + invar2 + scale * iv) + // Subtraction handled explicitly below. + assert(_scale == 0, "shouldn't be set yet"); + // _scale = -tmp._scale + if (!try_MulI_no_overflow(-1, tmp._scale, _scale)) { + return false; // mul overflow. + } + // _offset -= tmp._offset + if (!try_SubI_no_overflow(_offset, tmp._offset, _offset)) { + return false; // sub overflow. + } + // _invar -= tmp._invar + if (tmp._invar != nullptr) { + maybe_add_to_invar(tmp._invar, true); +#ifdef ASSERT + _debug_invar_scale = tmp._debug_invar_scale; + _debug_negate_invar = !tmp._debug_negate_invar; +#endif + } + + // Forward info about the int_index: + assert(!_has_int_index_after_convI2L, "no previous int_index discovered"); + _has_int_index_after_convI2L = tmp._has_int_index_after_convI2L; + _int_index_after_convI2L_offset = tmp._int_index_after_convI2L_offset; + _int_index_after_convI2L_invar = tmp._int_index_after_convI2L_invar; + _int_index_after_convI2L_scale = tmp._int_index_after_convI2L_scale; + NOT_PRODUCT(_tracer.scaled_iv_plus_offset_7(n);) return true; } @@ -990,7 +1019,9 @@ bool VPointer::scaled_iv(Node* n) { } } else if (opc == Op_LShiftI) { if (n->in(1) == iv() && n->in(2)->is_Con()) { - _scale = 1 << n->in(2)->get_int(); + if (!try_LShiftI_no_overflow(1, n->in(2)->get_int(), _scale)) { + return false; // shift overflow. + } NOT_PRODUCT(_tracer.scaled_iv_6(n, _scale);) return true; } @@ -1013,15 +1044,24 @@ bool VPointer::scaled_iv(Node* n) { if (tmp.scaled_iv_plus_offset(n->in(1)) && tmp.has_iv()) { // We successfully matched an integer index, of the form: // int_index = int_offset + int_invar + int_scale * iv + // Forward scale. + assert(_scale == 0 && tmp._scale != 0, "iv only found just now"); + _scale = tmp._scale; + // Accumulate offset. + if (!try_AddI_no_overflow(_offset, tmp._offset, _offset)) { + return false; // add overflow. + } + // Accumulate invar. + if (tmp._invar != nullptr) { + maybe_add_to_invar(tmp._invar, false); + } + // Set info about the int_index: + assert(!_has_int_index_after_convI2L, "no previous int_index discovered"); _has_int_index_after_convI2L = true; _int_index_after_convI2L_offset = tmp._offset; _int_index_after_convI2L_invar = tmp._invar; _int_index_after_convI2L_scale = tmp._scale; - } - // Now parse it again for the real VPointer. This makes sure that the int_offset, int_invar, - // and int_scale are properly added to the final VPointer's offset, invar, and scale. - if (scaled_iv_plus_offset(n->in(1))) { NOT_PRODUCT(_tracer.scaled_iv_7(n);) return true; } @@ -1040,12 +1080,14 @@ bool VPointer::scaled_iv(Node* n) { NOT_PRODUCT(_tracer.scaled_iv_8(n, &tmp);) if (tmp.scaled_iv_plus_offset(n->in(1))) { - int scale = n->in(2)->get_int(); + int shift = n->in(2)->get_int(); // Accumulate scale. - _scale = tmp._scale << scale; + if (!try_LShiftI_no_overflow(tmp._scale, shift, _scale)) { + return false; // shift overflow. + } // Accumulate offset. int shifted_offset = 0; - if (!try_LShiftI_no_overflow(tmp._offset, scale, shifted_offset)) { + if (!try_LShiftI_no_overflow(tmp._offset, shift, shifted_offset)) { return false; // shift overflow. } if (!try_AddI_no_overflow(_offset, shifted_offset, _offset)) { @@ -1062,6 +1104,7 @@ bool VPointer::scaled_iv(Node* n) { } // Forward info about the int_index: + assert(!_has_int_index_after_convI2L, "no previous int_index discovered"); _has_int_index_after_convI2L = tmp._has_int_index_after_convI2L; _int_index_after_convI2L_offset = tmp._int_index_after_convI2L_offset; _int_index_after_convI2L_invar = tmp._int_index_after_convI2L_invar; @@ -1256,6 +1299,9 @@ bool VPointer::try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, in } bool VPointer::try_LShiftI_no_overflow(int offset, int shift, int& result) { + if (shift < 0 || shift > 31) { + return false; + } jlong long_offset = java_shift_left((jlong)(offset), shift); jint int_offset = java_shift_left( offset, shift); if (long_offset != int_offset) { @@ -1265,6 +1311,16 @@ bool VPointer::try_LShiftI_no_overflow(int offset, int shift, int& result) { return true; } +bool VPointer::try_MulI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_multiply((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_multiply( offset1, offset2); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + #ifndef PRODUCT // Function for printing the fields of a VPointer void VPointer::print() const { diff --git a/src/hotspot/share/opto/vectorization.hpp b/src/hotspot/share/opto/vectorization.hpp index 2426e4d0361..2d164b824ad 100644 --- a/src/hotspot/share/opto/vectorization.hpp +++ b/src/hotspot/share/opto/vectorization.hpp @@ -935,6 +935,7 @@ class VPointer : public ArenaObj { static bool try_SubI_no_overflow(int offset1, int offset2, int& result); static bool try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result); static bool try_LShiftI_no_overflow(int offset1, int offset2, int& result); + static bool try_MulI_no_overflow(int offset1, int offset2, int& result); Node* register_if_new(Node* n) const; }; From ed242447a5d4aab499f326188e5ff30d148667c5 Mon Sep 17 00:00:00 2001 From: Ravi Reddy Date: Tue, 6 Aug 2024 11:28:52 +0000 Subject: [PATCH 03/14] 8335428: Enhanced Building of Processes Backport-of: 978dfdf9aa95da4196055cc288c5993d4dc6ef85 --- .../classes/java/lang/ProcessImpl.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/java.base/windows/classes/java/lang/ProcessImpl.java b/src/java.base/windows/classes/java/lang/ProcessImpl.java index d6fb51c4494..240f64d1c84 100644 --- a/src/java.base/windows/classes/java/lang/ProcessImpl.java +++ b/src/java.base/windows/classes/java/lang/ProcessImpl.java @@ -217,13 +217,14 @@ final class ProcessImpl extends Process { private static final int VERIFICATION_LEGACY = 3; // See Command shell overview for documentation of special characters. // https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb490954(v=technet.10) - private static final char ESCAPE_VERIFICATION[][] = { + private static final String ESCAPE_VERIFICATION[] = { // We guarantee the only command file execution for implicit [cmd.exe] run. // http://technet.microsoft.com/en-us/library/bb490954.aspx - {' ', '\t', '\"', '<', '>', '&', '|', '^'}, - {' ', '\t', '\"', '<', '>'}, - {' ', '\t', '\"', '<', '>'}, - {' ', '\t'} + // All space characters require quoting are checked in needsEscaping(). + "\t\"<>&|^", + "\t\"<>", + "\t\"<>", + "\t" }; private static String createCommandLine(int verificationType, @@ -338,9 +339,13 @@ final class ProcessImpl extends Process { } if (!argIsQuoted) { - char testEscape[] = ESCAPE_VERIFICATION[verificationType]; - for (int i = 0; i < testEscape.length; ++i) { - if (arg.indexOf(testEscape[i]) >= 0) { + for (int i = 0; i < arg.length(); i++) { + char ch = arg.charAt(i); + if (Character.isLetterOrDigit(ch)) + continue; // skip over common characters + // All space chars require quotes and other mode specific characters + if (Character.isSpaceChar(ch) || + ESCAPE_VERIFICATION[verificationType].indexOf(ch) >= 0) { return true; } } From 3a336fafc97eaaf64a8afbd432a5d3af2ec770e5 Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Wed, 7 Aug 2024 04:38:46 +0000 Subject: [PATCH 04/14] 8336564: Enhance mask blit functionality redux Backport-of: 6e7f9a23a4daf461acbc4c5a5110e19b34ced22c --- src/java.desktop/share/native/libawt/java2d/SurfaceData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.desktop/share/native/libawt/java2d/SurfaceData.h b/src/java.desktop/share/native/libawt/java2d/SurfaceData.h index 8975f8d4a9d..f7124961392 100644 --- a/src/java.desktop/share/native/libawt/java2d/SurfaceData.h +++ b/src/java.desktop/share/native/libawt/java2d/SurfaceData.h @@ -60,7 +60,7 @@ typedef struct { #define UNSAFE_TO_SUB(a, b) \ (((b >= 0) && (a < 0) && (a < (INT_MIN + b))) || \ - ((b < 0) && (a >= 0) && (-b > (INT_MAX - a)))) \ + ((b < 0) && (a >= 0) && (a > (INT_MAX + b)))) \ /* * The SurfaceDataRasInfo structure is used to pass in and return various From d98335de7acef838e273e36fcb86e9967653a800 Mon Sep 17 00:00:00 2001 From: Shyam Kishor Date: Fri, 20 Sep 2024 08:50:24 +0000 Subject: [PATCH 05/14] 8319933: Disable tests for JDK-8280481 on Graal Reviewed-by: rsunderbabu Backport-of: bd750b6b783101a3b992a25e7bc64777bb08de18 --- .../jtreg/compiler/sharedstubs/SharedStubToInterpTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/compiler/sharedstubs/SharedStubToInterpTest.java b/test/hotspot/jtreg/compiler/sharedstubs/SharedStubToInterpTest.java index 5dd938442cc..2bf8afae609 100644 --- a/test/hotspot/jtreg/compiler/sharedstubs/SharedStubToInterpTest.java +++ b/test/hotspot/jtreg/compiler/sharedstubs/SharedStubToInterpTest.java @@ -36,6 +36,7 @@ * @requires vm.opt.TieredStopAtLevel == null & vm.opt.TieredCompilation == null * @requires vm.simpleArch == "x86" | vm.simpleArch == "x64" | vm.simpleArch == "aarch64" | vm.simpleArch == "riscv64" * @requires vm.debug + * @requires !vm.graal.enabled * @run driver compiler.sharedstubs.SharedStubToInterpTest -XX:-TieredCompilation * */ From af36472270810b3f35d2bd47d88cf1c6e4a69928 Mon Sep 17 00:00:00 2001 From: Ravi Reddy Date: Wed, 25 Sep 2024 09:15:20 +0000 Subject: [PATCH 06/14] 8339180: Enhanced Building of Processes: Follow-on Issue Backport-of: a71624a69ec5cc8600f2a3a53c23c75b43068830 --- src/java.base/windows/classes/java/lang/ProcessImpl.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/java.base/windows/classes/java/lang/ProcessImpl.java b/src/java.base/windows/classes/java/lang/ProcessImpl.java index 240f64d1c84..6ac8035e5cc 100644 --- a/src/java.base/windows/classes/java/lang/ProcessImpl.java +++ b/src/java.base/windows/classes/java/lang/ProcessImpl.java @@ -221,10 +221,10 @@ final class ProcessImpl extends Process { // We guarantee the only command file execution for implicit [cmd.exe] run. // http://technet.microsoft.com/en-us/library/bb490954.aspx // All space characters require quoting are checked in needsEscaping(). - "\t\"<>&|^", - "\t\"<>", - "\t\"<>", - "\t" + "\"<>&|^", + "\"<>", + "\"<>", + "" }; private static String createCommandLine(int verificationType, @@ -345,6 +345,7 @@ final class ProcessImpl extends Process { continue; // skip over common characters // All space chars require quotes and other mode specific characters if (Character.isSpaceChar(ch) || + Character.isWhitespace(ch) || ESCAPE_VERIFICATION[verificationType].indexOf(ch) >= 0) { return true; } From efefa5975a9e4b4482cf0d05a9d173f023a0d1fb Mon Sep 17 00:00:00 2001 From: pavel_kharskii Date: Wed, 9 Oct 2024 08:53:20 +0000 Subject: [PATCH 07/14] 8341709: Change milestone to ea for JDK23.0.2 Reviewed-by: coffeys, mvs --- make/conf/version-numbers.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index 08d756aa67b..aec48255d03 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -39,4 +39,4 @@ DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 DEFAULT_ACCEPTABLE_BOOT_VERSIONS="22 23" DEFAULT_JDK_SOURCE_TARGET_VERSION=23 -DEFAULT_PROMOTED_VERSION_PRE= +DEFAULT_PROMOTED_VERSION_PRE=ea From 577dbe62563688a4f547a17de2b1412ddb0dff37 Mon Sep 17 00:00:00 2001 From: Prasadrao Koppula Date: Mon, 14 Oct 2024 11:48:00 +0000 Subject: [PATCH 08/14] 8302111: Serialization considerations Backport-of: 3be051b0ea4f2f340ea11887befdff7bc899b476 --- .../com/sun/crypto/provider/DHPrivateKey.java | 291 ++++++++++-------- .../com/sun/crypto/provider/DHPublicKey.java | 243 +++++++++------ .../provider/TlsMasterSecretGenerator.java | 38 +-- .../classes/java/security/Permissions.java | 7 +- .../classes/java/security/SignedObject.java | 54 ++-- .../classes/java/security/Timestamp.java | 14 +- .../UnresolvedPermissionCollection.java | 38 ++- .../cert/CertificateRevokedException.java | 23 +- .../javax/crypto/spec/SecretKeySpec.java | 30 +- .../auth/callback/ChoiceCallback.java | 59 ++-- .../auth/callback/ConfirmationCallback.java | 139 +++++---- .../auth/callback/PasswordCallback.java | 6 +- .../classes/sun/security/provider/DRBG.java | 12 +- .../sun/security/util/ObjectIdentifier.java | 18 +- .../classes/sun/security/x509/AlgIdDSA.java | 123 ++++---- .../sun/security/jgss/krb5/Krb5Context.java | 19 +- .../jgss/krb5/Krb5InitCredential.java | 17 +- .../sun/security/krb5/internal/KRBError.java | 40 ++- .../classes/sun/security/pkcs11/P11Key.java | 13 + .../sun/security/pkcs11/P11SecureRandom.java | 16 +- .../sun/security/pkcs11/SunPKCS11.java | 13 + 21 files changed, 745 insertions(+), 468 deletions(-) diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java index 0d5363e866a..fcdc3fbea49 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,6 +56,7 @@ final class DHPrivateKey implements PrivateKey, private final BigInteger x; // the key bytes, without the algorithm information + // cannot be final as it's re-assigned for deserialization private byte[] key; // the encoded key @@ -70,6 +71,131 @@ final class DHPrivateKey implements PrivateKey, // the private-value length (optional) private final int l; + private static class DHComponents { + final BigInteger x; + final BigInteger p; + final BigInteger g; + final int l; + final byte[] key; + + DHComponents(BigInteger x, BigInteger p, BigInteger g, int l, + byte[] key) { + this.x = x; + this.p = p; + this.g = g; + this.l = l; + this.key = key; + } + } + + // parses the specified encoding into a DHComponents object + private static DHComponents decode(byte[] encodedKey) + throws IOException { + DerValue val = null; + + try { + val = new DerValue(encodedKey); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Key not a SEQUENCE"); + } + + // version + BigInteger parsedVersion = val.data.getBigInteger(); + if (!parsedVersion.equals(PKCS8_VERSION)) { + throw new IOException("version mismatch: (supported: " + + PKCS8_VERSION + ", parsed: " + parsedVersion); + } + + // privateKeyAlgorithm + DerValue algid = val.data.getDerValue(); + if (algid.tag != DerValue.tag_Sequence) { + throw new IOException("AlgId is not a SEQUENCE"); + } + DerInputStream derInStream = algid.toDerInputStream(); + ObjectIdentifier oid = derInStream.getOID(); + if (oid == null) { + throw new IOException("Null OID"); + } + if (derInStream.available() == 0) { + throw new IOException("Parameters missing"); + } + // parse the parameters + DerValue params = derInStream.getDerValue(); + if (params.tag == DerValue.tag_Null) { + throw new IOException("Null parameters"); + } + if (params.tag != DerValue.tag_Sequence) { + throw new IOException("Parameters not a SEQUENCE"); + } + params.data.reset(); + BigInteger p = params.data.getBigInteger(); + BigInteger g = params.data.getBigInteger(); + // Private-value length is OPTIONAL + int l = (params.data.available() != 0 ? + params.data.getInteger() : 0); + // should have no trailing data + if (params.data.available() != 0) { + throw new IOException("Extra parameter data"); + } + + // privateKey + byte[] key = val.data.getOctetString(); + DerInputStream in = new DerInputStream(key); + BigInteger x = in.getBigInteger(); + + // should have no trailing data + if (val.data.available() != 0) { + throw new IOException("Excess trailing data"); + } + return new DHComponents(x, p, g, l, key); + } catch (NumberFormatException e) { + throw new IOException("Error parsing key encoding", e); + } finally { + if (val != null) { + val.clear(); + } + } + } + + // Generates the ASN.1 encoding + private static byte[] encode(BigInteger p, BigInteger g, int l, + byte[] key) { + DerOutputStream tmp = new DerOutputStream(); + + // version + tmp.putInteger(PKCS8_VERSION); + + // privateKeyAlgorithm + DerOutputStream algid = new DerOutputStream(); + + // store OID + algid.putOID(DHPublicKey.DH_OID); + // encode parameters + DerOutputStream params = new DerOutputStream(); + params.putInteger(p); + params.putInteger(g); + if (l != 0) { + params.putInteger(l); + } + // wrap parameters into SEQUENCE + DerValue paramSequence = new DerValue(DerValue.tag_Sequence, + params.toByteArray()); + // store parameter SEQUENCE in algid + algid.putDerValue(paramSequence); + // wrap algid into SEQUENCE + tmp.write(DerValue.tag_Sequence, algid); + + // privateKey + tmp.putOctetString(key); + + // make it a SEQUENCE + DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp); + byte[] encoded = val.toByteArray(); + val.clear(); + + return encoded; + } + /** * Make a DH private key out of a private value x, a prime * modulus p, and a base generator g. @@ -79,7 +205,7 @@ final class DHPrivateKey implements PrivateKey, * @param g the base generator */ DHPrivateKey(BigInteger x, BigInteger p, BigInteger g) - throws InvalidKeyException { + throws InvalidKeyException { this(x, p, g, 0); } @@ -98,12 +224,16 @@ final class DHPrivateKey implements PrivateKey, this.p = p; this.g = g; this.l = l; + byte[] xbytes = x.toByteArray(); DerValue val = new DerValue(DerValue.tag_Integer, xbytes); - this.key = val.toByteArray(); - val.clear(); - Arrays.fill(xbytes, (byte) 0); - encode(); + try { + this.key = val.toByteArray(); + } finally { + val.clear(); + Arrays.fill(xbytes, (byte) 0); + } + this.encodedKey = encode(p, g, l, key); } /** @@ -115,75 +245,18 @@ final class DHPrivateKey implements PrivateKey, * a Diffie-Hellman private key */ DHPrivateKey(byte[] encodedKey) throws InvalidKeyException { - DerValue val = null; + this.encodedKey = encodedKey.clone(); + DHComponents dc; try { - val = new DerValue(encodedKey); - if (val.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException ("Key not a SEQUENCE"); - } - - // - // version - // - BigInteger parsedVersion = val.data.getBigInteger(); - if (!parsedVersion.equals(PKCS8_VERSION)) { - throw new IOException("version mismatch: (supported: " + - PKCS8_VERSION + ", parsed: " + - parsedVersion); - } - - // - // privateKeyAlgorithm - // - DerValue algid = val.data.getDerValue(); - if (algid.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("AlgId is not a SEQUENCE"); - } - DerInputStream derInStream = algid.toDerInputStream(); - ObjectIdentifier oid = derInStream.getOID(); - if (oid == null) { - throw new InvalidKeyException("Null OID"); - } - if (derInStream.available() == 0) { - throw new InvalidKeyException("Parameters missing"); - } - // parse the parameters - DerValue params = derInStream.getDerValue(); - if (params.tag == DerValue.tag_Null) { - throw new InvalidKeyException("Null parameters"); - } - if (params.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("Parameters not a SEQUENCE"); - } - params.data.reset(); - this.p = params.data.getBigInteger(); - this.g = params.data.getBigInteger(); - // Private-value length is OPTIONAL - if (params.data.available() != 0) { - this.l = params.data.getInteger(); - } else { - this.l = 0; - } - if (params.data.available() != 0) { - throw new InvalidKeyException("Extra parameter data"); - } - - // - // privateKey - // - this.key = val.data.getOctetString(); - - DerInputStream in = new DerInputStream(this.key); - this.x = in.getBigInteger(); - - this.encodedKey = encodedKey.clone(); - } catch (IOException | NumberFormatException e) { - throw new InvalidKeyException("Error parsing key encoding", e); - } finally { - if (val != null) { - val.clear(); - } + dc = decode(this.encodedKey); + } catch (IOException e) { + throw new InvalidKeyException("Invalid encoding", e); } + this.x = dc.x; + this.p = dc.p; + this.g = dc.g; + this.l = dc.l; + this.key = dc.key; } /** @@ -204,55 +277,9 @@ final class DHPrivateKey implements PrivateKey, * Get the encoding of the key. */ public synchronized byte[] getEncoded() { - encode(); return encodedKey.clone(); } - /** - * Generate the encodedKey field if it has not been calculated. - * Could generate null. - */ - private void encode() { - if (this.encodedKey == null) { - DerOutputStream tmp = new DerOutputStream(); - - // - // version - // - tmp.putInteger(PKCS8_VERSION); - - // - // privateKeyAlgorithm - // - DerOutputStream algid = new DerOutputStream(); - - // store OID - algid.putOID(DHPublicKey.DH_OID); - // encode parameters - DerOutputStream params = new DerOutputStream(); - params.putInteger(this.p); - params.putInteger(this.g); - if (this.l != 0) { - params.putInteger(this.l); - } - // wrap parameters into SEQUENCE - DerValue paramSequence = new DerValue(DerValue.tag_Sequence, - params.toByteArray()); - // store parameter SEQUENCE in algid - algid.putDerValue(paramSequence); - // wrap algid into SEQUENCE - tmp.write(DerValue.tag_Sequence, algid); - - // privateKey - tmp.putOctetString(this.key); - - // make it a SEQUENCE - DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp); - this.encodedKey = val.toByteArray(); - val.clear(); - } - } - /** * Returns the private value, x. * @@ -307,10 +334,7 @@ final class DHPrivateKey implements PrivateKey, */ @java.io.Serial private Object writeReplace() throws java.io.ObjectStreamException { - encode(); - return new KeyRep(KeyRep.Type.PRIVATE, - getAlgorithm(), - getFormat(), + return new KeyRep(KeyRep.Type.PRIVATE, getAlgorithm(), getFormat(), encodedKey); } @@ -330,11 +354,28 @@ final class DHPrivateKey implements PrivateKey, if ((key == null) || (key.length == 0)) { throw new InvalidObjectException("key not deserializable"); } - this.key = key.clone(); if ((encodedKey == null) || (encodedKey.length == 0)) { throw new InvalidObjectException( "encoded key not deserializable"); } - this.encodedKey = encodedKey.clone(); + // check if the "encodedKey" value matches the deserialized fields + DHComponents c; + byte[] encodedKeyIntern = encodedKey.clone(); + try { + c = decode(encodedKeyIntern); + } catch (IOException e) { + throw new InvalidObjectException("Invalid encoding", e); + } + if (!Arrays.equals(c.key, key) || !c.x.equals(x) || !c.p.equals(p) + || !c.g.equals(g) || c.l != l) { + throw new InvalidObjectException( + "encoded key not matching internal fields"); + } + // zero out external arrays + Arrays.fill(key, (byte)0x00); + Arrays.fill(encodedKey, (byte)0x00); + // use self-created internal copies + this.key = c.key; + this.encodedKey = encodedKeyIntern; } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java index 47727c432a6..c95b40482d5 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package com.sun.crypto.provider; import java.io.*; +import java.util.Arrays; import java.util.Objects; import java.math.BigInteger; import java.security.KeyRep; @@ -70,6 +71,116 @@ javax.crypto.interfaces.DHPublicKey, Serializable { static final ObjectIdentifier DH_OID = ObjectIdentifier.of(KnownOIDs.DiffieHellman); + private static class DHComponents { + final BigInteger y; + final BigInteger p; + final BigInteger g; + final int l; + final byte[] key; + + DHComponents(BigInteger y, BigInteger p, BigInteger g, int l, + byte[] key) { + this.y = y; + this.p = p; + this.g = g; + this.l = l; + this.key = key; + } + } + + // parses the specified encoding into a DHComponents object + private static DHComponents decode(byte[] encodedKey) + throws IOException { + DerValue val = null; + + try { + val = new DerValue(encodedKey); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid key format"); + } + + // algorithm identifier + DerValue algid = val.data.getDerValue(); + if (algid.tag != DerValue.tag_Sequence) { + throw new IOException("AlgId is not a SEQUENCE"); + } + DerInputStream derInStream = algid.toDerInputStream(); + ObjectIdentifier oid = derInStream.getOID(); + if (oid == null) { + throw new IOException("Null OID"); + } + if (derInStream.available() == 0) { + throw new IOException("Parameters missing"); + } + + // parse the parameters + DerValue params = derInStream.getDerValue(); + if (params.tag == DerValue.tag_Null) { + throw new IOException("Null parameters"); + } + if (params.tag != DerValue.tag_Sequence) { + throw new IOException("Parameters not a SEQUENCE"); + } + params.data.reset(); + + BigInteger p = params.data.getBigInteger(); + BigInteger g = params.data.getBigInteger(); + // Private-value length is OPTIONAL + int l = (params.data.available() != 0 ? params.data.getInteger() : + 0); + if (params.data.available() != 0) { + throw new IOException("Extra parameter data"); + } + + // publickey + byte[] key = val.data.getBitString(); + DerInputStream in = new DerInputStream(key); + BigInteger y = in.getBigInteger(); + + if (val.data.available() != 0) { + throw new IOException("Excess key data"); + } + return new DHComponents(y, p, g, l, key); + } catch (NumberFormatException e) { + throw new IOException("Error parsing key encoding", e); + } + } + + // generates the ASN.1 encoding + private static byte[] encode(BigInteger p, BigInteger g, int l, + byte[] key) { + DerOutputStream algid = new DerOutputStream(); + + // store oid in algid + algid.putOID(DH_OID); + + // encode parameters + DerOutputStream params = new DerOutputStream(); + params.putInteger(p); + params.putInteger(g); + if (l != 0) { + params.putInteger(l); + } + + // wrap parameters into SEQUENCE + DerValue paramSequence = new DerValue(DerValue.tag_Sequence, + params.toByteArray()); + // store parameter SEQUENCE in algid + algid.putDerValue(paramSequence); + + // wrap algid into SEQUENCE, and store it in key encoding + DerOutputStream tmpDerKey = new DerOutputStream(); + tmpDerKey.write(DerValue.tag_Sequence, algid); + + // store key data + tmpDerKey.putBitString(key); + + // wrap algid and key into SEQUENCE + DerOutputStream derKey = new DerOutputStream(); + derKey.write(DerValue.tag_Sequence, tmpDerKey); + return derKey.toByteArray(); + } + /** * Make a DH public key out of a public value y, a prime * modulus p, and a base generator g. @@ -102,7 +213,7 @@ javax.crypto.interfaces.DHPublicKey, Serializable { this.l = l; this.key = new DerValue(DerValue.tag_Integer, this.y.toByteArray()).toByteArray(); - this.encodedKey = getEncoded(); + this.encodedKey = encode(p, g, l, key); } /** @@ -114,68 +225,19 @@ javax.crypto.interfaces.DHPublicKey, Serializable { * a Diffie-Hellman public key */ DHPublicKey(byte[] encodedKey) throws InvalidKeyException { - InputStream inStream = new ByteArrayInputStream(encodedKey); + this.encodedKey = encodedKey.clone(); + + DHComponents dc; try { - DerValue derKeyVal = new DerValue(inStream); - if (derKeyVal.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException ("Invalid key format"); - } - - /* - * Parse the algorithm identifier - */ - DerValue algid = derKeyVal.data.getDerValue(); - if (algid.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("AlgId is not a SEQUENCE"); - } - DerInputStream derInStream = algid.toDerInputStream(); - ObjectIdentifier oid = derInStream.getOID(); - if (oid == null) { - throw new InvalidKeyException("Null OID"); - } - if (derInStream.available() == 0) { - throw new InvalidKeyException("Parameters missing"); - } - - /* - * Parse the parameters - */ - DerValue params = derInStream.getDerValue(); - if (params.tag == DerValue.tag_Null) { - throw new InvalidKeyException("Null parameters"); - } - if (params.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("Parameters not a SEQUENCE"); - } - params.data.reset(); - this.p = params.data.getBigInteger(); - this.g = params.data.getBigInteger(); - // Private-value length is OPTIONAL - if (params.data.available() != 0) { - this.l = params.data.getInteger(); - } else { - this.l = 0; - } - if (params.data.available() != 0) { - throw new InvalidKeyException("Extra parameter data"); - } - - /* - * Parse the key - */ - this.key = derKeyVal.data.getBitString(); - - DerInputStream in = new DerInputStream(this.key); - this.y = in.getBigInteger(); - - if (derKeyVal.data.available() != 0) { - throw new InvalidKeyException("Excess key data"); - } - - this.encodedKey = encodedKey.clone(); - } catch (IOException | NumberFormatException e) { - throw new InvalidKeyException("Error parsing key encoding", e); + dc = decode(this.encodedKey); + } catch (IOException e) { + throw new InvalidKeyException("Invalid encoding", e); } + this.y = dc.y; + this.p = dc.p; + this.g = dc.g; + this.l = dc.l; + this.key = dc.key; } /** @@ -196,37 +258,6 @@ javax.crypto.interfaces.DHPublicKey, Serializable { * Get the encoding of the key. */ public synchronized byte[] getEncoded() { - if (this.encodedKey == null) { - DerOutputStream algid = new DerOutputStream(); - - // store oid in algid - algid.putOID(DH_OID); - - // encode parameters - DerOutputStream params = new DerOutputStream(); - params.putInteger(this.p); - params.putInteger(this.g); - if (this.l != 0) { - params.putInteger(this.l); - } - // wrap parameters into SEQUENCE - DerValue paramSequence = new DerValue(DerValue.tag_Sequence, - params.toByteArray()); - // store parameter SEQUENCE in algid - algid.putDerValue(paramSequence); - - // wrap algid into SEQUENCE, and store it in key encoding - DerOutputStream tmpDerKey = new DerOutputStream(); - tmpDerKey.write(DerValue.tag_Sequence, algid); - - // store key data - tmpDerKey.putBitString(this.key); - - // wrap algid and key into SEQUENCE - DerOutputStream derKey = new DerOutputStream(); - derKey.write(DerValue.tag_Sequence, tmpDerKey); - this.encodedKey = derKey.toByteArray(); - } return this.encodedKey.clone(); } @@ -263,8 +294,9 @@ javax.crypto.interfaces.DHPublicKey, Serializable { + Debug.toHexString(this.p) + LINE_SEP + "g:" + LINE_SEP + Debug.toHexString(this.g)); - if (this.l != 0) + if (this.l != 0) { sb.append(LINE_SEP + "l:" + LINE_SEP + " " + this.l); + } return sb.toString(); } @@ -304,7 +336,7 @@ javax.crypto.interfaces.DHPublicKey, Serializable { return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(), - getEncoded()); + encodedKey); } /** @@ -323,11 +355,28 @@ javax.crypto.interfaces.DHPublicKey, Serializable { if ((key == null) || (key.length == 0)) { throw new InvalidObjectException("key not deserializable"); } - this.key = key.clone(); if ((encodedKey == null) || (encodedKey.length == 0)) { throw new InvalidObjectException( "encoded key not deserializable"); } - this.encodedKey = encodedKey.clone(); + // check if the "encodedKey" value matches the deserialized fields + DHComponents c; + byte[] encodedKeyIntern = encodedKey.clone(); + try { + c = decode(encodedKeyIntern); + } catch (IOException e) { + throw new InvalidObjectException("Invalid encoding", e); + } + if (!Arrays.equals(c.key, key) || !c.y.equals(y) || !c.p.equals(p) + || !c.g.equals(g) || c.l != l) { + throw new InvalidObjectException( + "encoded key not matching internal fields"); + } + // zero out external arrays + Arrays.fill(key, (byte)0x00); + Arrays.fill(encodedKey, (byte)0x00); + // use self-created internal copies + this.key = c.key; + this.encodedKey = encodedKeyIntern; } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java index 14ada1699c1..2762fb3751c 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -194,22 +194,24 @@ public final class TlsMasterSecretGenerator extends KeyGeneratorSpi { return key.clone(); } - /** - * Restores the state of this object from the stream. - * - * @param stream the {@code ObjectInputStream} from which data is read - * @throws IOException if an I/O error occurs - * @throws ClassNotFoundException if a serialized class cannot be loaded - */ - @java.io.Serial - private void readObject(ObjectInputStream stream) - throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - if ((key == null) || (key.length == 0)) { - throw new InvalidObjectException("TlsMasterSecretKey is null"); - } - key = key.clone(); - } - } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + if (key == null || key.length == 0) { + throw new InvalidObjectException("TlsMasterSecretKey is null"); + } + byte[] temp = key; + this.key = temp.clone(); + Arrays.fill(temp, (byte)0); + } + } } diff --git a/src/java.base/share/classes/java/security/Permissions.java b/src/java.base/share/classes/java/security/Permissions.java index 42c1adc9002..3bdeac6f929 100644 --- a/src/java.base/share/classes/java/security/Permissions.java +++ b/src/java.base/share/classes/java/security/Permissions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -407,6 +407,11 @@ implements Serializable @SuppressWarnings("unchecked") Hashtable, PermissionCollection> perms = (Hashtable, PermissionCollection>)gfields.get("perms", null); + + if (perms == null) { + throw new InvalidObjectException("perms can't be null"); + } + permsMap = new ConcurrentHashMap<>(perms.size()*2); permsMap.putAll(perms); diff --git a/src/java.base/share/classes/java/security/SignedObject.java b/src/java.base/share/classes/java/security/SignedObject.java index e2f9c764ec2..f65300fc808 100644 --- a/src/java.base/share/classes/java/security/SignedObject.java +++ b/src/java.base/share/classes/java/security/SignedObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -152,20 +152,20 @@ public final class SignedObject implements Serializable { */ public SignedObject(Serializable object, PrivateKey signingKey, Signature signingEngine) - throws IOException, InvalidKeyException, SignatureException { - // creating a stream pipe-line, from a to b - ByteArrayOutputStream b = new ByteArrayOutputStream(); - ObjectOutput a = new ObjectOutputStream(b); + throws IOException, InvalidKeyException, SignatureException { + // creating a stream pipe-line, from a to b + ByteArrayOutputStream b = new ByteArrayOutputStream(); + ObjectOutput a = new ObjectOutputStream(b); - // write and flush the object content to byte array - a.writeObject(object); - a.flush(); - a.close(); - this.content = b.toByteArray(); - b.close(); + // write and flush the object content to byte array + a.writeObject(object); + a.flush(); + a.close(); + this.content = b.toByteArray(); + b.close(); - // now sign the encapsulated object - this.sign(signingKey, signingEngine); + // now sign the encapsulated object + this.sign(signingKey, signingEngine); } /** @@ -245,12 +245,12 @@ public final class SignedObject implements Serializable { * @throws SignatureException if signing fails. */ private void sign(PrivateKey signingKey, Signature signingEngine) - throws InvalidKeyException, SignatureException { - // initialize the signing engine - signingEngine.initSign(signingKey); - signingEngine.update(this.content.clone()); - this.signature = signingEngine.sign().clone(); - this.thealgorithm = signingEngine.getAlgorithm(); + throws InvalidKeyException, SignatureException { + // initialize the signing engine + signingEngine.initSign(signingKey); + signingEngine.update(this.content.clone()); + this.signature = signingEngine.sign(); + this.thealgorithm = signingEngine.getAlgorithm(); } /** @@ -263,10 +263,16 @@ public final class SignedObject implements Serializable { */ @Serial private void readObject(ObjectInputStream s) - throws IOException, ClassNotFoundException { - ObjectInputStream.GetField fields = s.readFields(); - content = ((byte[])fields.get("content", null)).clone(); - signature = ((byte[])fields.get("signature", null)).clone(); - thealgorithm = (String)fields.get("thealgorithm", null); + throws IOException, ClassNotFoundException { + ObjectInputStream.GetField fields = s.readFields(); + byte[] c = (byte[]) fields.get("content", null); + byte[] sig = (byte[]) fields.get("signature", null); + String a = (String) fields.get("thealgorithm", null); + if (c == null || sig == null || a == null) { + throw new InvalidObjectException("One or more null fields"); + } + content = c.clone(); + signature = sig.clone(); + thealgorithm = a; } } diff --git a/src/java.base/share/classes/java/security/Timestamp.java b/src/java.base/share/classes/java/security/Timestamp.java index 10a93a9b180..96df37a8c1f 100644 --- a/src/java.base/share/classes/java/security/Timestamp.java +++ b/src/java.base/share/classes/java/security/Timestamp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ package java.security; import java.io.IOException; import java.io.ObjectInputStream; +import java.io.InvalidObjectException; import java.io.Serializable; import java.security.cert.CertPath; import java.security.cert.Certificate; @@ -78,7 +79,7 @@ public final class Timestamp implements Serializable { * {@code null}. */ public Timestamp(Date timestamp, CertPath signerCertPath) { - if (timestamp == null || signerCertPath == null) { + if (isNull(timestamp, signerCertPath)) { throw new NullPointerException(); } this.timestamp = new Date(timestamp.getTime()); // clone @@ -166,9 +167,16 @@ public final class Timestamp implements Serializable { */ @java.io.Serial private void readObject(ObjectInputStream ois) - throws IOException, ClassNotFoundException { + throws IOException, ClassNotFoundException { ois.defaultReadObject(); + if (isNull(timestamp, signerCertPath)) { + throw new InvalidObjectException("Invalid null field(s)"); + } myhash = -1; timestamp = new Date(timestamp.getTime()); } + + private static boolean isNull(Date d, CertPath c) { + return (d == null || c == null); + } } diff --git a/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java b/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java index a5f4de22d89..c0bdf5fc2a1 100644 --- a/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java +++ b/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; +import java.io.InvalidObjectException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -196,23 +197,32 @@ implements java.io.Serializable ObjectInputStream.GetField gfields = in.readFields(); // Get permissions - @SuppressWarnings("unchecked") // writeObject writes a Hashtable> // for the permissions key, so this cast is safe, unless the data is corrupt. - Hashtable> permissions = - (Hashtable>) - gfields.get("permissions", null); - perms = new ConcurrentHashMap<>(permissions.size()*2); + try { + @SuppressWarnings("unchecked") + Hashtable> permissions = + (Hashtable>) + gfields.get("permissions", null); - // Convert each entry (Vector) into a List - Set>> set = permissions.entrySet(); - for (Map.Entry> e : set) { - // Convert Vector into ArrayList - Vector vec = e.getValue(); - List list = new CopyOnWriteArrayList<>(vec); + if (permissions == null) { + throw new InvalidObjectException("Invalid null permissions"); + } - // Add to Hashtable being serialized - perms.put(e.getKey(), list); + perms = new ConcurrentHashMap<>(permissions.size()*2); + + // Convert each entry (Vector) into a List + Set>> set = permissions.entrySet(); + for (Map.Entry> e : set) { + // Convert Vector into ArrayList + Vector vec = e.getValue(); + List list = new CopyOnWriteArrayList<>(vec); + + // Add to Hashtable being serialized + perms.put(e.getKey(), list); + } + } catch (ClassCastException cce) { + throw new InvalidObjectException("Invalid type for permissions"); } } } diff --git a/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java b/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java index 70083033fc6..6649dcda6cc 100644 --- a/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java +++ b/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ package java.security.cert; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.IOException; +import java.io.InvalidObjectException; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -70,6 +71,13 @@ public class CertificateRevokedException extends CertificateException { private transient Map extensions; + private static boolean isNull(Date revocationDate, + CRLReason reason, X500Principal authority, + Map extensions) { + return (revocationDate == null || reason == null || authority == null + || extensions == null); + } + /** * Constructs a {@code CertificateRevokedException} with * the specified revocation date, reason code, authority name, and map @@ -92,8 +100,7 @@ public class CertificateRevokedException extends CertificateException { */ public CertificateRevokedException(Date revocationDate, CRLReason reason, X500Principal authority, Map extensions) { - if (revocationDate == null || reason == null || authority == null || - extensions == null) { + if (isNull(revocationDate, reason, authority, extensions)) { throw new NullPointerException(); } this.revocationDate = new Date(revocationDate.getTime()); @@ -234,9 +241,6 @@ public class CertificateRevokedException extends CertificateException { // (revocationDate, reason, authority) ois.defaultReadObject(); - // Defensively copy the revocation date - revocationDate = new Date(revocationDate.getTime()); - // Read in the size (number of mappings) of the extensions map // and create the extensions map int size = ois.readInt(); @@ -247,6 +251,13 @@ public class CertificateRevokedException extends CertificateException { } else { extensions = HashMap.newHashMap(Math.min(size, 20)); } + // make sure all fields are set before checking + if (isNull(revocationDate, reason, authority, extensions)) { + throw new InvalidObjectException("Invalid null field(s)"); + } + + // Defensively copy the revocation date + revocationDate = new Date(revocationDate.getTime()); // Read in the extensions and put the mappings in the extensions map for (int i = 0; i < size; i++) { diff --git a/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java b/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java index e76a51e5d68..2ad9a7748f2 100644 --- a/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -100,11 +100,9 @@ public class SecretKeySpec implements KeySpec, SecretKey { * is null or key is null or empty. */ public SecretKeySpec(byte[] key, String algorithm) { - if (key == null || algorithm == null) { - throw new IllegalArgumentException("Missing argument"); - } - if (key.length == 0) { - throw new IllegalArgumentException("Empty key"); + String errMsg = doSanityCheck(key, algorithm); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); } this.key = key.clone(); this.algorithm = algorithm; @@ -266,14 +264,22 @@ public class SecretKeySpec implements KeySpec, SecretKey { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); + String errMsg = doSanityCheck(key, algorithm); + if (errMsg != null) { + throw new InvalidObjectException(errMsg); + } + byte[] temp = key; + this.key = temp.clone(); + Arrays.fill(temp, (byte) 0); + } + private static String doSanityCheck(byte[] key, String algorithm) { + String errMsg = null; if (key == null || algorithm == null) { - throw new InvalidObjectException("Missing argument"); - } - - this.key = key.clone(); - if (key.length == 0) { - throw new InvalidObjectException("Invalid key length"); + errMsg = "Missing argument"; + } else if (key.length == 0) { + errMsg = "Empty key"; } + return errMsg; } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java b/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java index c005b4ea02b..1c35491e4e2 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -102,20 +102,18 @@ public class ChoiceCallback implements Callback, java.io.Serializable { public ChoiceCallback(String prompt, String[] choices, int defaultChoice, boolean multipleSelectionsAllowed) { - if (prompt == null || prompt.isEmpty() || - choices == null || choices.length == 0 || - defaultChoice < 0 || defaultChoice >= choices.length) - throw new IllegalArgumentException(); - + choices = (choices == null || choices.length == 0 ? choices : + choices.clone()); + String errMsg = doSanityCheck(prompt, choices, defaultChoice, + multipleSelectionsAllowed); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); + } this.prompt = prompt; this.defaultChoice = defaultChoice; this.multipleSelectionsAllowed = multipleSelectionsAllowed; - this.choices = choices.clone(); - for (int i = 0; i < choices.length; i++) { - if (choices[i] == null || choices[i].isEmpty()) - throw new IllegalArgumentException(); - } + this.choices = choices; } /** @@ -183,9 +181,11 @@ public class ChoiceCallback implements Callback, java.io.Serializable { * @see #getSelectedIndexes */ public void setSelectedIndexes(int[] selections) { - if (!multipleSelectionsAllowed) + if (!multipleSelectionsAllowed) { throw new UnsupportedOperationException(); - this.selections = selections == null ? null : selections.clone(); + } + this.selections = ((selections == null || selections.length == 0) ? + selections : selections.clone()); } /** @@ -211,26 +211,35 @@ public class ChoiceCallback implements Callback, java.io.Serializable { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); + choices = (choices == null || choices.length == 0 ? + choices : choices.clone()); + String errMsg = doSanityCheck(prompt, choices, defaultChoice, + multipleSelectionsAllowed); + if (errMsg != null) { + throw new InvalidObjectException(errMsg); + } + selections = (selections == null || selections.length == 0 ? + selections : selections.clone()); + if (selections != null && selections.length > 1 && + !multipleSelectionsAllowed) { + throw new InvalidObjectException("Multiple selections not allowed"); + } + } + + private static String doSanityCheck(String prompt, String[] choices, + int defaultChoice, boolean allowMultiple) { if ((prompt == null) || prompt.isEmpty() || (choices == null) || (choices.length == 0) || (defaultChoice < 0) || (defaultChoice >= choices.length)) { - throw new InvalidObjectException( - "Missing/invalid prompt/choices"); + return "Missing/invalid prompt/choices"; } - choices = choices.clone(); for (int i = 0; i < choices.length; i++) { - if ((choices[i] == null) || choices[i].isEmpty()) - throw new InvalidObjectException("Null/empty choices"); - } - - if (selections != null) { - selections = selections.clone(); - if (!multipleSelectionsAllowed && (selections.length != 1)) { - throw new InvalidObjectException( - "Multiple selections not allowed"); + if ((choices[i] == null) || choices[i].isEmpty()) { + return "Null/empty choices value"; } } + return null; } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java b/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java index 437ce7041a7..a00fc7013ec 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package javax.security.auth.callback; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; /** @@ -189,25 +190,10 @@ public class ConfirmationCallback implements Callback, java.io.Serializable { */ public ConfirmationCallback(int messageType, int optionType, int defaultOption) { - - if (messageType < INFORMATION || messageType > ERROR || - optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) - throw new IllegalArgumentException(); - - switch (optionType) { - case YES_NO_OPTION: - if (defaultOption != YES && defaultOption != NO) - throw new IllegalArgumentException(); - break; - case YES_NO_CANCEL_OPTION: - if (defaultOption != YES && defaultOption != NO && - defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; - case OK_CANCEL_OPTION: - if (defaultOption != OK && defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; + String errMsg = doSanityCheck(messageType, optionType, false, null, + defaultOption, null, false); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); } this.prompt = null; @@ -250,21 +236,20 @@ public class ConfirmationCallback implements Callback, java.io.Serializable { public ConfirmationCallback(int messageType, String[] options, int defaultOption) { - if (messageType < INFORMATION || messageType > ERROR || - options == null || options.length == 0 || - defaultOption < 0 || defaultOption >= options.length) - throw new IllegalArgumentException(); + if (options != null) { + options = options.clone(); + } + String errMsg = doSanityCheck(messageType, UNSPECIFIED_OPTION, true, + options, defaultOption, null, false); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); + } this.prompt = null; this.messageType = messageType; this.optionType = UNSPECIFIED_OPTION; this.defaultOption = defaultOption; - - this.options = options.clone(); - for (int i = 0; i < options.length; i++) { - if (options[i] == null || options[i].isEmpty()) - throw new IllegalArgumentException(); - } + this.options = options; } /** @@ -304,27 +289,11 @@ public class ConfirmationCallback implements Callback, java.io.Serializable { public ConfirmationCallback(String prompt, int messageType, int optionType, int defaultOption) { - if (prompt == null || prompt.isEmpty() || - messageType < INFORMATION || messageType > ERROR || - optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) - throw new IllegalArgumentException(); - - switch (optionType) { - case YES_NO_OPTION: - if (defaultOption != YES && defaultOption != NO) - throw new IllegalArgumentException(); - break; - case YES_NO_CANCEL_OPTION: - if (defaultOption != YES && defaultOption != NO && - defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; - case OK_CANCEL_OPTION: - if (defaultOption != OK && defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; + String errMsg = doSanityCheck(messageType, optionType, false, null, + defaultOption, prompt, true); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); } - this.prompt = prompt; this.messageType = messageType; this.optionType = optionType; @@ -369,22 +338,20 @@ public class ConfirmationCallback implements Callback, java.io.Serializable { public ConfirmationCallback(String prompt, int messageType, String[] options, int defaultOption) { - if (prompt == null || prompt.isEmpty() || - messageType < INFORMATION || messageType > ERROR || - options == null || options.length == 0 || - defaultOption < 0 || defaultOption >= options.length) - throw new IllegalArgumentException(); + if (options != null) { + options = options.clone(); + } + String errMsg = doSanityCheck(messageType, UNSPECIFIED_OPTION, true, + options, defaultOption, prompt, true); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); + } this.prompt = prompt; this.messageType = messageType; this.optionType = UNSPECIFIED_OPTION; this.defaultOption = defaultOption; - - this.options = options.clone(); - for (int i = 0; i < options.length; i++) { - if (options[i] == null || options[i].isEmpty()) - throw new IllegalArgumentException(); - } + this.options = options; } /** @@ -491,6 +458,49 @@ public class ConfirmationCallback implements Callback, java.io.Serializable { return selection; } + private static String doSanityCheck(int msgType, int optionType, + boolean isUnspecifiedOption, String[] options, int defOption, + String prompt, boolean checkPrompt) { + // validate msgType + if (msgType < INFORMATION || msgType > ERROR) { + return "Invalid msgType"; + } + // validate prompt if checkPrompt == true + if (checkPrompt && (prompt == null || prompt.isEmpty())) { + return "Invalid prompt"; + } + // validate optionType + if (isUnspecifiedOption) { + if (optionType != UNSPECIFIED_OPTION) { + return "Invalid optionType"; + } + // check options + if (options == null || options.length == 0 || + defOption < 0 || defOption >= options.length) { + return "Invalid options and/or default option"; + } + for (String ov : options) { + if (ov == null || ov.isEmpty()) { + return "Invalid option value"; + } + } + } else { + if (optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) { + return "Invalid optionType"; + } + // validate defOption based on optionType + if ((optionType == YES_NO_OPTION && (defOption != YES && + defOption != NO)) || + (optionType == YES_NO_CANCEL_OPTION && (defOption != YES && + defOption != NO && defOption != CANCEL)) || + (optionType == OK_CANCEL_OPTION && (defOption != OK && + defOption != CANCEL))) { + return "Invalid default option"; + } + } + return null; + } + /** * Restores the state of this object from the stream. * @@ -502,8 +512,15 @@ public class ConfirmationCallback implements Callback, java.io.Serializable { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); + if (options != null) { options = options.clone(); } + String errMsg = doSanityCheck(messageType, optionType, + (optionType == UNSPECIFIED_OPTION), options, defaultOption, + prompt, false); + if (errMsg != null) { + throw new InvalidObjectException(errMsg); + } } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java b/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java index bbe7ab882a6..2bee38ceaaa 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -178,7 +178,9 @@ public class PasswordCallback implements Callback, java.io.Serializable { } if (inputPassword != null) { - inputPassword = inputPassword.clone(); + char[] temp = inputPassword; + inputPassword = temp.clone(); + Arrays.fill(temp, '0'); cleanable = CleanerFactory.cleaner().register( this, cleanerFor(inputPassword)); } diff --git a/src/java.base/share/classes/sun/security/provider/DRBG.java b/src/java.base/share/classes/sun/security/provider/DRBG.java index 923c8c3aa54..01958285e43 100644 --- a/src/java.base/share/classes/sun/security/provider/DRBG.java +++ b/src/java.base/share/classes/sun/security/provider/DRBG.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package sun.security.provider; import java.io.IOException; +import java.io.InvalidObjectException; import java.security.AccessController; import java.security.DrbgParameters; import java.security.PrivilegedAction; @@ -272,11 +273,18 @@ public final class DRBG extends SecureRandomSpi { } } + /** + * Restores the state of this object from the stream. + * + * @param s the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ @java.io.Serial private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); - if (mdp.mech == null) { + if (mdp == null || mdp.mech == null) { throw new IllegalArgumentException("Input data is corrupted"); } createImpl(); diff --git a/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java b/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java index 6181bf223e9..306d34d7149 100644 --- a/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java +++ b/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -127,13 +127,24 @@ public final class ObjectIdentifier implements Serializable { // Is the component's field calculated? private transient boolean componentsCalculated = false; + /** + * Restores the state of this object from the stream. + * + * @param is the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ @java.io.Serial private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { is.defaultReadObject(); if (encoding == null) { // from an old version - int[] comp = (int[])components; + if (components == null) { + throw new InvalidObjectException("OID components is null"); + } + + int[] comp = ((int[]) components).clone(); if (componentLen > comp.length) { componentLen = comp.length; } @@ -142,7 +153,9 @@ public final class ObjectIdentifier implements Serializable { // will be performed again in init(). checkOidSize(componentLen); init(comp, componentLen); + components = comp; } else { + encoding = encoding.clone(); // defensive copying checkOidSize(encoding.length); check(encoding); } @@ -261,6 +274,7 @@ public final class ObjectIdentifier implements Serializable { encoding = in.getDerValue().getOID().encoding; } + // set 'encoding' field based on the specified 'components' and 'length' private void init(int[] components, int length) throws IOException { int pos = 0; byte[] tmp = new byte[length * 5 + 1]; // +1 for empty input diff --git a/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java b/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java index 3a3d384a0e8..764d77e6da8 100644 --- a/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java +++ b/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,11 +25,13 @@ package sun.security.x509; +import java.io.ObjectInputStream; import java.io.IOException; +import java.io.InvalidObjectException; import java.math.BigInteger; import java.security.*; import java.security.interfaces.DSAParams; - +import java.util.Arrays; import sun.security.util.*; @@ -72,33 +74,42 @@ import sun.security.util.*; * * @author David Brownell */ -public final -class AlgIdDSA extends AlgorithmId implements DSAParams -{ +public final class AlgIdDSA extends AlgorithmId implements DSAParams { @java.io.Serial private static final long serialVersionUID = 3437177836797504046L; + private static class DSAComponents { + private final BigInteger p; + private final BigInteger q; + private final BigInteger g; + DSAComponents(BigInteger p, BigInteger q, BigInteger g) { + this.p = p; + this.q = q; + this.g = g; + } + } + /* * The three unsigned integer parameters. */ - private BigInteger p , q, g; + private BigInteger p, q, g; /** Returns the DSS/DSA parameter "P" */ - public BigInteger getP () { return p; } + public BigInteger getP() { return p; } /** Returns the DSS/DSA parameter "Q" */ - public BigInteger getQ () { return q; } + public BigInteger getQ() { return q; } /** Returns the DSS/DSA parameter "G" */ - public BigInteger getG () { return g; } + public BigInteger getG() { return g; } /** * Default constructor. The OID and parameters must be * deserialized before this algorithm ID is used. */ @Deprecated - public AlgIdDSA () {} + public AlgIdDSA() {} /** * Constructs a DSS/DSA Algorithm ID from numeric parameters. @@ -109,7 +120,7 @@ class AlgIdDSA extends AlgorithmId implements DSAParams * @param q the DSS/DSA parameter "Q" * @param g the DSS/DSA parameter "G" */ - public AlgIdDSA (BigInteger p, BigInteger q, BigInteger g) { + public AlgIdDSA(BigInteger p, BigInteger q, BigInteger g) { super (DSA_oid); if (p != null || q != null || g != null) { @@ -120,8 +131,10 @@ class AlgIdDSA extends AlgorithmId implements DSAParams this.p = p; this.q = q; this.g = g; - initializeParams (); - + // For algorithm IDs which haven't been created from a DER + // encoded value, need to create DER encoding and store it + // into "encodedParams" + encodedParams = encode(p, q, g); } catch (IOException e) { /* this should not happen */ throw new ProviderException ("Construct DSS/DSA Algorithm ID"); @@ -133,50 +146,10 @@ class AlgIdDSA extends AlgorithmId implements DSAParams * Returns "DSA", indicating the Digital Signature Algorithm (DSA) as * defined by the Digital Signature Standard (DSS), FIPS 186. */ - public String getName () - { return "DSA"; } - - - /* - * For algorithm IDs which haven't been created from a DER encoded - * value, "params" must be created. - */ - private void initializeParams () throws IOException { - DerOutputStream out = new DerOutputStream(); - out.putInteger(p); - out.putInteger(q); - out.putInteger(g); - DerOutputStream result = new DerOutputStream(); - result.write(DerValue.tag_Sequence, out); - encodedParams = result.toByteArray(); + public String getName() { + return "DSA"; } - /** - * Parses algorithm parameters P, Q, and G. They're found - * in the "params" member, which never needs to be changed. - */ - protected void decodeParams () throws IOException { - if (encodedParams == null) { - throw new IOException("DSA alg params are null"); - } - - DerValue params = new DerValue(encodedParams); - if (params.tag != DerValue.tag_Sequence) { - throw new IOException("DSA alg parsing error"); - } - - params.data.reset (); - - this.p = params.data.getBigInteger(); - this.q = params.data.getBigInteger(); - this.g = params.data.getBigInteger(); - - if (params.data.available () != 0) - throw new IOException ("AlgIdDSA params, extra="+ - params.data.available ()); - } - - /* * Returns a formatted string describing the parameters. */ @@ -197,4 +170,44 @@ class AlgIdDSA extends AlgorithmId implements DSAParams "\n"; } } + + /** + * Restores the state of this object from the stream. Override to check + * on the 'p', 'q', 'g', and 'encodedParams'. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) throws IOException { + try { + stream.defaultReadObject(); + // if any of the 'p', 'q', 'g', 'encodedParams' is non-null, + // then they must be all non-null w/ matching encoding + if ((p != null || q != null || g != null || encodedParams != null) + && !Arrays.equals(encodedParams, encode(p, q, g))) { + throw new InvalidObjectException("Invalid DSA alg params"); + } + } catch (ClassNotFoundException e) { + throw new IOException(e); + } + } + + /* + * Create the DER encoding w/ the specified 'p', 'q', 'g' + */ + private static byte[] encode(BigInteger p, BigInteger q, + BigInteger g) throws IOException { + if (p == null || q == null || g == null) { + throw new InvalidObjectException("invalid null value"); + } + DerOutputStream out = new DerOutputStream(); + out.putInteger(p); + out.putInteger(q); + out.putInteger(g); + DerOutputStream result = new DerOutputStream(); + result.write(DerValue.tag_Sequence, out); + return result.toByteArray(); + } } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java index 7fe670fd680..92b694efb86 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java @@ -33,8 +33,10 @@ import sun.security.jgss.spi.*; import sun.security.jgss.TokenTracker; import sun.security.krb5.*; import java.io.InputStream; -import java.io.OutputStream; +import java.io.InvalidObjectException; import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.OutputStream; import java.security.*; import javax.security.auth.Subject; import javax.security.auth.kerberos.ServicePermission; @@ -1398,6 +1400,20 @@ class Krb5Context implements GSSContextSpi { return "Kerberos session key: etype=" + key.getEType() + ", " + Krb5Util.keyInfo(key.getBytes()); } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException + ("KerberosSessionKey not directly deserializable"); + } } /** @@ -1467,5 +1483,4 @@ class Krb5Context implements GSSContextSpi { public void setAuthzData(AuthorizationData authzData) { this.authzData = authzData; } - } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java index 1b109cc881f..4cc306282e6 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,9 @@ import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.kerberos.KerberosPrincipal; import java.io.Serial; import java.net.InetAddress; +import java.io.InvalidObjectException; import java.io.IOException; +import java.io.ObjectInputStream; import java.util.Date; import java.security.AccessController; import java.security.PrivilegedExceptionAction; @@ -400,4 +402,17 @@ public class Krb5InitCredential throw ge; } } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException("Krb5InitCredential not deserializable"); + } } diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java index b93ced00c65..2be746fd8dc 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java @@ -83,28 +83,34 @@ import static sun.security.krb5.internal.Krb5.DEBUG; * * http://www.ietf.org/rfc/rfc4120.txt. */ -// The instance fields not statically typed as Serializable are ASN.1 -// encoded and written by the writeObject method. -@SuppressWarnings("serial") + public class KRBError implements java.io.Serializable { static final long serialVersionUID = 3643809337475284503L; - private int pvno; - private int msgType; - private KerberosTime cTime; //optional - private Integer cuSec; //optional - private KerberosTime sTime; - private Integer suSec; - private int errorCode; - private Realm crealm; //optional - private PrincipalName cname; //optional - private PrincipalName sname; - private String eText; //optional - private byte[] eData; //optional - private Checksum eCksum; //optional + private transient int pvno; + private transient int msgType; + private transient KerberosTime cTime; //optional + private transient Integer cuSec; //optional + private transient KerberosTime sTime; + private transient Integer suSec; + private transient int errorCode; + private transient Realm crealm; //optional + private transient PrincipalName cname; //optional + private transient PrincipalName sname; + private transient String eText; //optional + private transient byte[] eData; //optional + private transient Checksum eCksum; //optional - private PAData[] pa; // PA-DATA in eData + private transient PAData[] pa; // PA-DATA in eData + /** + * Restores the state of this object from the stream. + * + * @param is the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { try { diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java index 837a3a3dba1..f1dd0a507b1 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java @@ -240,6 +240,19 @@ abstract class P11Key implements Key, Length { return new KeyRep(type, getAlgorithm(), format, getEncodedInternal()); } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException("P11Key not directly deserializable"); + } + public String toString() { token.ensureValid(); String s1 = token.provider.getName() + " " + algorithm + " " + type diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java index 70effc141bc..7ef8510ddee 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -187,9 +187,23 @@ final class P11SecureRandom extends SecureRandomSpi { } } + /** + * Restores the state of this object from the stream. + * + * @param in the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); + if (token == null) { + throw new InvalidObjectException("token is null"); + } + if (mixBuffer != null) { + mixBuffer = mixBuffer.clone(); + } // assign default values to non-null transient fields iBuffer = new byte[IBUFFER_SIZE]; ibuffered = 0; diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java index 07aaa1037ea..674cb423039 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java @@ -1924,6 +1924,19 @@ public final class SunPKCS11 extends AuthProvider { return new SunPKCS11Rep(this); } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException("SunPKCS11 not directly deserializable"); + } + /** * Serialized representation of the SunPKCS11 provider. */ From 9c61a138bbd9b2e3faf47d1dd9d514bfba005fbb Mon Sep 17 00:00:00 2001 From: Christian Hagedorn Date: Wed, 16 Oct 2024 13:12:16 +0000 Subject: [PATCH 09/14] 8330045: Enhance array handling Co-authored-by: Christian Hagedorn Co-authored-by: Emanuel Peter Co-authored-by: Francisco Ferrari Bihurriet Co-authored-by: Martin Balao Reviewed-by: thartmann, adinn, roland Backport-of: 727c2612766dd1737bf59fb0d9ba1e58a7b8c32b --- src/hotspot/share/opto/subnode.cpp | 90 ++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp index 7eb7922c5fb..9a9c95dcd09 100644 --- a/src/hotspot/share/opto/subnode.cpp +++ b/src/hotspot/share/opto/subnode.cpp @@ -1623,44 +1623,86 @@ Node *BoolNode::Ideal(PhaseGVN *phase, bool can_reshape) { return new BoolNode( ncmp, _test.negate() ); } - // Change ((x & m) u<= m) or ((m & x) u<= m) to always true - // Same with ((x & m) u< m+1) and ((m & x) u< m+1) + // We use the following Lemmas/insights for the following two transformations (1) and (2): + // x & y <=u y, for any x and y (Lemma 1, masking always results in a smaller unsigned number) + // y Opcode() == Op_AddI && cmp2->in(2)->find_int_con(0) == 1) { - bound = cmp2->in(1); + // (1b) "(x & m) in(1); + const TypeInt* rhs_m_type = phase->type(rhs_m)->isa_int(); + if (rhs_m_type->_lo > -1 || rhs_m_type->_hi < -1) { + // Exclude any case where m == -1 is possible. + m = rhs_m; + } } - if (cmp1->in(2) == bound || cmp1->in(1) == bound) { + if (cmp1->in(2) == m || cmp1->in(1) == m) { return ConINode::make(1); } } - // Change ((x & (m - 1)) u< m) into (m > 0) - // This is the off-by-one variant of the above + // (2) Change ((x & (m - 1)) u 0) + // This is the off-by-one variant of the above. + // + // We now prove that this replacement is correct. This is the same as proving + // "m >u 0" if and only if "x & (m - 1) u 0 <=> x & (m - 1) m >u 0": + // We prove this by contradiction: + // Assume m <=u 0 which is equivalent to m == 0: + // and thus + // x & (m - 1) u 0 => x & (m - 1) u 0, no underflow of "m - 1" + // + // + // Note that the signed version of "m > 0": + // m > 0 <=> x & (m - 1) 0 + // is false which is a contradiction. if (cop == Op_CmpU && _test._test == BoolTest::lt && cmp1_op == Op_AndI) { - Node* l = cmp1->in(1); - Node* r = cmp1->in(2); - for (int repeat = 0; repeat < 2; repeat++) { - bool match = r->Opcode() == Op_AddI && r->in(2)->find_int_con(0) == -1 && - r->in(1) == cmp2; - if (match) { - // arraylength known to be non-negative, so a (arraylength != 0) is sufficient, - // but to be compatible with the array range check pattern, use (arraylength u> 0) - Node* ncmp = cmp2->Opcode() == Op_LoadRange - ? phase->transform(new CmpUNode(cmp2, phase->intcon(0))) - : phase->transform(new CmpINode(cmp2, phase->intcon(0))); - return new BoolNode(ncmp, BoolTest::gt); - } else { - // commute and try again - l = cmp1->in(2); - r = cmp1->in(1); + Node* m = cmp2; // RHS: m + for (int add_idx = 1; add_idx <= 2; add_idx++) { // LHS: "(m + (-1)) & x" or "x & (m + (-1))"? + Node* maybe_m_minus_1 = cmp1->in(add_idx); + if (maybe_m_minus_1->Opcode() == Op_AddI && + maybe_m_minus_1->in(2)->find_int_con(0) == -1 && + maybe_m_minus_1->in(1) == m) { + Node* m_cmpu_0 = phase->transform(new CmpUNode(m, phase->intcon(0))); + return new BoolNode(m_cmpu_0, BoolTest::gt); } } } From f0a4bb22d35773294157ea1519dc3c7abe963d55 Mon Sep 17 00:00:00 2001 From: pavel_kharskii Date: Fri, 25 Oct 2024 11:25:49 +0000 Subject: [PATCH 10/14] 8342114: Change milestone to fcs for all releases Reviewed-by: robm, mvs --- make/conf/version-numbers.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index aec48255d03..08d756aa67b 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -39,4 +39,4 @@ DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 DEFAULT_ACCEPTABLE_BOOT_VERSIONS="22 23" DEFAULT_JDK_SOURCE_TARGET_VERSION=23 -DEFAULT_PROMOTED_VERSION_PRE=ea +DEFAULT_PROMOTED_VERSION_PRE= From 84ec30769efe04d0a8403b04935a0a3e664a00d6 Mon Sep 17 00:00:00 2001 From: Jorn Vernee Date: Tue, 19 Nov 2024 13:54:09 +0000 Subject: [PATCH 11/14] 8337753: Target class of upcall stub may be unloaded Backport-of: 52e373c7e81e9ff178d5b0ddb5d3938423819162 --- .../cpu/aarch64/stubGenerator_aarch64.cpp | 23 +++++ .../cpu/aarch64/upcallLinker_aarch64.cpp | 16 ++- src/hotspot/cpu/arm/upcallLinker_arm.cpp | 2 +- src/hotspot/cpu/ppc/stubGenerator_ppc.cpp | 25 +++++ src/hotspot/cpu/ppc/upcallLinker_ppc.cpp | 18 ++-- src/hotspot/cpu/riscv/stubGenerator_riscv.cpp | 24 +++++ src/hotspot/cpu/riscv/upcallLinker_riscv.cpp | 16 ++- src/hotspot/cpu/s390/stubGenerator_s390.cpp | 24 +++++ src/hotspot/cpu/s390/upcallLinker_s390.cpp | 17 ++-- src/hotspot/cpu/x86/stubGenerator_x86_64.cpp | 24 +++++ src/hotspot/cpu/x86/stubGenerator_x86_64.hpp | 1 + src/hotspot/cpu/x86/upcallLinker_x86_32.cpp | 2 +- src/hotspot/cpu/x86/upcallLinker_x86_64.cpp | 19 ++-- src/hotspot/cpu/zero/upcallLinker_zero.cpp | 2 +- src/hotspot/share/prims/upcallLinker.cpp | 28 ++---- src/hotspot/share/prims/upcallLinker.hpp | 4 +- src/hotspot/share/runtime/stubRoutines.cpp | 1 + src/hotspot/share/runtime/stubRoutines.hpp | 6 ++ test/jdk/TEST.groups | 3 +- test/jdk/java/foreign/TestUpcallStress.java | 97 +++++++++++++++++++ 20 files changed, 281 insertions(+), 71 deletions(-) create mode 100644 test/jdk/java/foreign/TestUpcallStress.java diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 3f1a4423b5e..d5b14aca19a 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -7412,6 +7412,28 @@ class StubGenerator: public StubCodeGenerator { return start; } + // load Method* target of MethodHandle + // j_rarg0 = jobject receiver + // rmethod = result + address generate_upcall_stub_load_target() { + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(j_rarg0, rscratch1, rscratch2); + // Load target method from receiver + __ load_heap_oop(rmethod, Address(j_rarg0, java_lang_invoke_MethodHandle::form_offset()), rscratch1, rscratch2); + __ load_heap_oop(rmethod, Address(rmethod, java_lang_invoke_LambdaForm::vmentry_offset()), rscratch1, rscratch2); + __ load_heap_oop(rmethod, Address(rmethod, java_lang_invoke_MemberName::method_offset()), rscratch1, rscratch2); + __ access_load_at(T_ADDRESS, IN_HEAP, rmethod, + Address(rmethod, java_lang_invoke_ResolvedMethodName::vmtarget_offset()), + noreg, noreg); + __ str(rmethod, Address(rthread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + + __ ret(lr); + + return start; + } + // Continuation point for throwing of implicit exceptions that are // not handled in the current activation. Fabricates an exception // oop and initiates normal exception dispatching in this @@ -8477,6 +8499,7 @@ class StubGenerator: public StubCodeGenerator { #endif StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); StubRoutines::aarch64::set_completed(); // Inidicate that arraycopy and zero_blocks stubs are generated } diff --git a/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp b/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp index 28ec07815be..517fccb2d1a 100644 --- a/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" +#include "classfile/javaClasses.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" @@ -117,7 +118,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -222,7 +223,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ lea(c_rarg0, Address(sp, frame_data_offset)); - __ movptr(c_rarg1, (intptr_t)receiver); __ movptr(rscratch1, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry)); __ blr(rscratch1); __ mov(rthread, r0); @@ -238,12 +238,10 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, as_VMStorage(shuffle_reg), abi._shadow_space_bytes, 0); __ block_comment("} argument shuffle"); - __ block_comment("{ receiver "); - __ get_vm_result(j_rarg0, rthread); - __ block_comment("} receiver "); - - __ mov_metadata(rmethod, entry); - __ str(rmethod, Address(rthread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + __ block_comment("{ load target "); + __ movptr(j_rarg0, (intptr_t)receiver); + __ far_call(RuntimeAddress(StubRoutines::upcall_stub_load_target()), rscratch1); // puts target Method* in rmethod + __ block_comment("} load target "); __ push_cont_fastpath(rthread); @@ -318,7 +316,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char* name = _masm->code_string(ss.as_string()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/arm/upcallLinker_arm.cpp b/src/hotspot/cpu/arm/upcallLinker_arm.cpp index c7645f4a033..696b2001e6b 100644 --- a/src/hotspot/cpu/arm/upcallLinker_arm.cpp +++ b/src/hotspot/cpu/arm/upcallLinker_arm.cpp @@ -25,7 +25,7 @@ #include "prims/upcallLinker.hpp" #include "utilities/debug.hpp" -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index ad8b76ba667..f33255bd006 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -4728,6 +4728,30 @@ class StubGenerator: public StubCodeGenerator { return start; } + // load Method* target of MethodHandle + // R3_ARG1 = jobject receiver + // R19_method = result Method* + address generate_upcall_stub_load_target() { + + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(R3_ARG1, R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS); + // Load target method from receiver + __ load_heap_oop(R19_method, java_lang_invoke_MethodHandle::form_offset(), R3_ARG1, + R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, IS_NOT_NULL); + __ load_heap_oop(R19_method, java_lang_invoke_LambdaForm::vmentry_offset(), R19_method, + R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, IS_NOT_NULL); + __ load_heap_oop(R19_method, java_lang_invoke_MemberName::method_offset(), R19_method, + R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, IS_NOT_NULL); + __ ld(R19_method, java_lang_invoke_ResolvedMethodName::vmtarget_offset(), R19_method); + __ std(R19_method, in_bytes(JavaThread::callee_target_offset()), R16_thread); // just in case callee is deoptimized + + __ blr(); + + return start; + } + // Initialization void generate_initial_stubs() { // Generates all stubs and initializes the entry points @@ -4808,6 +4832,7 @@ class StubGenerator: public StubCodeGenerator { generate_arraycopy_stubs(); StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); } void generate_compiler_stubs() { diff --git a/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp b/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp index b60fd4f16d1..635bab900d1 100644 --- a/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp +++ b/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "classfile/javaClasses.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" @@ -118,7 +119,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; // arg save & restore + move -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -221,7 +222,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry), R0); __ addi(R3_ARG1, R1_SP, frame_data_offset); - __ load_const_optimized(R4_ARG2, (intptr_t)receiver, R0); __ call_c(call_target_address); __ mr(R16_thread, R3_RET); __ block_comment("} on_entry"); @@ -236,12 +236,12 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, as_VMStorage(callerSP), frame::native_abi_minframe_size, frame::jit_out_preserve_size); __ block_comment("} argument shuffle"); - __ block_comment("{ receiver "); - __ get_vm_result(R3_ARG1); - __ block_comment("} receiver "); - - __ load_const_optimized(R19_method, (intptr_t)entry); - __ std(R19_method, in_bytes(JavaThread::callee_target_offset()), R16_thread); + __ block_comment("{ load target "); + __ load_const_optimized(call_target_address, StubRoutines::upcall_stub_load_target(), R0); + __ load_const_optimized(R3_ARG1, (intptr_t)receiver, R0); + __ mtctr(call_target_address); + __ bctrl(); // loads target Method* into R19_method + __ block_comment("} load target "); __ push_cont_fastpath(); @@ -326,7 +326,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char* name = _masm->code_string(ss.as_string()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index c292f671325..0be9e9f7c1e 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -5374,6 +5374,29 @@ static const int64_t right_3_bits = right_n_bits(3); return start; } + // load Method* target of MethodHandle + // j_rarg0 = jobject receiver + // xmethod = Method* result + address generate_upcall_stub_load_target() { + + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(j_rarg0, t0, t1); + // Load target method from receiver + __ load_heap_oop(xmethod, Address(j_rarg0, java_lang_invoke_MethodHandle::form_offset()), t0, t1); + __ load_heap_oop(xmethod, Address(xmethod, java_lang_invoke_LambdaForm::vmentry_offset()), t0, t1); + __ load_heap_oop(xmethod, Address(xmethod, java_lang_invoke_MemberName::method_offset()), t0, t1); + __ access_load_at(T_ADDRESS, IN_HEAP, xmethod, + Address(xmethod, java_lang_invoke_ResolvedMethodName::vmtarget_offset()), + noreg, noreg); + __ sd(xmethod, Address(xthread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + + __ ret(); + + return start; + } + // Continuation point for throwing of implicit exceptions that are // not handled in the current activation. Fabricates an exception // oop and initiates normal exception dispatching in this @@ -5567,6 +5590,7 @@ static const int64_t right_3_bits = right_n_bits(3); } StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); StubRoutines::riscv::set_completed(); } diff --git a/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp b/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp index 383f332f8fd..55160be99d0 100644 --- a/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp +++ b/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" +#include "classfile/javaClasses.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" @@ -117,7 +118,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -223,7 +224,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ la(c_rarg0, Address(sp, frame_data_offset)); - __ movptr(c_rarg1, (address) receiver); __ rt_call(CAST_FROM_FN_PTR(address, UpcallLinker::on_entry)); __ mv(xthread, x10); __ reinit_heapbase(); @@ -260,12 +260,10 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, as_VMStorage(shuffle_reg), abi._shadow_space_bytes, 0); __ block_comment("} argument shuffle"); - __ block_comment("{ receiver "); - __ get_vm_result(j_rarg0, xthread); - __ block_comment("} receiver "); - - __ mov_metadata(xmethod, entry); - __ sd(xmethod, Address(xthread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + __ block_comment("{ load target "); + __ movptr(j_rarg0, (address) receiver); + __ far_call(RuntimeAddress(StubRoutines::upcall_stub_load_target())); // loads Method* into xmethod + __ block_comment("} load target "); __ push_cont_fastpath(xthread); @@ -338,7 +336,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char *name = _masm->code_string(ss.as_string()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index dfd20730b84..ea9b1d49d0f 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -3107,6 +3107,29 @@ class StubGenerator: public StubCodeGenerator { return start; } + // load Method* target of MethodHandle + // Z_ARG1 = jobject receiver + // Z_method = Method* result + address generate_upcall_stub_load_target() { + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(Z_ARG1, Z_tmp_1, Z_tmp_2); + // Load target method from receiver + __ load_heap_oop(Z_method, Address(Z_ARG1, java_lang_invoke_MethodHandle::form_offset()), + noreg, noreg, IS_NOT_NULL); + __ load_heap_oop(Z_method, Address(Z_method, java_lang_invoke_LambdaForm::vmentry_offset()), + noreg, noreg, IS_NOT_NULL); + __ load_heap_oop(Z_method, Address(Z_method, java_lang_invoke_MemberName::method_offset()), + noreg, noreg, IS_NOT_NULL); + __ z_lg(Z_method, Address(Z_method, java_lang_invoke_ResolvedMethodName::vmtarget_offset())); + __ z_stg(Z_method, Address(Z_thread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + + __ z_br(Z_R14); + + return start; + } + void generate_initial_stubs() { // Generates all stubs and initializes the entry points. @@ -3188,6 +3211,7 @@ class StubGenerator: public StubCodeGenerator { } StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); } void generate_compiler_stubs() { diff --git a/src/hotspot/cpu/s390/upcallLinker_s390.cpp b/src/hotspot/cpu/s390/upcallLinker_s390.cpp index 734b4e89c7c..8baad40a519 100644 --- a/src/hotspot/cpu/s390/upcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/upcallLinker_s390.cpp @@ -23,6 +23,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "classfile/javaClasses.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" @@ -116,7 +117,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; // arg save & restore + move -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -206,7 +207,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("on_entry {"); __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry)); __ z_aghik(Z_ARG1, Z_SP, frame_data_offset); - __ load_const_optimized(Z_ARG2, (intptr_t)receiver); __ call(call_target_address); __ z_lgr(Z_thread, Z_RET); __ block_comment("} on_entry"); @@ -216,12 +216,11 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, shuffle_reg, abi._shadow_space_bytes, frame::z_jit_out_preserve_size); __ block_comment("} argument_shuffle"); - __ block_comment("receiver {"); - __ get_vm_result(Z_ARG1); - __ block_comment("} receiver"); - - __ load_const_optimized(Z_method, (intptr_t)entry); - __ z_stg(Z_method, Address(Z_thread, in_bytes(JavaThread::callee_target_offset()))); + __ block_comment("load_target {"); + __ load_const_optimized(Z_ARG1, (intptr_t)receiver); + __ load_const_optimized(call_target_address, StubRoutines::upcall_stub_load_target()); + __ call(call_target_address); // load taget Method* into Z_method + __ block_comment("} load_target"); __ z_lg(call_target_address, Address(Z_method, in_bytes(Method::from_compiled_offset()))); __ call(call_target_address); @@ -274,7 +273,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char* name = _masm->code_string(ss.as_string()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index 577c56cb7a2..c227679176f 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" +#include "classfile/javaClasses.hpp" #include "classfile/vmIntrinsics.hpp" #include "compiler/oopMap.hpp" #include "gc/shared/barrierSet.hpp" @@ -3988,6 +3989,28 @@ address StubGenerator::generate_upcall_stub_exception_handler() { return start; } +// load Method* target of MethodHandle +// j_rarg0 = jobject receiver +// rbx = result +address StubGenerator::generate_upcall_stub_load_target() { + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(j_rarg0, r15_thread, rscratch1); + // Load target method from receiver + __ load_heap_oop(rbx, Address(j_rarg0, java_lang_invoke_MethodHandle::form_offset()), rscratch1); + __ load_heap_oop(rbx, Address(rbx, java_lang_invoke_LambdaForm::vmentry_offset()), rscratch1); + __ load_heap_oop(rbx, Address(rbx, java_lang_invoke_MemberName::method_offset()), rscratch1); + __ access_load_at(T_ADDRESS, IN_HEAP, rbx, + Address(rbx, java_lang_invoke_ResolvedMethodName::vmtarget_offset()), + noreg, noreg); + __ movptr(Address(r15_thread, JavaThread::callee_target_offset()), rbx); // just in case callee is deoptimized + + __ ret(0); + + return start; +} + address StubGenerator::generate_lookup_secondary_supers_table_stub(u1 super_klass_index) { StubCodeMark mark(this, "StubRoutines", "lookup_secondary_supers_table"); @@ -4190,6 +4213,7 @@ void StubGenerator::generate_final_stubs() { } StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); } void StubGenerator::generate_compiler_stubs() { diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 02435bd172c..b427572b565 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -615,6 +615,7 @@ class StubGenerator: public StubCodeGenerator { // shared exception handler for FFM upcall stubs address generate_upcall_stub_exception_handler(); + address generate_upcall_stub_load_target(); // Specialized stub implementations for UseSecondarySupersTable. address generate_lookup_secondary_supers_table_stub(u1 super_klass_index); diff --git a/src/hotspot/cpu/x86/upcallLinker_x86_32.cpp b/src/hotspot/cpu/x86/upcallLinker_x86_32.cpp index e5075e180d9..d795c751d02 100644 --- a/src/hotspot/cpu/x86/upcallLinker_x86_32.cpp +++ b/src/hotspot/cpu/x86/upcallLinker_x86_32.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" #include "prims/upcallLinker.hpp" -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, diff --git a/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp b/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp index 7b9d49dd461..a3f86c2450b 100644 --- a/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp +++ b/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp @@ -23,7 +23,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" -#include "code/codeBlob.hpp" +#include "classfile/javaClasses.hpp" #include "code/codeBlob.hpp" #include "code/vmreg.inline.hpp" #include "compiler/disassembler.hpp" @@ -165,10 +165,10 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr __ block_comment("} restore_callee_saved_regs "); } -static const int upcall_stub_code_base_size = 1024; +static const int upcall_stub_code_base_size = 1200; static const int upcall_stub_size_per_arg = 16; -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -277,7 +277,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ vzeroupper(); __ lea(c_rarg0, Address(rsp, frame_data_offset)); - __ movptr(c_rarg1, (intptr_t)receiver); // stack already aligned __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, UpcallLinker::on_entry))); __ movptr(r15_thread, rax); @@ -293,12 +292,10 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, shuffle_reg, abi._shadow_space_bytes, 0); __ block_comment("} argument shuffle"); - __ block_comment("{ receiver "); - __ get_vm_result(j_rarg0, r15_thread); - __ block_comment("} receiver "); - - __ mov_metadata(rbx, entry); - __ movptr(Address(r15_thread, JavaThread::callee_target_offset()), rbx); // just in case callee is deoptimized + __ block_comment("{ load target "); + __ movptr(j_rarg0, (intptr_t)receiver); + __ call(RuntimeAddress(StubRoutines::upcall_stub_load_target())); // puts target Method* in rbx + __ block_comment("} load target "); __ push_cont_fastpath(); @@ -373,7 +370,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char* name = _masm->code_string(ss.freeze()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/zero/upcallLinker_zero.cpp b/src/hotspot/cpu/zero/upcallLinker_zero.cpp index 6447dac86c9..408ebc32820 100644 --- a/src/hotspot/cpu/zero/upcallLinker_zero.cpp +++ b/src/hotspot/cpu/zero/upcallLinker_zero.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" #include "prims/upcallLinker.hpp" -address UpcallLinker::make_upcall_stub(jobject mh, Method* entry, +address UpcallLinker::make_upcall_stub(jobject mh, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, diff --git a/src/hotspot/share/prims/upcallLinker.cpp b/src/hotspot/share/prims/upcallLinker.cpp index 4b924b5302d..8ea22272c6a 100644 --- a/src/hotspot/share/prims/upcallLinker.cpp +++ b/src/hotspot/share/prims/upcallLinker.cpp @@ -22,7 +22,7 @@ */ #include "precompiled.hpp" -#include "classfile/javaClasses.hpp" +#include "classfile/javaClasses.inline.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "compiler/compilationPolicy.hpp" @@ -74,7 +74,7 @@ JavaThread* UpcallLinker::maybe_attach_and_get_thread() { } // modelled after JavaCallWrapper::JavaCallWrapper -JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context, jobject receiver) { +JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context) { JavaThread* thread = maybe_attach_and_get_thread(); guarantee(thread->thread_state() == _thread_in_native, "wrong thread state for upcall"); context->thread = thread; @@ -109,8 +109,6 @@ JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context, jobject recei debug_only(thread->inc_java_call_counter()); thread->set_active_handles(context->new_handles); // install new handle block and reset Java frame linkage - thread->set_vm_result(JNIHandles::resolve(receiver)); - return thread; } @@ -151,36 +149,30 @@ JVM_ENTRY(jlong, UL_MakeUpcallStub(JNIEnv *env, jclass unused, jobject mh, jobje ResourceMark rm(THREAD); Handle mh_h(THREAD, JNIHandles::resolve(mh)); jobject mh_j = JNIHandles::make_global(mh_h); + oop type = java_lang_invoke_MethodHandle::type(mh_h()); - oop lform = java_lang_invoke_MethodHandle::form(mh_h()); - oop vmentry = java_lang_invoke_LambdaForm::vmentry(lform); - Method* entry = java_lang_invoke_MemberName::vmtarget(vmentry); - const methodHandle mh_entry(THREAD, entry); - - assert(entry->method_holder()->is_initialized(), "no clinit barrier"); - CompilationPolicy::compile_if_required(mh_entry, CHECK_0); - - assert(entry->is_static(), "static only"); // Fill in the signature array, for the calling-convention call. - const int total_out_args = entry->size_of_parameters(); - assert(total_out_args > 0, "receiver arg"); + const int total_out_args = java_lang_invoke_MethodType::ptype_slot_count(type) + 1; // +1 for receiver + bool create_new = true; + TempNewSymbol signature = java_lang_invoke_MethodType::as_signature(type, create_new); BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_out_args); BasicType ret_type; { int i = 0; - SignatureStream ss(entry->signature()); + out_sig_bt[i++] = T_OBJECT; // receiver MH + SignatureStream ss(signature); for (; !ss.at_return_type(); ss.next()) { out_sig_bt[i++] = ss.type(); // Collect remaining bits of signature if (ss.type() == T_LONG || ss.type() == T_DOUBLE) out_sig_bt[i++] = T_VOID; // Longs & doubles take 2 Java slots } - assert(i == total_out_args, ""); + assert(i == total_out_args, "%d != %d", i, total_out_args); ret_type = ss.type(); } return (jlong) UpcallLinker::make_upcall_stub( - mh_j, entry, out_sig_bt, total_out_args, ret_type, + mh_j, signature, out_sig_bt, total_out_args, ret_type, abi, conv, needs_return_buffer, checked_cast(ret_buf_size)); JVM_END diff --git a/src/hotspot/share/prims/upcallLinker.hpp b/src/hotspot/share/prims/upcallLinker.hpp index d80516b2566..765ed63fc5a 100644 --- a/src/hotspot/share/prims/upcallLinker.hpp +++ b/src/hotspot/share/prims/upcallLinker.hpp @@ -34,10 +34,10 @@ class UpcallLinker { private: static JavaThread* maybe_attach_and_get_thread(); - static JavaThread* on_entry(UpcallStub::FrameData* context, jobject receiver); + static JavaThread* on_entry(UpcallStub::FrameData* context); static void on_exit(UpcallStub::FrameData* context); public: - static address make_upcall_stub(jobject mh, Method* entry, + static address make_upcall_stub(jobject mh, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index 74286a4ac98..9a1a545ee9f 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -195,6 +195,7 @@ JFR_ONLY(RuntimeStub* StubRoutines::_jfr_return_lease_stub = nullptr;) JFR_ONLY(address StubRoutines::_jfr_return_lease = nullptr;) address StubRoutines::_upcall_stub_exception_handler = nullptr; +address StubRoutines::_upcall_stub_load_target = nullptr; address StubRoutines::_lookup_secondary_supers_table_slow_path_stub = nullptr; address StubRoutines::_lookup_secondary_supers_table_stubs[Klass::SECONDARY_SUPERS_TABLE_SIZE] = { nullptr }; diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index 65b0c0d2f26..d5aa1377327 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -277,6 +277,7 @@ class StubRoutines: AllStatic { static address _vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP]; static address _upcall_stub_exception_handler; + static address _upcall_stub_load_target; static address _lookup_secondary_supers_table_stubs[]; static address _lookup_secondary_supers_table_slow_path_stub; @@ -493,6 +494,11 @@ class StubRoutines: AllStatic { return _upcall_stub_exception_handler; } + static address upcall_stub_load_target() { + assert(_upcall_stub_load_target != nullptr, "not implemented"); + return _upcall_stub_load_target; + } + static address lookup_secondary_supers_table_stub(u1 slot) { assert(slot < Klass::SECONDARY_SUPERS_TABLE_SIZE, "out of bounds"); assert(_lookup_secondary_supers_table_stubs[slot] != nullptr, "not implemented"); diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index 97b355fbea6..43f1089226c 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -379,7 +379,8 @@ jdk_svc = \ jdk_foreign = \ java/foreign \ jdk/internal/reflect/CallerSensitive/CheckCSMs.java \ - -java/foreign/TestMatrix.java + -java/foreign/TestMatrix.java \ + -java/foreign/TestUpcallStress.java jdk_vector = \ jdk/incubator/vector diff --git a/test/jdk/java/foreign/TestUpcallStress.java b/test/jdk/java/foreign/TestUpcallStress.java new file mode 100644 index 00000000000..40607746856 --- /dev/null +++ b/test/jdk/java/foreign/TestUpcallStress.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * 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 www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @requires jdk.foreign.linker != "FALLBACK" + * @requires (os.arch == "aarch64" | os.arch=="riscv64") & os.name == "Linux" + * @requires os.maxMemory > 4G + * @modules java.base/jdk.internal.foreign + * @build NativeTestHelper CallGeneratorHelper TestUpcallBase + * @bug 8337753 + * + * @run testng/othervm/timeout=3200 + * -Xcheck:jni + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:-VerifyDependencies + * --enable-native-access=ALL-UNNAMED + * -Dgenerator.sample.factor=17 + * TestUpcallStress + */ + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemorySegment; + +import org.testng.annotations.Test; + +import java.lang.invoke.MethodHandle; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.*; +import java.util.function.Consumer; + +public class TestUpcallStress extends TestUpcallBase { + + static { + System.loadLibrary("TestUpcall"); + } + + @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class) + public void testUpcallsStress(int count, String fName, Ret ret, List paramTypes, + List fields) throws Throwable { + ExecutorService executor = Executors.newFixedThreadPool(16); + for (int threadIdx = 0; threadIdx < 16; threadIdx++) { + executor.submit(() -> { + for (int iter = 0; iter < 10000; iter++) { + List> returnChecks = new ArrayList<>(); + List> argChecks = new ArrayList<>(); + MemorySegment addr = findNativeOrThrow(fName); + try (Arena arena = Arena.ofConfined()) { + FunctionDescriptor descriptor = function(ret, paramTypes, fields); + MethodHandle mh = downcallHandle(LINKER, addr, arena, descriptor); + AtomicReference capturedArgs = new AtomicReference<>(); + Object[] args = makeArgs(capturedArgs, arena, descriptor, returnChecks, argChecks, 0); + + Object res = mh.invokeWithArguments(args); + + if (ret == Ret.NON_VOID) { + returnChecks.forEach(c -> c.accept(res)); + } + + Object[] capturedArgsArr = capturedArgs.get(); + for (int i = 0; i < capturedArgsArr.length; i++) { + argChecks.get(i).accept(capturedArgsArr[i]); + } + } catch (Throwable ex) { + throw new AssertionError(ex); + } + } + }); + } + // This shutdownNow is 'wrong', since it doesn't wait for tasks to terminate, + // but it seems to be the only way to reproduce the race of JDK-8337753 + executor.shutdownNow(); + } +} From 290b4b9bc4684e85829412ec36b5ab19accb97a1 Mon Sep 17 00:00:00 2001 From: Ravi Reddy Date: Fri, 29 Nov 2024 06:48:36 +0000 Subject: [PATCH 12/14] 8342489: compiler/c2/irTests/TestVectorizationMismatchedAccess.java fails on big-endian platforms Backport-of: 37c05c05c1977bcf22cdeb9d0b6c44a4fa51d7cc --- .../TestVectorizationMismatchedAccess.java | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java index 2fdbb0816ad..2d17753ba94 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java @@ -50,9 +50,6 @@ public class TestVectorizationMismatchedAccess { private final static WhiteBox wb = WhiteBox.getWhiteBox(); public static void main(String[] args) { - if (ByteOrder.nativeOrder() != ByteOrder.LITTLE_ENDIAN) { - throw new RuntimeException("fix test that was written for a little endian platform"); - } TestFramework.runWithFlags("--add-modules", "java.base", "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED"); } @@ -77,6 +74,14 @@ public class TestVectorizationMismatchedAccess { } } + // Method to adjust the value for the native byte order + static private long handleByteOrder(long value) { + if (ByteOrder.nativeOrder() != ByteOrder.LITTLE_ENDIAN) { + value = Long.reverseBytes(value); + } + return value; + } + static private void runAndVerify(Runnable test, int offset) { System.arraycopy(verifyLongArray, 0, longArray, 0, longArray.length); Arrays.fill(byteArray, (byte)0); @@ -154,7 +159,7 @@ public class TestVectorizationMismatchedAccess { // might get fixed with JDK-8325155. public static void testByteLong1a(byte[] dest, long[] src) { for (int i = 0; i < src.length; i++) { - UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i, src[i]); + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i, handleByteOrder(src[i])); } } @@ -165,7 +170,7 @@ public class TestVectorizationMismatchedAccess { // 32-bit: address has ConvL2I for cast of long to address, not supported. public static void testByteLong1b(byte[] dest, long[] src) { for (int i = 0; i < src.length; i++) { - UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i, src[i]); + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i, handleByteOrder(src[i])); } } @@ -175,7 +180,7 @@ public class TestVectorizationMismatchedAccess { public static void testByteLong1c(byte[] dest, long[] src) { long base = 64; // make sure it is big enough and 8 byte aligned (required for 32-bit) for (int i = 0; i < src.length - 8; i++) { - UNSAFE.putLongUnaligned(dest, base + 8 * i, src[i]); + UNSAFE.putLongUnaligned(dest, base + 8 * i, handleByteOrder(src[i])); } } @@ -187,7 +192,7 @@ public class TestVectorizationMismatchedAccess { public static void testByteLong1d(byte[] dest, long[] src) { long base = 64; // make sure it is big enough and 8 byte aligned (required for 32-bit) for (int i = 0; i < src.length - 8; i++) { - UNSAFE.putLongUnaligned(dest, base + 8L * i, src[i]); + UNSAFE.putLongUnaligned(dest, base + 8L * i, handleByteOrder(src[i])); } } @@ -207,7 +212,7 @@ public class TestVectorizationMismatchedAccess { // might get fixed with JDK-8325155. public static void testByteLong2a(byte[] dest, long[] src) { for (int i = 1; i < src.length; i++) { - UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i - 1), src[i]); + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i - 1), handleByteOrder(src[i])); } } @@ -218,7 +223,7 @@ public class TestVectorizationMismatchedAccess { // 32-bit: address has ConvL2I for cast of long to address, not supported. public static void testByteLong2b(byte[] dest, long[] src) { for (int i = 1; i < src.length; i++) { - UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i - 1), src[i]); + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i - 1), handleByteOrder(src[i])); } } @@ -236,7 +241,7 @@ public class TestVectorizationMismatchedAccess { // might get fixed with JDK-8325155. public static void testByteLong3a(byte[] dest, long[] src) { for (int i = 0; i < src.length - 1; i++) { - UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + 1), src[i]); + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + 1), handleByteOrder(src[i])); } } @@ -247,7 +252,7 @@ public class TestVectorizationMismatchedAccess { // 32-bit: address has ConvL2I for cast of long to address, not supported. public static void testByteLong3b(byte[] dest, long[] src) { for (int i = 0; i < src.length - 1; i++) { - UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + 1), src[i]); + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + 1), handleByteOrder(src[i])); } } @@ -267,7 +272,7 @@ public class TestVectorizationMismatchedAccess { // AlignVector cannot guarantee that invar is aligned. public static void testByteLong4a(byte[] dest, long[] src, int start, int stop) { for (int i = start; i < stop; i++) { - UNSAFE.putLongUnaligned(dest, 8 * i + baseOffset, src[i]); + UNSAFE.putLongUnaligned(dest, 8 * i + baseOffset, handleByteOrder(src[i])); } } @@ -280,7 +285,7 @@ public class TestVectorizationMismatchedAccess { // AlignVector cannot guarantee that invar is aligned. public static void testByteLong4b(byte[] dest, long[] src, int start, int stop) { for (int i = start; i < stop; i++) { - UNSAFE.putLongUnaligned(dest, 8L * i + baseOffset, src[i]); + UNSAFE.putLongUnaligned(dest, 8L * i + baseOffset, handleByteOrder(src[i])); } } @@ -299,7 +304,7 @@ public class TestVectorizationMismatchedAccess { // might get fixed with JDK-8325155. public static void testByteLong5a(byte[] dest, long[] src, int start, int stop) { for (int i = start; i < stop; i++) { - UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + baseOffset), src[i]); + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + baseOffset), handleByteOrder(src[i])); } } @@ -310,7 +315,7 @@ public class TestVectorizationMismatchedAccess { // 32-bit: address has ConvL2I for cast of long to address, not supported. public static void testByteLong5b(byte[] dest, long[] src, int start, int stop) { for (int i = start; i < stop; i++) { - UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + baseOffset), src[i]); + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + baseOffset), handleByteOrder(src[i])); } } @@ -454,7 +459,7 @@ public class TestVectorizationMismatchedAccess { // See: JDK-8331576 public static void testOffHeapLong1a(long dest, long[] src) { for (int i = 0; i < src.length; i++) { - UNSAFE.putLongUnaligned(null, dest + 8 * i, src[i]); + UNSAFE.putLongUnaligned(null, dest + 8 * i, handleByteOrder(src[i])); } } @@ -465,7 +470,7 @@ public class TestVectorizationMismatchedAccess { // See: JDK-8331576 public static void testOffHeapLong1b(long dest, long[] src) { for (int i = 0; i < src.length; i++) { - UNSAFE.putLongUnaligned(null, dest + 8L * i, src[i]); + UNSAFE.putLongUnaligned(null, dest + 8L * i, handleByteOrder(src[i])); } } @@ -482,7 +487,7 @@ public class TestVectorizationMismatchedAccess { // See: JDK-8331576 public static void testOffHeapLong2a(long dest, long[] src) { for (int i = 1; i < src.length; i++) { - UNSAFE.putLongUnaligned(null, dest + 8 * (i - 1), src[i]); + UNSAFE.putLongUnaligned(null, dest + 8 * (i - 1), handleByteOrder(src[i])); } } @@ -493,7 +498,7 @@ public class TestVectorizationMismatchedAccess { // See: JDK-8331576 public static void testOffHeapLong2b(long dest, long[] src) { for (int i = 1; i < src.length; i++) { - UNSAFE.putLongUnaligned(null, dest + 8L * (i - 1), src[i]); + UNSAFE.putLongUnaligned(null, dest + 8L * (i - 1), handleByteOrder(src[i])); } } @@ -510,7 +515,7 @@ public class TestVectorizationMismatchedAccess { // See: JDK-8331576 public static void testOffHeapLong3a(long dest, long[] src) { for (int i = 0; i < src.length - 1; i++) { - UNSAFE.putLongUnaligned(null, dest + 8 * (i + 1), src[i]); + UNSAFE.putLongUnaligned(null, dest + 8 * (i + 1), handleByteOrder(src[i])); } } @@ -521,7 +526,7 @@ public class TestVectorizationMismatchedAccess { // See: JDK-8331576 public static void testOffHeapLong3b(long dest, long[] src) { for (int i = 0; i < src.length - 1; i++) { - UNSAFE.putLongUnaligned(null, dest + 8L * (i + 1), src[i]); + UNSAFE.putLongUnaligned(null, dest + 8L * (i + 1), handleByteOrder(src[i])); } } @@ -540,7 +545,7 @@ public class TestVectorizationMismatchedAccess { // AlignVector cannot guarantee that invar is aligned. public static void testOffHeapLong4a(long dest, long[] src, int start, int stop) { for (int i = start; i < stop; i++) { - UNSAFE.putLongUnaligned(null, dest + 8 * i + baseOffset, src[i]); + UNSAFE.putLongUnaligned(null, dest + 8 * i + baseOffset, handleByteOrder(src[i])); } } @@ -553,7 +558,7 @@ public class TestVectorizationMismatchedAccess { // AlignVector cannot guarantee that invar is aligned. public static void testOffHeapLong4b(long dest, long[] src, int start, int stop) { for (int i = start; i < stop; i++) { - UNSAFE.putLongUnaligned(null, dest + 8L * i + baseOffset, src[i]); + UNSAFE.putLongUnaligned(null, dest + 8L * i + baseOffset, handleByteOrder(src[i])); } } From a7fbf37d16d37966e1f483f2bc52c5e242a963fd Mon Sep 17 00:00:00 2001 From: Ravi Reddy Date: Fri, 29 Nov 2024 06:51:15 +0000 Subject: [PATCH 13/14] 8342962: [s390x] TestOSRLotsOfLocals.java crashes Backport-of: 69d97e02192ae000bee41c21c24453ab43c92709 --- src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index a5e62169a93..a7900042a9b 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -131,9 +131,19 @@ void LIR_Assembler::osr_entry() { // copied into place by code emitted in the IR. Register OSR_buf = osrBufferPointer()->as_register(); - { assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); - int monitor_offset = BytesPerWord * method()->max_locals() + - (2 * BytesPerWord) * (number_of_locks - 1); + { + assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); + + const int locals_space = BytesPerWord * method() -> max_locals(); + int monitor_offset = locals_space + (2 * BytesPerWord) * (number_of_locks - 1); + bool large_offset = !Immediate::is_simm20(monitor_offset + BytesPerWord) && number_of_locks > 0; + + if (large_offset) { + // z_lg can only handle displacement upto 20bit signed binary integer + __ z_algfi(OSR_buf, locals_space); + monitor_offset -= locals_space; + } + // SharedRuntime::OSR_migration_begin() packs BasicObjectLocks in // the OSR buffer using 2 word entries: first the lock and then // the oop. @@ -147,6 +157,10 @@ void LIR_Assembler::osr_entry() { __ z_lg(Z_R1_scratch, slot_offset + 1*BytesPerWord, OSR_buf); __ z_stg(Z_R1_scratch, frame_map()->address_for_monitor_object(i)); } + + if (large_offset) { + __ z_slgfi(OSR_buf, locals_space); + } } } From a13b02ce6f8545b9fce1263f0b5a219b55fb2522 Mon Sep 17 00:00:00 2001 From: Ravi Reddy Date: Fri, 29 Nov 2024 08:15:42 +0000 Subject: [PATCH 14/14] 8340586: JdkJfrEvent::get_all_klasses stores non-strong oops in JNI handles Backport-of: d620426835a15fe75c42758567a7dab4ef0eb772 --- .../share/jfr/support/jfrJdkJfrEvent.cpp | 28 ++++++++----------- src/hotspot/share/oops/klass.hpp | 2 ++ src/hotspot/share/oops/klass.inline.hpp | 8 ++++++ 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp b/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp index cd476302289..a879cf6bbc6 100644 --- a/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp +++ b/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp @@ -35,6 +35,7 @@ #include "oops/klass.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaThread.hpp" +#include "runtime/safepointVerifiers.hpp" #include "utilities/stack.inline.hpp" static jobject empty_java_util_arraylist = nullptr; @@ -80,30 +81,25 @@ static bool is_allowed(const Klass* k) { return !(k->is_abstract() || k->should_be_initialized()); } -static void fill_klasses(GrowableArray& event_subklasses, const InstanceKlass* event_klass, JavaThread* thread) { +static void fill_klasses(GrowableArray& event_subklasses, const InstanceKlass* event_klass, JavaThread* thread) { assert(event_subklasses.length() == 0, "invariant"); assert(event_klass != nullptr, "invariant"); DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread)); + // Do not safepoint while walking the ClassHierarchy, keeping klasses alive and storing their mirrors in JNI handles. + NoSafepointVerifier nsv; for (ClassHierarchyIterator iter(const_cast(event_klass)); !iter.done(); iter.next()) { Klass* subk = iter.klass(); if (is_allowed(subk)) { - event_subklasses.append(subk); + // We are walking the class hierarchy and saving the relevant klasses in JNI handles. + // To be allowed to store the java mirror, we must ensure that the klass and its oops are kept alive, + // and perform the store before the next safepoint. + subk->keep_alive(); + event_subklasses.append((jclass)JfrJavaSupport::local_jni_handle(subk->java_mirror(), thread)); } } } -static void transform_klasses_to_local_jni_handles(GrowableArray& event_subklasses, JavaThread* thread) { - assert(event_subklasses.is_nonempty(), "invariant"); - DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread)); - - for (int i = 0; i < event_subklasses.length(); ++i) { - const InstanceKlass* k = static_cast(event_subklasses.at(i)); - assert(is_allowed(k), "invariant"); - event_subklasses.at_put(i, JfrJavaSupport::local_jni_handle(k->java_mirror(), thread)); - } -} - jobject JdkJfrEvent::get_all_klasses(TRAPS) { DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); initialize(THREAD); @@ -126,15 +122,13 @@ jobject JdkJfrEvent::get_all_klasses(TRAPS) { } ResourceMark rm(THREAD); - GrowableArray event_subklasses(initial_array_size); + GrowableArray event_subklasses(initial_array_size); fill_klasses(event_subklasses, InstanceKlass::cast(klass), THREAD); if (event_subklasses.is_empty()) { return empty_java_util_arraylist; } - transform_klasses_to_local_jni_handles(event_subklasses, THREAD); - Handle h_array_list(THREAD, new_java_util_arraylist(THREAD)); assert(h_array_list.not_null(), "invariant"); @@ -150,7 +144,7 @@ jobject JdkJfrEvent::get_all_klasses(TRAPS) { JavaValue result(T_BOOLEAN); for (int i = 0; i < event_subklasses.length(); ++i) { - const jclass clazz = (jclass)event_subklasses.at(i); + const jclass clazz = event_subklasses.at(i); assert(JdkJfrEvent::is_subklass(clazz), "invariant"); JfrJavaArguments args(&result, array_list_klass, add_method_sym, add_method_sig_sym); args.set_receiver(h_array_list()); diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index 2923235e2f3..b4971325502 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -582,6 +582,8 @@ protected: inline oop klass_holder() const; + inline void keep_alive() const; + protected: // Error handling when length > max_length or length < 0 diff --git a/src/hotspot/share/oops/klass.inline.hpp b/src/hotspot/share/oops/klass.inline.hpp index a72868a08d8..e8c9eb24ebe 100644 --- a/src/hotspot/share/oops/klass.inline.hpp +++ b/src/hotspot/share/oops/klass.inline.hpp @@ -36,6 +36,13 @@ inline oop Klass::klass_holder() const { return class_loader_data()->holder(); } +inline void Klass::keep_alive() const { + // Resolving the holder (a WeakHandle) will keep the klass alive until the next safepoint. + // Making the klass's CLD handle oops (e.g. the java_mirror), safe to store in the object + // graph and its roots (e.g. Handles). + static_cast(klass_holder()); +} + inline bool Klass::is_non_strong_hidden() const { return access_flags().is_hidden_class() && class_loader_data()->has_class_mirror_holder(); @@ -52,6 +59,7 @@ inline bool Klass::is_loader_alive() const { return class_loader_data()->is_alive(); } +// Loading the java_mirror does not keep its holder alive. See Klass::keep_alive(). inline oop Klass::java_mirror() const { return _java_mirror.resolve(); }