From 369c573383a0120e0d85aeb89a211f38b5261013 Mon Sep 17 00:00:00 2001
From: Valerie Peng
Date: Mon, 5 Feb 2024 22:53:51 +0000
Subject: [PATCH 1/9] 8302111: Serialization considerations
Reviewed-by: skoivu, rhalade, weijun, wetmore
---
.../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 | 42 ++-
.../classes/sun/security/pkcs11/P11Key.java | 13 +
.../sun/security/pkcs11/P11SecureRandom.java | 16 +-
.../sun/security/pkcs11/SunPKCS11.java | 13 +
21 files changed, 747 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 fa596fc6a1e..cb9b8746994 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;
@@ -1408,6 +1410,20 @@ class Krb5Context implements GSSContextSpi {
return "Kerberos session key: etype: " + key.getEType() + "\n" +
new HexDumpEncoder().encodeBuffer(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");
+ }
}
/**
@@ -1477,5 +1493,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..db6192ce9ee 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,36 @@ 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 d528f1b8485..49718e254b3 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 44a2c4efb06..e77edc98399 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
@@ -1947,6 +1947,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 893e7bc894e2828f8d02db06302485d02f2cfa5a Mon Sep 17 00:00:00 2001
From: Weijun Wang
Date: Wed, 17 Apr 2024 22:38:46 +0000
Subject: [PATCH 2/9] 8328726: Better Kerberos support
Reviewed-by: ahgross, rhalade, valeriep, coffeys
---
.../security/auth/kerberos/EncryptionKey.java | 4 ++--
.../auth/kerberos/KerberosCredMessage.java | 6 ++---
.../security/auth/kerberos/KerberosKey.java | 8 +++----
.../javax/security/auth/kerberos/KeyImpl.java | 16 +++++---------
.../sun/security/jgss/krb5/Krb5Context.java | 22 +++++--------------
.../sun/security/jgss/krb5/Krb5Util.java | 15 +++++++++++++
.../sun/security/krb5/EncryptionKey.java | 8 ++-----
.../sun/security/krb5/internal/Krb5.java | 3 ---
.../security/krb5/internal/tools/Kinit.java | 4 ----
.../pkcs11/wrapper/CK_PBE_PARAMS.java | 5 -----
.../security/auth/module/Krb5LoginModule.java | 10 +++------
11 files changed, 39 insertions(+), 62 deletions(-)
diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java
index 5a6f74e130c..6140168ebbb 100644
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 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
@@ -170,7 +170,7 @@ public final class EncryptionKey implements SecretKey {
if (destroyed) {
return "Destroyed EncryptionKey";
}
- return "key " + key.toString();
+ return "EncryptionKey: " + key.toString();
}
/**
diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java
index c6f3f083a66..c331833e9f0 100644
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 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,7 +27,6 @@ package javax.security.auth.kerberos;
import javax.security.auth.Destroyable;
import java.util.Arrays;
-import java.util.Base64;
import java.util.Objects;
/**
@@ -140,8 +139,7 @@ public final class KerberosCredMessage implements Destroyable {
if (destroyed) {
return "Destroyed KerberosCredMessage";
} else {
- return "KRB_CRED from " + sender + " to " + recipient + ":\n"
- + Base64.getUrlEncoder().encodeToString(message);
+ return "KRB_CRED from " + sender + " to " + recipient;
}
}
diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java
index 55c1be3c0d8..71aaddda9de 100644
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2023, 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
@@ -273,9 +273,9 @@ public class KerberosKey implements SecretKey {
if (destroyed) {
return "Destroyed KerberosKey";
}
- return "Kerberos Principal " + principal +
- "Key Version " + versionNum +
- "key " + key.toString();
+ return "KerberosKey: principal " + principal +
+ ", version " + versionNum +
+ ", key " + key.toString();
}
/**
diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java
index 46168cf8377..b18f7d8eae1 100644
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.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
@@ -30,7 +30,8 @@ import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.security.auth.Destroyable;
import javax.security.auth.DestroyFailedException;
-import sun.security.util.HexDumpEncoder;
+
+import sun.security.jgss.krb5.Krb5Util;
import sun.security.krb5.Asn1Exception;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.EncryptionKey;
@@ -225,15 +226,8 @@ class KeyImpl implements SecretKey, Destroyable, Serializable {
}
public String toString() {
- HexDumpEncoder hd = new HexDumpEncoder();
- return "EncryptionKey: keyType=" + keyType
- + " keyBytes (hex dump)="
- + (keyBytes == null || keyBytes.length == 0 ?
- " Empty Key" :
- '\n' + hd.encodeBuffer(keyBytes)
- + '\n');
-
-
+ return "keyType=" + keyType
+ + ", " + Krb5Util.keyInfo(keyBytes);
}
public int hashCode() {
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 cb9b8746994..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
@@ -901,15 +901,11 @@ class Krb5Context implements GSSContextSpi {
public final byte[] wrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException {
- if (DEBUG != null) {
- DEBUG.println("Krb5Context.wrap: data=["
- + getHexBytes(inBuf, offset, len)
- + "]");
- }
- if (state != STATE_DONE)
- throw new GSSException(GSSException.NO_CONTEXT, -1,
- "Wrap called in invalid state!");
+ if (state != STATE_DONE) {
+ throw new GSSException(GSSException.NO_CONTEXT, -1,
+ "Wrap called in invalid state!");
+ }
byte[] encToken = null;
try {
@@ -1052,12 +1048,6 @@ class Krb5Context implements GSSContextSpi {
setSequencingAndReplayProps(token, msgProp);
}
- if (DEBUG != null) {
- DEBUG.println("Krb5Context.unwrap: data=["
- + getHexBytes(data, 0, data.length)
- + "]");
- }
-
return data;
}
@@ -1407,8 +1397,8 @@ class Krb5Context implements GSSContextSpi {
@Override
public String toString() {
- return "Kerberos session key: etype: " + key.getEType() + "\n" +
- new HexDumpEncoder().encodeBuffer(key.getBytes());
+ return "Kerberos session key: etype=" + key.getEType()
+ + ", " + Krb5Util.keyInfo(key.getBytes());
}
/**
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java
index 10dea6749e5..e784b7b33ca 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java
@@ -187,4 +187,19 @@ public class Krb5Util {
KeyTab ktab, PrincipalName cname) {
return snapshotFromJavaxKeyTab(ktab).readServiceKeys(cname);
}
+
+ public static String keyInfo(byte[] data) {
+ if (data == null) {
+ return "null key";
+ } else if (data.length == 0) {
+ return "empty key";
+ } else {
+ for (byte b : data) {
+ if (b != 0) {
+ return data.length + "-byte key";
+ }
+ }
+ return data.length + "-byte zero key";
+ }
+ }
}
diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java b/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java
index f975ba15a67..b5453fae916 100644
--- a/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java
@@ -31,6 +31,7 @@
package sun.security.krb5;
+import sun.security.jgss.krb5.Krb5Util;
import sun.security.util.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
@@ -498,12 +499,7 @@ public class EncryptionKey
public String toString() {
return "EncryptionKey: keyType=" + keyType
- + " kvno=" + kvno
- + " keyValue (hex dump)="
- + (keyValue == null || keyValue.length == 0 ?
- " Empty Key" : '\n'
- + Krb5.hexDumper.encodeBuffer(keyValue)
- + '\n');
+ + ", kvno=" + kvno + ", " + Krb5Util.keyInfo(keyValue);
}
/**
diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java
index c3cac113c40..0850abb53c8 100644
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java
@@ -320,9 +320,6 @@ public class Krb5 {
public static final Debug DEBUG = Debug.of("krb5", GetPropertyAction
.privilegedGetProperty("sun.security.krb5.debug"));
- public static final sun.security.util.HexDumpEncoder hexDumper =
- new sun.security.util.HexDumpEncoder();
-
static {
errMsgList = new Hashtable ();
errMsgList.put(KDC_ERR_NONE, "No error");
diff --git a/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Kinit.java b/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Kinit.java
index 9d24de5c2e6..6b9f5de0c3d 100644
--- a/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Kinit.java
+++ b/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Kinit.java
@@ -195,10 +195,6 @@ public class Kinit {
System.out.print("Password for " + princName + ":");
System.out.flush();
psswd = Password.readPassword(System.in);
- if (DEBUG != null) {
- DEBUG.println(">>> Kinit console input " +
- new String(psswd));
- }
}
builder = new KrbAsReqBuilder(principal, psswd);
} else {
diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java
index a25fa1c39e5..d6c291ebc57 100644
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java
@@ -127,11 +127,6 @@ public class CK_PBE_PARAMS {
sb.append(pPassword.length);
sb.append(Constants.NEWLINE);
- sb.append(Constants.INDENT);
- sb.append("pPassword: ");
- sb.append(pPassword);
- sb.append(Constants.NEWLINE);
-
sb.append(Constants.INDENT);
sb.append("ulSaltLen: ");
sb.append(pSalt.length);
diff --git a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java
index 24f26d4364b..18016a07260 100644
--- a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java
+++ b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java
@@ -43,7 +43,7 @@ import sun.security.krb5.*;
import sun.security.jgss.krb5.Krb5Util;
import sun.security.krb5.Credentials;
import sun.security.util.Debug;
-import sun.security.util.HexDumpEncoder;
+
import static sun.security.util.ResourcesMgr.getAuthResourceString;
/**
@@ -769,15 +769,11 @@ public class Krb5LoginModule implements LoginModule {
if (debug != null) {
debug.println("principal is " + principal);
- HexDumpEncoder hd = new HexDumpEncoder();
if (ktab != null) {
debug.println("Will use keytab");
} else if (storeKey) {
for (int i = 0; i < encKeys.length; i++) {
- debug.println("EncryptionKey: keyType=" +
- encKeys[i].getEType() +
- " keyBytes (hex dump)=" +
- hd.encodeBuffer(encKeys[i].getBytes()));
+ debug.println(encKeys[i].toString());
}
}
}
@@ -868,7 +864,7 @@ public class Krb5LoginModule implements LoginModule {
}
if (debug != null) {
debug.println
- ("password is " + new String(password));
+ ("Get password from shared state");
}
return;
}
From 03bc6b359fc5ff4fa2b569723903cdf1c9c5775b Mon Sep 17 00:00:00 2001
From: Daniel Fuchs
Date: Tue, 7 May 2024 19:29:49 +0000
Subject: [PATCH 3/9] 8328286: Enhance HTTP client
Reviewed-by: aefimov, michaelm
---
.../java/net/doc-files/net-properties.html | 9 +
.../classes/sun/net/www/MessageHeader.java | 55 ++++
.../www/protocol/http/HttpURLConnection.java | 23 +-
src/java.base/share/conf/net.properties | 17 +
.../jdk/internal/net/http/Exchange.java | 45 ++-
.../internal/net/http/Http1HeaderParser.java | 49 ++-
.../internal/net/http/Http2ClientImpl.java | 78 +++--
.../internal/net/http/Http2Connection.java | 294 ++++++++++++++----
.../jdk/internal/net/http/HttpClientImpl.java | 4 +-
.../internal/net/http/HttpRequestImpl.java | 4 +-
.../net/http/ResponseBodyHandlers.java | 12 +-
.../classes/jdk/internal/net/http/Stream.java | 118 +++++--
.../net/http/common/HeaderDecoder.java | 6 +-
.../jdk/internal/net/http/common/Utils.java | 13 +
.../jdk/internal/net/http/hpack/Decoder.java | 80 ++++-
.../net/http/hpack/DecodingCallback.java | 14 +
.../jdk/internal/net/http/hpack/Encoder.java | 14 +-
.../share/classes/module-info.java | 30 +-
.../share/classes/module-info.java | 12 +-
.../classes/sun/net/httpserver/Request.java | 35 ++-
.../sun/net/httpserver/ServerConfig.java | 18 +-
.../net/httpclient/ExpectContinueTest.java | 3 +-
test/jdk/java/net/httpclient/ShutdownNow.java | 1 +
.../http2/PushPromiseContinuation.java | 9 +-
.../test/lib/common/HttpServerAdapters.java | 12 +-
.../test/lib/http2/HpackTestEncoder.java | 174 +++++++++++
.../test/lib/http2/Http2TestExchange.java | 13 +
.../test/lib/http2/Http2TestExchangeImpl.java | 16 +-
.../lib/http2/Http2TestServerConnection.java | 153 +++++++--
29 files changed, 1126 insertions(+), 185 deletions(-)
create mode 100644 test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/HpackTestEncoder.java
diff --git a/src/java.base/share/classes/java/net/doc-files/net-properties.html b/src/java.base/share/classes/java/net/doc-files/net-properties.html
index a61844cca6e..a67df0c0d00 100644
--- a/src/java.base/share/classes/java/net/doc-files/net-properties.html
+++ b/src/java.base/share/classes/java/net/doc-files/net-properties.html
@@ -253,6 +253,15 @@ to determine the proxy that should be used for connecting to a given URI.
The channel binding tokens generated are of the type "tls-server-end-point" as defined in
RFC 5929.
+
+ {@systemProperty sun.net.httpserver.maxReqTime} (default: -1)
* The maximum time in milliseconds allowed to receive a request headers and body.
* In practice, the actual time is a function of request size, network speed, and handler
diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java
index 99dac0547ce..6c0c2c271ed 100644
--- a/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java
+++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.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
@@ -44,8 +44,10 @@ class Request {
private SocketChannel chan;
private InputStream is;
private OutputStream os;
+ private final int maxReqHeaderSize;
Request (InputStream rawInputStream, OutputStream rawout) throws IOException {
+ this.maxReqHeaderSize = ServerConfig.getMaxReqHeaderSize();
is = rawInputStream;
os = rawout;
do {
@@ -75,6 +77,7 @@ class Request {
public String readLine () throws IOException {
boolean gotCR = false, gotLF = false;
pos = 0; lineBuf = new StringBuffer();
+ long lsize = 32;
while (!gotLF) {
int c = is.read();
if (c == -1) {
@@ -87,20 +90,27 @@ class Request {
gotCR = false;
consume (CR);
consume (c);
+ lsize = lsize + 2;
}
} else {
if (c == CR) {
gotCR = true;
} else {
consume (c);
+ lsize = lsize + 1;
}
}
+ if (maxReqHeaderSize > 0 && lsize > maxReqHeaderSize) {
+ throw new IOException("Maximum header (" +
+ "sun.net.httpserver.maxReqHeaderSize) exceeded, " +
+ ServerConfig.getMaxReqHeaderSize() + ".");
+ }
}
lineBuf.append (buf, 0, pos);
return new String (lineBuf);
}
- private void consume (int c) {
+ private void consume (int c) throws IOException {
if (pos == BUF_LEN) {
lineBuf.append (buf);
pos = 0;
@@ -138,13 +148,22 @@ class Request {
len = 1;
firstc = c;
}
+ long hsize = startLine.length() + 32L;
while (firstc != LF && firstc != CR && firstc >= 0) {
int keyend = -1;
int c;
boolean inKey = firstc > ' ';
s[len++] = (char) firstc;
+ hsize = hsize + 1;
parseloop:{
+ // We start parsing for a new name value pair here.
+ // The max header size includes an overhead of 32 bytes per
+ // name value pair.
+ // See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2.
+ long maxRemaining = maxReqHeaderSize > 0
+ ? maxReqHeaderSize - hsize - 32
+ : Long.MAX_VALUE;
while ((c = is.read()) >= 0) {
switch (c) {
/*fallthrough*/
@@ -178,6 +197,11 @@ class Request {
s = ns;
}
s[len++] = (char) c;
+ if (maxReqHeaderSize > 0 && len > maxRemaining) {
+ throw new IOException("Maximum header (" +
+ "sun.net.httpserver.maxReqHeaderSize) exceeded, " +
+ ServerConfig.getMaxReqHeaderSize() + ".");
+ }
}
firstc = -1;
}
@@ -205,6 +229,13 @@ class Request {
"sun.net.httpserver.maxReqHeaders) exceeded, " +
ServerConfig.getMaxReqHeaders() + ".");
}
+ hsize = hsize + len + 32;
+ if (maxReqHeaderSize > 0 && hsize > maxReqHeaderSize) {
+ throw new IOException("Maximum header (" +
+ "sun.net.httpserver.maxReqHeaderSize) exceeded, " +
+ ServerConfig.getMaxReqHeaderSize() + ".");
+ }
+
if (k == null) { // Headers disallows null keys, use empty string
k = ""; // instead to represent invalid key
}
diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java
index 21f6165b05e..9186dd4c168 100644
--- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java
+++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2022, 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
@@ -49,6 +49,7 @@ class ServerConfig {
// timing out request/response if max request/response time is configured
private static final long DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS = 1000;
private static final int DEFAULT_MAX_REQ_HEADERS = 200;
+ private static final int DEFAULT_MAX_REQ_HEADER_SIZE = 380 * 1024;
private static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024;
private static long idleTimerScheduleMillis;
@@ -62,6 +63,9 @@ class ServerConfig {
private static int maxIdleConnections;
// The maximum number of request headers allowable
private static int maxReqHeaders;
+ // a maximum value for the header list size. This is the
+ // names size + values size + 32 bytes per field line
+ private static int maxReqHeadersSize;
// max time a request or response is allowed to take
private static long maxReqTime;
private static long maxRspTime;
@@ -107,6 +111,14 @@ class ServerConfig {
maxReqHeaders = DEFAULT_MAX_REQ_HEADERS;
}
+ // a value <= 0 means unlimited
+ maxReqHeadersSize = Integer.getInteger(
+ "sun.net.httpserver.maxReqHeaderSize",
+ DEFAULT_MAX_REQ_HEADER_SIZE);
+ if (maxReqHeadersSize <= 0) {
+ maxReqHeadersSize = 0;
+ }
+
maxReqTime = Long.getLong("sun.net.httpserver.maxReqTime",
DEFAULT_MAX_REQ_TIME);
@@ -215,6 +227,10 @@ class ServerConfig {
return maxReqHeaders;
}
+ static int getMaxReqHeaderSize() {
+ return maxReqHeadersSize;
+ }
+
/**
* @return Returns the maximum amount of time the server will wait for the request to be read
* completely. This method can return a value of 0 or negative to imply no maximum limit has
diff --git a/test/jdk/java/net/httpclient/ExpectContinueTest.java b/test/jdk/java/net/httpclient/ExpectContinueTest.java
index 3d28ae8c8b4..50e43099255 100644
--- a/test/jdk/java/net/httpclient/ExpectContinueTest.java
+++ b/test/jdk/java/net/httpclient/ExpectContinueTest.java
@@ -59,6 +59,7 @@ import java.io.PrintWriter;
import java.io.Writer;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.ProtocolException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
@@ -361,7 +362,7 @@ public class ExpectContinueTest implements HttpServerAdapters {
}
if (exceptionally && testThrowable != null) {
err.println("Finished exceptionally Test throwable: " + testThrowable);
- assertEquals(IOException.class, testThrowable.getClass());
+ assertEquals(testThrowable.getClass(), ProtocolException.class);
} else if (exceptionally) {
throw new TestException("Expected case to finish with an IOException but testException is null");
} else if (resp != null) {
diff --git a/test/jdk/java/net/httpclient/ShutdownNow.java b/test/jdk/java/net/httpclient/ShutdownNow.java
index 77504b5052a..47eb1f6d7c9 100644
--- a/test/jdk/java/net/httpclient/ShutdownNow.java
+++ b/test/jdk/java/net/httpclient/ShutdownNow.java
@@ -136,6 +136,7 @@ public class ShutdownNow implements HttpServerAdapters {
if (message.equals("shutdownNow")) return true;
// exception from cancelling an HTTP/2 stream
if (message.matches("Stream [0-9]+ cancelled")) return true;
+ if (message.contains("connection closed locally")) return true;
return false;
}
diff --git a/test/jdk/java/net/httpclient/http2/PushPromiseContinuation.java b/test/jdk/java/net/httpclient/http2/PushPromiseContinuation.java
index 6395cc839a2..c0021e7e4ea 100644
--- a/test/jdk/java/net/httpclient/http2/PushPromiseContinuation.java
+++ b/test/jdk/java/net/httpclient/http2/PushPromiseContinuation.java
@@ -40,6 +40,7 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.ProtocolException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
@@ -195,7 +196,8 @@ public class PushPromiseContinuation {
client.sendAsync(hreq, HttpResponse.BodyHandlers.ofString(UTF_8), pph);
CompletionException t = expectThrows(CompletionException.class, () -> cf.join());
- assertEquals(t.getCause().getClass(), IOException.class, "Expected an IOException but got " + t.getCause());
+ assertEquals(t.getCause().getClass(), ProtocolException.class,
+ "Expected a ProtocolException but got " + t.getCause());
System.err.println("Client received the following expected exception: " + t.getCause());
faultyServer.stop();
}
@@ -222,7 +224,10 @@ public class PushPromiseContinuation {
static class Http2PushPromiseHeadersExchangeImpl extends Http2TestExchangeImpl {
- Http2PushPromiseHeadersExchangeImpl(int streamid, String method, HttpHeaders reqheaders, HttpHeadersBuilder rspheadersBuilder, URI uri, InputStream is, SSLSession sslSession, BodyOutputStream os, Http2TestServerConnection conn, boolean pushAllowed) {
+ Http2PushPromiseHeadersExchangeImpl(int streamid, String method, HttpHeaders reqheaders,
+ HttpHeadersBuilder rspheadersBuilder, URI uri, InputStream is,
+ SSLSession sslSession, BodyOutputStream os,
+ Http2TestServerConnection conn, boolean pushAllowed) {
super(streamid, method, reqheaders, rspheadersBuilder, uri, is, sslSession, os, conn, pushAllowed);
}
diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java
index 36498684a9a..27dbe637b94 100644
--- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java
+++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -241,6 +241,7 @@ public interface HttpServerAdapters {
public abstract void close();
public abstract InetSocketAddress getRemoteAddress();
public abstract String getConnectionKey();
+ public abstract InetSocketAddress getLocalAddress();
public void serverPush(URI uri, HttpHeaders headers, byte[] body) {
ByteArrayInputStream bais = new ByteArrayInputStream(body);
serverPush(uri, headers, bais);
@@ -303,7 +304,10 @@ public interface HttpServerAdapters {
public InetSocketAddress getRemoteAddress() {
return exchange.getRemoteAddress();
}
-
+ @Override
+ public InetSocketAddress getLocalAddress() {
+ return exchange.getLocalAddress();
+ }
@Override
public URI getRequestURI() { return exchange.getRequestURI(); }
@Override
@@ -370,6 +374,10 @@ public interface HttpServerAdapters {
public InetSocketAddress getRemoteAddress() {
return exchange.getRemoteAddress();
}
+ @Override
+ public InetSocketAddress getLocalAddress() {
+ return exchange.getLocalAddress();
+ }
@Override
public String getConnectionKey() {
diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/HpackTestEncoder.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/HpackTestEncoder.java
new file mode 100644
index 00000000000..f54a4a766b8
--- /dev/null
+++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/HpackTestEncoder.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2015, 2023, 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.
+ */
+
+package jdk.httpclient.test.lib.http2;
+
+import java.util.function.*;
+
+import jdk.internal.net.http.hpack.Encoder;
+
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+import static jdk.internal.net.http.hpack.HPACK.Logger.Level.EXTRA;
+import static jdk.internal.net.http.hpack.HPACK.Logger.Level.NORMAL;
+
+public class HpackTestEncoder extends Encoder {
+
+ public HpackTestEncoder(int maxCapacity) {
+ super(maxCapacity);
+ }
+
+ /**
+ * Sets up the given header {@code (name, value)} with possibly sensitive
+ * value.
+ *
+ *
If the {@code value} is sensitive (think security, secrecy, etc.)
+ * this encoder will compress it using a special representation
+ * (see 6.2.3. Literal Header Field Never Indexed).
+ *
+ *
Fixates {@code name} and {@code value} for the duration of encoding.
+ *
+ * @param name
+ * the name
+ * @param value
+ * the value
+ * @param sensitive
+ * whether or not the value is sensitive
+ *
+ * @throws NullPointerException
+ * if any of the arguments are {@code null}
+ * @throws IllegalStateException
+ * if the encoder hasn't fully encoded the previous header, or
+ * hasn't yet started to encode it
+ * @see #header(CharSequence, CharSequence)
+ * @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean)
+ */
+ public void header(CharSequence name,
+ CharSequence value,
+ boolean sensitive) throws IllegalStateException {
+ if (sensitive || getMaxCapacity() == 0) {
+ super.header(name, value, true);
+ } else {
+ header(name, value, false, (n,v) -> false);
+ }
+ }
+ /**
+ * Sets up the given header {@code (name, value)} with possibly sensitive
+ * value.
+ *
+ *
If the {@code value} is sensitive (think security, secrecy, etc.)
+ * this encoder will compress it using a special representation
+ * (see 6.2.3. Literal Header Field Never Indexed).
+ *
+ *
Fixates {@code name} and {@code value} for the duration of encoding.
+ *
+ * @param name
+ * the name
+ * @param value
+ * the value
+ * @param insertionPolicy
+ * a bipredicate to indicate whether a name value pair
+ * should be added to the dynamic table
+ *
+ * @throws NullPointerException
+ * if any of the arguments are {@code null}
+ * @throws IllegalStateException
+ * if the encoder hasn't fully encoded the previous header, or
+ * hasn't yet started to encode it
+ * @see #header(CharSequence, CharSequence)
+ * @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean)
+ */
+ public void header(CharSequence name,
+ CharSequence value,
+ BiPredicate insertionPolicy)
+ throws IllegalStateException {
+ header(name, value, false, insertionPolicy);
+ }
+
+ /**
+ * Sets up the given header {@code (name, value)} with possibly sensitive
+ * value.
+ *
+ * If the {@code value} is sensitive (think security, secrecy, etc.)
+ * this encoder will compress it using a special representation
+ * (see
+ * 6.2.3. Literal Header Field Never Indexed).
+ *
+ *
Fixates {@code name} and {@code value} for the duration of encoding.
+ *
+ * @param name
+ * the name
+ * @param value
+ * the value
+ * @param sensitive
+ * whether or not the value is sensitive
+ * @param insertionPolicy
+ * a bipredicate to indicate whether a name value pair
+ * should be added to the dynamic table
+ *
+ * @throws NullPointerException
+ * if any of the arguments are {@code null}
+ * @throws IllegalStateException
+ * if the encoder hasn't fully encoded the previous header, or
+ * hasn't yet started to encode it
+ * @see #header(CharSequence, CharSequence)
+ * @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean)
+ */
+ public void header(CharSequence name,
+ CharSequence value,
+ boolean sensitive,
+ BiPredicate insertionPolicy)
+ throws IllegalStateException {
+ if (sensitive == true || getMaxCapacity() == 0 || !insertionPolicy.test(name, value)) {
+ super.header(name, value, sensitive);
+ return;
+ }
+ var logger = logger();
+ // Arguably a good balance between complexity of implementation and
+ // efficiency of encoding
+ requireNonNull(name, "name");
+ requireNonNull(value, "value");
+ var t = getHeaderTable();
+ int index = tableIndexOf(name, value);
+ if (logger.isLoggable(NORMAL)) {
+ logger.log(NORMAL, () -> format("encoding with indexing ('%s', '%s'): index:%s",
+ name, value, index));
+ }
+ if (index > 0) {
+ indexed(index);
+ } else {
+ boolean huffmanValue = isHuffmanBetterFor(value);
+ if (index < 0) {
+ literalWithIndexing(-index, value, huffmanValue);
+ } else {
+ boolean huffmanName = isHuffmanBetterFor(name);
+ literalWithIndexing(name, huffmanName, value, huffmanValue);
+ }
+ }
+ }
+
+ protected int calculateCapacity(int maxCapacity) {
+ return maxCapacity;
+ }
+
+}
diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java
index d982349dac5..828c939f53f 100644
--- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java
+++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java
@@ -29,9 +29,12 @@ import java.io.OutputStream;
import java.net.URI;
import java.net.InetSocketAddress;
import java.net.http.HttpHeaders;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
+import java.util.function.BiPredicate;
import javax.net.ssl.SSLSession;
import jdk.internal.net.http.common.HttpHeadersBuilder;
+import jdk.internal.net.http.frame.Http2Frame;
public interface Http2TestExchange {
@@ -53,6 +56,12 @@ public interface Http2TestExchange {
void sendResponseHeaders(int rCode, long responseLength) throws IOException;
+ default void sendResponseHeaders(int rCode, long responseLength,
+ BiPredicate insertionPolicy)
+ throws IOException {
+ sendResponseHeaders(rCode, responseLength);
+ }
+
InetSocketAddress getRemoteAddress();
int getResponseCode();
@@ -65,6 +74,10 @@ public interface Http2TestExchange {
void serverPush(URI uri, HttpHeaders headers, InputStream content);
+ default void sendFrames(List frames) throws IOException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
/**
* Send a PING on this exchanges connection, and completes the returned CF
* with the number of milliseconds it took to get a valid response.
diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java
index d25019f9094..fa7589c0232 100644
--- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java
+++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java
@@ -27,6 +27,7 @@ import jdk.httpclient.test.lib.http2.Http2TestServerConnection.ResponseHeaders;
import jdk.internal.net.http.common.HttpHeadersBuilder;
import jdk.internal.net.http.frame.HeaderFrame;
import jdk.internal.net.http.frame.HeadersFrame;
+import jdk.internal.net.http.frame.Http2Frame;
import jdk.internal.net.http.frame.ResetFrame;
import javax.net.ssl.SSLSession;
@@ -39,6 +40,7 @@ import java.net.http.HttpHeaders;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
+import java.util.function.BiPredicate;
public class Http2TestExchangeImpl implements Http2TestExchange {
@@ -132,8 +134,13 @@ public class Http2TestExchangeImpl implements Http2TestExchange {
return os;
}
- @Override
public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
+ sendResponseHeaders(rCode, responseLength, (n,v) -> false);
+ }
+ @Override
+ public void sendResponseHeaders(int rCode, long responseLength,
+ BiPredicate insertionPolicy)
+ throws IOException {
// Do not set Content-Length for 100, and do not set END_STREAM
if (rCode == 100) responseLength = 0;
@@ -147,7 +154,7 @@ public class Http2TestExchangeImpl implements Http2TestExchange {
HttpHeaders headers = rspheadersBuilder.build();
ResponseHeaders response
- = new ResponseHeaders(headers);
+ = new ResponseHeaders(headers, insertionPolicy);
response.streamid(streamid);
response.setFlag(HeaderFrame.END_HEADERS);
@@ -172,6 +179,11 @@ public class Http2TestExchangeImpl implements Http2TestExchange {
conn.outputQ.put(response);
}
+ @Override
+ public void sendFrames(List frames) throws IOException {
+ conn.sendFrames(frames);
+ }
+
@Override
public InetSocketAddress getRemoteAddress() {
return (InetSocketAddress) conn.socket.getRemoteSocketAddress();
diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java
index 1aeeee60b19..c98e986ca85 100644
--- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java
+++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java
@@ -24,6 +24,8 @@
package jdk.httpclient.test.lib.http2;
import jdk.internal.net.http.common.HttpHeadersBuilder;
+import jdk.internal.net.http.common.Log;
+import jdk.internal.net.http.frame.ContinuationFrame;
import jdk.internal.net.http.frame.DataFrame;
import jdk.internal.net.http.frame.ErrorFrame;
import jdk.internal.net.http.frame.FramesDecoder;
@@ -80,6 +82,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -87,6 +90,7 @@ import java.util.function.Predicate;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
import static jdk.internal.net.http.frame.ErrorFrame.REFUSED_STREAM;
+import static jdk.internal.net.http.frame.SettingsFrame.DEFAULT_MAX_FRAME_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE;
/**
@@ -105,7 +109,7 @@ public class Http2TestServerConnection {
final Http2TestExchangeSupplier exchangeSupplier;
final InputStream is;
final OutputStream os;
- volatile Encoder hpackOut;
+ volatile HpackTestEncoder hpackOut;
volatile Decoder hpackIn;
volatile SettingsFrame clientSettings;
final SettingsFrame serverSettings;
@@ -421,7 +425,9 @@ public class Http2TestServerConnection {
}
public int getMaxFrameSize() {
- return clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE);
+ var max = clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE);
+ if (max <= 0) max = DEFAULT_MAX_FRAME_SIZE;
+ return max;
}
/** Sends a pre-canned HTTP/1.1 response. */
@@ -482,7 +488,7 @@ public class Http2TestServerConnection {
//System.out.println("ServerSettings: " + serverSettings);
//System.out.println("ClientSettings: " + clientSettings);
- hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE));
+ hpackOut = new HpackTestEncoder(serverSettings.getParameter(HEADER_TABLE_SIZE));
hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE));
if (!secure) {
@@ -812,6 +818,14 @@ public class Http2TestServerConnection {
}
}
+ public void sendFrames(List frames) throws IOException {
+ synchronized (outputQ) {
+ for (var frame : frames) {
+ outputQ.put(frame);
+ }
+ }
+ }
+
protected HttpHeadersBuilder createNewHeadersBuilder() {
return new HttpHeadersBuilder();
}
@@ -938,26 +952,38 @@ public class Http2TestServerConnection {
return (streamid & 0x01) == 0x00;
}
+ final ReentrantLock headersLock = new ReentrantLock();
+
/** Encodes an group of headers, without any ordering guarantees. */
public List encodeHeaders(HttpHeaders headers) {
+ return encodeHeaders(headers, (n,v) -> false);
+ }
+
+ public List encodeHeaders(HttpHeaders headers,
+ BiPredicate insertionPolicy) {
List buffers = new LinkedList<>();
ByteBuffer buf = getBuffer();
boolean encoded;
- for (Map.Entry> entry : headers.map().entrySet()) {
- List values = entry.getValue();
- String key = entry.getKey().toLowerCase();
- for (String value : values) {
- do {
- hpackOut.header(key, value);
- encoded = hpackOut.encode(buf);
- if (!encoded) {
- buf.flip();
- buffers.add(buf);
- buf = getBuffer();
- }
- } while (!encoded);
+ headersLock.lock();
+ try {
+ for (Map.Entry> entry : headers.map().entrySet()) {
+ List values = entry.getValue();
+ String key = entry.getKey().toLowerCase();
+ for (String value : values) {
+ hpackOut.header(key, value, insertionPolicy);
+ do {
+ encoded = hpackOut.encode(buf);
+ if (!encoded && !buf.hasRemaining()) {
+ buf.flip();
+ buffers.add(buf);
+ buf = getBuffer();
+ }
+ } while (!encoded);
+ }
}
+ } finally {
+ headersLock.unlock();
}
buf.flip();
buffers.add(buf);
@@ -970,18 +996,23 @@ public class Http2TestServerConnection {
ByteBuffer buf = getBuffer();
boolean encoded;
- for (Map.Entry entry : headers) {
- String value = entry.getValue();
- String key = entry.getKey().toLowerCase();
- do {
+ headersLock.lock();
+ try {
+ for (Map.Entry entry : headers) {
+ String value = entry.getValue();
+ String key = entry.getKey().toLowerCase();
hpackOut.header(key, value);
- encoded = hpackOut.encode(buf);
- if (!encoded) {
- buf.flip();
- buffers.add(buf);
- buf = getBuffer();
- }
- } while (!encoded);
+ do {
+ encoded = hpackOut.encode(buf);
+ if (!encoded && !buf.hasRemaining()) {
+ buf.flip();
+ buffers.add(buf);
+ buf = getBuffer();
+ }
+ } while (!encoded);
+ }
+ } finally {
+ headersLock.unlock();
}
buf.flip();
buffers.add(buf);
@@ -1008,10 +1039,50 @@ public class Http2TestServerConnection {
break;
} else throw x;
}
- if (frame instanceof ResponseHeaders) {
- ResponseHeaders rh = (ResponseHeaders)frame;
- HeadersFrame hf = new HeadersFrame(rh.streamid(), rh.getFlags(), encodeHeaders(rh.headers));
- writeFrame(hf);
+ if (frame instanceof ResponseHeaders rh) {
+ var buffers = encodeHeaders(rh.headers, rh.insertionPolicy);
+ int maxFrameSize = Math.min(rh.getMaxFrameSize(), getMaxFrameSize() - 64);
+ int next = 0;
+ int cont = 0;
+ do {
+ // If the total size of headers exceeds the max frame
+ // size we need to split the headers into one
+ // HeadersFrame + N x ContinuationFrames
+ int remaining = maxFrameSize;
+ var list = new ArrayList(buffers.size());
+ for (; next < buffers.size(); next++) {
+ var b = buffers.get(next);
+ var len = b.remaining();
+ if (!b.hasRemaining()) continue;
+ if (len <= remaining) {
+ remaining -= len;
+ list.add(b);
+ } else {
+ if (next == 0) {
+ list.add(b.slice(b.position(), remaining));
+ b.position(b.position() + remaining);
+ remaining = 0;
+ }
+ break;
+ }
+ }
+ int flags = rh.getFlags();
+ if (next != buffers.size()) {
+ flags = flags & ~HeadersFrame.END_HEADERS;
+ }
+ if (cont > 0) {
+ flags = flags & ~HeadersFrame.END_STREAM;
+ }
+ HeaderFrame hf = cont == 0
+ ? new HeadersFrame(rh.streamid(), flags, list)
+ : new ContinuationFrame(rh.streamid(), flags, list);
+ if (Log.headers()) {
+ // avoid too much chatter: log only if Log.headers() is enabled
+ System.err.println("TestServer writing " + hf);
+ }
+ writeFrame(hf);
+ cont++;
+ } while (next < buffers.size());
} else if (frame instanceof OutgoingPushPromise) {
handlePush((OutgoingPushPromise)frame);
} else
@@ -1322,11 +1393,29 @@ public class Http2TestServerConnection {
// for the hashmap.
public static class ResponseHeaders extends Http2Frame {
- HttpHeaders headers;
+ final HttpHeaders headers;
+ final BiPredicate insertionPolicy;
+
+ final int maxFrameSize;
public ResponseHeaders(HttpHeaders headers) {
+ this(headers, (n,v) -> false);
+ }
+ public ResponseHeaders(HttpHeaders headers, BiPredicate insertionPolicy) {
+ this(headers, insertionPolicy, Integer.MAX_VALUE);
+ }
+
+ public ResponseHeaders(HttpHeaders headers,
+ BiPredicate insertionPolicy,
+ int maxFrameSize) {
super(0, 0);
this.headers = headers;
+ this.insertionPolicy = insertionPolicy;
+ this.maxFrameSize = maxFrameSize;
+ }
+
+ public int getMaxFrameSize() {
+ return maxFrameSize;
}
}
From cfa25b71a65bfff1b31efe0d37ded37c50a98247 Mon Sep 17 00:00:00 2001
From: Emanuel Peter
Date: Tue, 21 May 2024 10:31:14 +0000
Subject: [PATCH 4/9] 8328544: Improve handling of vectorization
Co-authored-by: Christian Hagedorn
Reviewed-by: mschoene, kvn, chagedorn, rhalade
---
src/hotspot/share/opto/vectorization.cpp | 472 +++++++++++++++++-
src/hotspot/share/opto/vectorization.hpp | 89 +++-
.../TestVectorizationMismatchedAccess.java | 337 +++++++++++--
.../loopopts/superword/TestAlignVector.java | 8 +-
.../superword/TestAlignVectorFuzzer.java | 20 +-
...tIndependentPacksWithCyclicDependency.java | 140 +++---
...IndependentPacksWithCyclicDependency2.java | 28 +-
.../TestScheduleReordersScalarMemops.java | 8 +-
8 files changed, 936 insertions(+), 166 deletions(-)
diff --git a/src/hotspot/share/opto/vectorization.cpp b/src/hotspot/share/opto/vectorization.cpp
index 8d2d3868fe6..f013abb38fb 100644
--- a/src/hotspot/share/opto/vectorization.cpp
+++ b/src/hotspot/share/opto/vectorization.cpp
@@ -416,6 +416,10 @@ VPointer::VPointer(MemNode* const mem, const VLoop& vloop,
#ifdef ASSERT
_debug_invar(nullptr), _debug_negate_invar(false), _debug_invar_scale(nullptr),
#endif
+ _has_int_index_after_convI2L(false),
+ _int_index_after_convI2L_offset(0),
+ _int_index_after_convI2L_invar(nullptr),
+ _int_index_after_convI2L_scale(0),
_nstack(nstack), _analyze_only(analyze_only), _stack_idx(0)
#ifndef PRODUCT
, _tracer(vloop.is_trace_pointer_analysis())
@@ -495,6 +499,11 @@ VPointer::VPointer(MemNode* const mem, const VLoop& vloop,
return;
}
+ if (!is_safe_to_use_as_simple_form(base, adr)) {
+ assert(!valid(), "does not have simple form");
+ return;
+ }
+
_base = base;
_adr = adr;
assert(valid(), "Usable");
@@ -508,6 +517,10 @@ VPointer::VPointer(VPointer* p) :
#ifdef ASSERT
_debug_invar(nullptr), _debug_negate_invar(false), _debug_invar_scale(nullptr),
#endif
+ _has_int_index_after_convI2L(false),
+ _int_index_after_convI2L_offset(0),
+ _int_index_after_convI2L_invar(nullptr),
+ _int_index_after_convI2L_scale(0),
_nstack(p->_nstack), _analyze_only(p->_analyze_only), _stack_idx(p->_stack_idx)
#ifndef PRODUCT
, _tracer(p->_tracer._is_trace_alignment)
@@ -530,6 +543,354 @@ int VPointer::invar_factor() const {
return 1;
}
+// We would like to make decisions about aliasing (i.e. removing memory edges) and adjacency
+// (i.e. which loads/stores can be packed) based on the simple form:
+//
+// s_pointer = adr + offset + invar + scale * ConvI2L(iv)
+//
+// However, we parse the compound-long-int form:
+//
+// c_pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_index)
+// int_index = int_offset + int_invar + int_scale * iv
+//
+// In general, the simple and the compound-long-int form do not always compute the same pointer
+// at runtime. For example, the simple form would give a different result due to an overflow
+// in the int_index.
+//
+// Example:
+// For both forms, we have:
+// iv = 0
+// scale = 1
+//
+// We now account the offset and invar once to the long part and once to the int part:
+// Pointer 1 (long offset and long invar):
+// long_offset = min_int
+// long_invar = min_int
+// int_offset = 0
+// int_invar = 0
+//
+// Pointer 2 (int offset and int invar):
+// long_offset = 0
+// long_invar = 0
+// int_offset = min_int
+// int_invar = min_int
+//
+// This gives us the following pointers:
+// Compound-long-int form pointers:
+// Form:
+// c_pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_offset + int_invar + int_scale * iv)
+//
+// Pointers:
+// c_pointer1 = adr + min_int + min_int + 1 * ConvI2L(0 + 0 + 1 * 0)
+// = adr + min_int + min_int
+// = adr - 2^32
+//
+// c_pointer2 = adr + 0 + 0 + 1 * ConvI2L(min_int + min_int + 1 * 0)
+// = adr + ConvI2L(min_int + min_int)
+// = adr + 0
+// = adr
+//
+// Simple form pointers:
+// Form:
+// s_pointer = adr + offset + invar + scale * ConvI2L(iv)
+// s_pointer = adr + (long_offset + int_offset) + (long_invar + int_invar) + (long_scale * int_scale) * ConvI2L(iv)
+//
+// Pointers:
+// s_pointer1 = adr + (min_int + 0 ) + (min_int + 0 ) + 1 * 0
+// = adr + min_int + min_int
+// = adr - 2^32
+// s_pointer2 = adr + (0 + min_int ) + (0 + min_int ) + 1 * 0
+// = adr + min_int + min_int
+// = adr - 2^32
+//
+// We see that the two addresses are actually 2^32 bytes apart (derived from the c_pointers), but their simple form look identical.
+//
+// Hence, we need to determine in which cases it is safe to make decisions based on the simple
+// form, rather than the compound-long-int form. If we cannot prove that using the simple form
+// is safe (i.e. equivalent to the compound-long-int form), then we do not get a valid VPointer,
+// and the associated memop cannot be vectorized.
+bool VPointer::is_safe_to_use_as_simple_form(Node* base, Node* adr) const {
+#ifndef _LP64
+ // On 32-bit platforms, there is never an explicit int_index with ConvI2L for the iv. Thus, the
+ // parsed pointer form is always the simple form, with int operations:
+ //
+ // pointer = adr + offset + invar + scale * iv
+ //
+ assert(!_has_int_index_after_convI2L, "32-bit never has an int_index with ConvI2L for the iv");
+ return true;
+#else
+
+ // Array accesses that are not Unsafe always have a RangeCheck which ensures that there is no
+ // int_index overflow. This implies that the conversion to long can be done separately:
+ //
+ // ConvI2L(int_index) = ConvI2L(int_offset) + ConvI2L(int_invar) + ConvI2L(scale) * ConvI2L(iv)
+ //
+ // And hence, the simple form is guaranteed to be identical to the compound-long-int form at
+ // runtime and the VPointer is safe/valid to be used.
+ const TypeAryPtr* ary_ptr_t = _mem->adr_type()->isa_aryptr();
+ if (ary_ptr_t != nullptr) {
+ if (!_mem->is_unsafe_access()) {
+ return true;
+ }
+ }
+
+ // We did not find the int_index. Just to be safe, reject this VPointer.
+ if (!_has_int_index_after_convI2L) {
+ return false;
+ }
+
+ int int_offset = _int_index_after_convI2L_offset;
+ Node* int_invar = _int_index_after_convI2L_invar;
+ int int_scale = _int_index_after_convI2L_scale;
+ int long_scale = _scale / int_scale;
+
+ // If "int_index = iv", then the simple form is identical to the compound-long-int form.
+ //
+ // int_index = int_offset + int_invar + int_scale * iv
+ // = 0 0 1 * iv
+ // = iv
+ if (int_offset == 0 && int_invar == nullptr && int_scale == 1) {
+ return true;
+ }
+
+ // Intuition: What happens if the int_index overflows? Let us look at two pointers on the "overflow edge":
+ //
+ // pointer1 = adr + ConvI2L(int_index1)
+ // pointer2 = adr + ConvI2L(int_index2)
+ //
+ // int_index1 = max_int + 0 = max_int -> very close to but before the overflow
+ // int_index2 = max_int + 1 = min_int -> just enough to get the overflow
+ //
+ // When looking at the difference of pointer1 and pointer2, we notice that it is very large
+ // (almost 2^32). Since arrays have at most 2^31 elements, chances are high that pointer2 is
+ // an actual out-of-bounds access at runtime. These would normally be prevented by range checks
+ // at runtime. However, if the access was done by using Unsafe, where range checks are omitted,
+ // then an out-of-bounds access constitutes undefined behavior. This means that we are allowed to
+ // do anything, including changing the behavior.
+ //
+ // If we can set the right conditions, we have a guarantee that an overflow is either impossible
+ // (no overflow or range checks preventing that) or undefined behavior. In both cases, we are
+ // safe to do a vectorization.
+ //
+ // Approach: We want to prove a lower bound for the distance between these two pointers, and an
+ // upper bound for the size of a memory object. We can derive such an upper bound for
+ // arrays. We know they have at most 2^31 elements. If we know the size of the elements
+ // in bytes, we have:
+ //
+ // array_element_size_in_bytes * 2^31 >= max_possible_array_size_in_bytes
+ // >= array_size_in_bytes (ARR)
+ //
+ // If some small difference "delta" leads to an int_index overflow, we know that the
+ // int_index1 before overflow must have been close to max_int, and the int_index2 after
+ // the overflow must be close to min_int:
+ //
+ // pointer1 = adr + long_offset + long_invar + long_scale * ConvI2L(int_index1)
+ // =approx adr + long_offset + long_invar + long_scale * max_int
+ //
+ // pointer2 = adr + long_offset + long_invar + long_scale * ConvI2L(int_index2)
+ // =approx adr + long_offset + long_invar + long_scale * min_int
+ //
+ // We realize that the pointer difference is very large:
+ //
+ // difference =approx long_scale * 2^32
+ //
+ // Hence, if we set the right condition for long_scale and array_element_size_in_bytes,
+ // we can prove that an overflow is impossible (or would imply undefined behaviour).
+ //
+ // We must now take this intuition, and develop a rigorous proof. We start by stating the problem
+ // more precisely, with the help of some definitions and the Statement we are going to prove.
+ //
+ // Definition:
+ // Two VPointers are "comparable" (i.e. VPointer::comparable is true, set with VPointer::cmp()),
+ // iff all of these conditions apply for the simple form:
+ // 1) Both VPointers are valid.
+ // 2) The adr are identical, or both are array bases of different arrays.
+ // 3) They have identical scale.
+ // 4) They have identical invar.
+ // 5) The difference in offsets is limited: abs(offset1 - offset2) < 2^31. (DIFF)
+ //
+ // For the Vectorization Optimization, we pair-wise compare VPointers and determine if they are:
+ // 1) "not comparable":
+ // We do not optimize them (assume they alias, not assume adjacency).
+ //
+ // Whenever we chose this option based on the simple form, it is also correct based on the
+ // compound-long-int form, since we make no optimizations based on it.
+ //
+ // 2) "comparable" with different array bases at runtime:
+ // We assume they do not alias (remove memory edges), but not assume adjacency.
+ //
+ // Whenever we have two different array bases for the simple form, we also have different
+ // array bases for the compound-long-form. Since VPointers provably point to different
+ // memory objects, they can never alias.
+ //
+ // 3) "comparable" with the same base address:
+ // We compute the relative pointer difference, and based on the load/store size we can
+ // compute aliasing and adjacency.
+ //
+ // We must find a condition under which the pointer difference of the simple form is
+ // identical to the pointer difference of the compound-long-form. We do this with the
+ // Statement below, which we then proceed to prove.
+ //
+ // Statement:
+ // If two VPointers satisfy these 3 conditions:
+ // 1) They are "comparable".
+ // 2) They have the same base address.
+ // 3) Their long_scale is a multiple of the array element size in bytes:
+ //
+ // abs(long_scale) % array_element_size_in_bytes = 0 (A)
+ //
+ // Then their pointer difference of the simple form is identical to the pointer difference
+ // of the compound-long-int form.
+ //
+ // More precisely:
+ // Such two VPointers by definition have identical adr, invar, and scale.
+ // Their simple form is:
+ //
+ // s_pointer1 = adr + offset1 + invar + scale * ConvI2L(iv) (B1)
+ // s_pointer2 = adr + offset2 + invar + scale * ConvI2L(iv) (B2)
+ //
+ // Thus, the pointer difference of the simple forms collapses to the difference in offsets:
+ //
+ // s_difference = s_pointer1 - s_pointer2 = offset1 - offset2 (C)
+ //
+ // Their compound-long-int form for these VPointer is:
+ //
+ // c_pointer1 = adr + long_offset1 + long_invar1 + long_scale1 * ConvI2L(int_index1) (D1)
+ // int_index1 = int_offset1 + int_invar1 + int_scale1 * iv (D2)
+ //
+ // c_pointer2 = adr + long_offset2 + long_invar2 + long_scale2 * ConvI2L(int_index2) (D3)
+ // int_index2 = int_offset2 + int_invar2 + int_scale2 * iv (D4)
+ //
+ // And these are the offset1, offset2, invar and scale from the simple form (B1) and (B2):
+ //
+ // offset1 = long_offset1 + long_scale1 * ConvI2L(int_offset1) (D5)
+ // offset2 = long_offset2 + long_scale2 * ConvI2L(int_offset2) (D6)
+ //
+ // invar = long_invar1 + long_scale1 * ConvI2L(int_invar1)
+ // = long_invar2 + long_scale2 * ConvI2L(int_invar2) (D7)
+ //
+ // scale = long_scale1 * ConvI2L(int_scale1)
+ // = long_scale2 * ConvI2L(int_scale2) (D8)
+ //
+ // The pointer difference of the compound-long-int form is defined as:
+ //
+ // c_difference = c_pointer1 - c_pointer2
+ //
+ // Thus, the statement claims that for the two VPointer we have:
+ //
+ // s_difference = c_difference (Statement)
+ //
+ // We prove the Statement with the help of a Lemma:
+ //
+ // Lemma:
+ // There is some integer x, such that:
+ //
+ // c_difference = s_difference + array_element_size_in_bytes * x * 2^32 (Lemma)
+ //
+ // From condition (DIFF), we can derive:
+ //
+ // abs(s_difference) < 2^31 (E)
+ //
+ // Assuming the Lemma, we prove the Statement:
+ // If "x = 0" (intuitively: the int_index does not overflow), then:
+ // c_difference = s_difference
+ // and hence the simple form computes the same pointer difference as the compound-long-int form.
+ // If "x != 0" (intuitively: the int_index overflows), then:
+ // abs(c_difference) >= abs(s_difference + array_element_size_in_bytes * x * 2^32)
+ // >= array_element_size_in_bytes * 2^32 - abs(s_difference)
+ // -- apply (E) --
+ // > array_element_size_in_bytes * 2^32 - 2^31
+ // >= array_element_size_in_bytes * 2^31
+ // -- apply (ARR) --
+ // >= max_possible_array_size_in_bytes
+ // >= array_size_in_bytes
+ //
+ // This shows that c_pointer1 and c_pointer2 have a distance that exceeds the maximum array size.
+ // Thus, at least one of the two pointers must be outside of the array bounds. But we can assume
+ // that out-of-bounds accesses do not happen. If they still do, it is undefined behavior. Hence,
+ // we are allowed to do anything. We can also "safely" use the simple form in this case even though
+ // it might not match the compound-long-int form at runtime.
+ // QED Statement.
+ //
+ // We must now prove the Lemma.
+ //
+ // ConvI2L always truncates by some power of 2^32, i.e. there is some integer y such that:
+ //
+ // ConvI2L(y1 + y2) = ConvI2L(y1) + ConvI2L(y2) + 2^32 * y (F)
+ //
+ // It follows, that there is an integer y1 such that:
+ //
+ // ConvI2L(int_index1) = ConvI2L(int_offset1 + int_invar1 + int_scale1 * iv)
+ // -- apply (F) --
+ // = ConvI2L(int_offset1)
+ // + ConvI2L(int_invar1)
+ // + ConvI2L(int_scale1) * ConvI2L(iv)
+ // + y1 * 2^32 (G)
+ //
+ // Thus, we can write the compound-long-int form (D1) as:
+ //
+ // c_pointer1 = adr + long_offset1 + long_invar1 + long_scale1 * ConvI2L(int_index1)
+ // -- apply (G) --
+ // = adr
+ // + long_offset1
+ // + long_invar1
+ // + long_scale1 * ConvI2L(int_offset1)
+ // + long_scale1 * ConvI2L(int_invar1)
+ // + long_scale1 * ConvI2L(int_scale1) * ConvI2L(iv)
+ // + long_scale1 * y1 * 2^32 (H)
+ //
+ // And we can write the simple form as:
+ //
+ // s_pointer1 = adr + offset1 + invar + scale * ConvI2L(iv)
+ // -- apply (D5, D7, D8) --
+ // = adr
+ // + long_offset1
+ // + long_scale1 * ConvI2L(int_offset1)
+ // + long_invar1
+ // + long_scale1 * ConvI2L(int_invar1)
+ // + long_scale1 * ConvI2L(int_scale1) * ConvI2L(iv) (K)
+ //
+ // We now compute the pointer difference between the simple (K) and compound-long-int form (H).
+ // Most terms cancel out immediately:
+ //
+ // sc_difference1 = c_pointer1 - s_pointer1 = long_scale1 * y1 * 2^32 (L)
+ //
+ // Rearranging the equation (L), we get:
+ //
+ // c_pointer1 = s_pointer1 + long_scale1 * y1 * 2^32 (M)
+ //
+ // And since long_scale1 is a multiple of array_element_size_in_bytes, there is some integer
+ // x1, such that (M) implies:
+ //
+ // c_pointer1 = s_pointer1 + array_element_size_in_bytes * x1 * 2^32 (N)
+ //
+ // With an analogue equation for c_pointer2, we can now compute the pointer difference for
+ // the compound-long-int form:
+ //
+ // c_difference = c_pointer1 - c_pointer2
+ // -- apply (N) --
+ // = s_pointer1 + array_element_size_in_bytes * x1 * 2^32
+ // -(s_pointer2 + array_element_size_in_bytes * x2 * 2^32)
+ // -- where "x = x1 - x2" --
+ // = s_pointer1 - s_pointer2 + array_element_size_in_bytes * x * 2^32
+ // -- apply (C) --
+ // = s_difference + array_element_size_in_bytes * x * 2^32
+ // QED Lemma.
+ if (ary_ptr_t != nullptr) {
+ BasicType array_element_bt = ary_ptr_t->elem()->array_element_basic_type();
+ if (is_java_primitive(array_element_bt)) {
+ int array_element_size_in_bytes = type2aelembytes(array_element_bt);
+ if (abs(long_scale) % array_element_size_in_bytes == 0) {
+ return true;
+ }
+ }
+ }
+
+ // General case: we do not know if it is safe to use the simple form.
+ return false;
+#endif
+}
+
bool VPointer::is_loop_member(Node* n) const {
Node* n_c = phase()->get_ctrl(n);
return lpt()->is_member(phase()->get_loop(n_c));
@@ -632,6 +993,37 @@ bool VPointer::scaled_iv(Node* n) {
NOT_PRODUCT(_tracer.scaled_iv_6(n, _scale);)
return true;
}
+ } else if (opc == Op_ConvI2L && !has_iv()) {
+ // So far we have not found the iv yet, and are about to enter a ConvI2L subgraph,
+ // which may be the int index (that might overflow) for the memory access, of the form:
+ //
+ // int_index = int_offset + int_invar + int_scale * iv
+ //
+ // If we simply continue parsing with the current VPointer, then the int_offset and
+ // int_invar simply get added to the long offset and invar. But for the checks in
+ // VPointer::is_safe_to_use_as_simple_form() we need to have explicit access to the
+ // int_index. Thus, we must parse it explicitly here. For this, we use a temporary
+ // VPointer, to pattern match the int_index sub-expression of the address.
+
+ NOT_PRODUCT(Tracer::Depth dddd;)
+ VPointer tmp(this);
+ NOT_PRODUCT(_tracer.scaled_iv_8(n, &tmp);)
+
+ 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
+ _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;
+ }
} else if (opc == Op_ConvI2L || opc == Op_CastII) {
if (scaled_iv_plus_offset(n->in(1))) {
NOT_PRODUCT(_tracer.scaled_iv_7(n);)
@@ -648,8 +1040,17 @@ bool VPointer::scaled_iv(Node* n) {
if (tmp.scaled_iv_plus_offset(n->in(1))) {
int scale = n->in(2)->get_int();
+ // Accumulate scale.
_scale = tmp._scale << scale;
- _offset += tmp._offset << scale;
+ // Accumulate offset.
+ int shifted_offset = 0;
+ if (!try_LShiftI_no_overflow(tmp._offset, scale, shifted_offset)) {
+ return false; // shift overflow.
+ }
+ if (!try_AddI_no_overflow(_offset, shifted_offset, _offset)) {
+ return false; // add overflow.
+ }
+ // Accumulate invar.
if (tmp._invar != nullptr) {
BasicType bt = tmp._invar->bottom_type()->basic_type();
assert(bt == T_INT || bt == T_LONG, "");
@@ -658,6 +1059,13 @@ bool VPointer::scaled_iv(Node* n) {
_debug_invar_scale = n->in(2);
#endif
}
+
+ // Forward info about the int_index:
+ _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_9(n, _scale, _offset, _invar);)
return true;
}
@@ -675,7 +1083,9 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
int opc = n->Opcode();
if (opc == Op_ConI) {
- _offset += negate ? -(n->get_int()) : n->get_int();
+ if (!try_AddSubI_no_overflow(_offset, n->get_int(), negate, _offset)) {
+ return false; // add/sub overflow.
+ }
NOT_PRODUCT(_tracer.offset_plus_k_2(n, _offset);)
return true;
} else if (opc == Op_ConL) {
@@ -684,7 +1094,9 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
if (t->higher_equal(TypeLong::INT)) {
jlong loff = n->get_long();
jint off = (jint)loff;
- _offset += negate ? -off : loff;
+ if (!try_AddSubI_no_overflow(_offset, off, negate, _offset)) {
+ return false; // add/sub overflow.
+ }
NOT_PRODUCT(_tracer.offset_plus_k_3(n, _offset);)
return true;
}
@@ -699,11 +1111,15 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
if (opc == Op_AddI) {
if (n->in(2)->is_Con() && invariant(n->in(1))) {
maybe_add_to_invar(n->in(1), negate);
- _offset += negate ? -(n->in(2)->get_int()) : n->in(2)->get_int();
+ if (!try_AddSubI_no_overflow(_offset, n->in(2)->get_int(), negate, _offset)) {
+ return false; // add/sub overflow.
+ }
NOT_PRODUCT(_tracer.offset_plus_k_6(n, _invar, negate, _offset);)
return true;
} else if (n->in(1)->is_Con() && invariant(n->in(2))) {
- _offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int();
+ if (!try_AddSubI_no_overflow(_offset, n->in(1)->get_int(), negate, _offset)) {
+ return false; // add/sub overflow.
+ }
maybe_add_to_invar(n->in(2), negate);
NOT_PRODUCT(_tracer.offset_plus_k_7(n, _invar, negate, _offset);)
return true;
@@ -712,11 +1128,15 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
if (opc == Op_SubI) {
if (n->in(2)->is_Con() && invariant(n->in(1))) {
maybe_add_to_invar(n->in(1), negate);
- _offset += !negate ? -(n->in(2)->get_int()) : n->in(2)->get_int();
+ if (!try_AddSubI_no_overflow(_offset, n->in(2)->get_int(), !negate, _offset)) {
+ return false; // add/sub overflow.
+ }
NOT_PRODUCT(_tracer.offset_plus_k_8(n, _invar, negate, _offset);)
return true;
} else if (n->in(1)->is_Con() && invariant(n->in(2))) {
- _offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int();
+ if (!try_AddSubI_no_overflow(_offset, n->in(1)->get_int(), negate, _offset)) {
+ return false; // add/sub overflow.
+ }
maybe_add_to_invar(n->in(2), !negate);
NOT_PRODUCT(_tracer.offset_plus_k_9(n, _invar, !negate, _offset);)
return true;
@@ -806,6 +1226,44 @@ void VPointer::maybe_add_to_invar(Node* new_invar, bool negate) {
_invar = register_if_new(add);
}
+bool VPointer::try_AddI_no_overflow(int offset1, int offset2, int& result) {
+ jlong long_offset = java_add((jlong)(offset1), (jlong)(offset2));
+ jint int_offset = java_add( offset1, offset2);
+ if (long_offset != int_offset) {
+ return false;
+ }
+ result = int_offset;
+ return true;
+}
+
+bool VPointer::try_SubI_no_overflow(int offset1, int offset2, int& result) {
+ jlong long_offset = java_subtract((jlong)(offset1), (jlong)(offset2));
+ jint int_offset = java_subtract( offset1, offset2);
+ if (long_offset != int_offset) {
+ return false;
+ }
+ result = int_offset;
+ return true;
+}
+
+bool VPointer::try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result) {
+ if (is_sub) {
+ return try_SubI_no_overflow(offset1, offset2, result);
+ } else {
+ return try_AddI_no_overflow(offset1, offset2, result);
+ }
+}
+
+bool VPointer::try_LShiftI_no_overflow(int offset, int shift, int& result) {
+ jlong long_offset = java_shift_left((jlong)(offset), shift);
+ jint int_offset = java_shift_left( offset, shift);
+ if (long_offset != int_offset) {
+ return false;
+ }
+ result = int_offset;
+ return true;
+}
+
// We use two comparisons, because a subtraction could underflow.
#define RETURN_CMP_VALUE_IF_NOT_EQUAL(a, b) \
if (a < b) { return -1; } \
diff --git a/src/hotspot/share/opto/vectorization.hpp b/src/hotspot/share/opto/vectorization.hpp
index 3984407c565..7de29444e5f 100644
--- a/src/hotspot/share/opto/vectorization.hpp
+++ b/src/hotspot/share/opto/vectorization.hpp
@@ -670,13 +670,51 @@ private:
// A vectorization pointer (VPointer) has information about an address for
// dependence checking and vector alignment. It's usually bound to a memory
// operation in a counted loop for vectorizable analysis.
+//
+// We parse and represent pointers of the simple form:
+//
+// pointer = adr + offset + invar + scale * ConvI2L(iv)
+//
+// Where:
+//
+// adr: the base address of an array (base = adr)
+// OR
+// some address to off-heap memory (base = TOP)
+//
+// offset: a constant offset
+// invar: a runtime variable, which is invariant during the loop
+// scale: scaling factor
+// iv: loop induction variable
+//
+// But more precisely, we parse the composite-long-int form:
+//
+// pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_offset + inv_invar + int_scale * iv)
+//
+// pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_index)
+// int_index = int_offset + int_invar + int_scale * iv
+//
+// However, for aliasing and adjacency checks (e.g. VPointer::cmp()) we always use the simple form to make
+// decisions. Hence, we must make sure to only create a "valid" VPointer if the optimisations based on the
+// simple form produce the same result as the compound-long-int form would. Intuitively, this depends on
+// if the int_index overflows, but the precise conditions are given in VPointer::is_safe_to_use_as_simple_form().
+//
+// ConvI2L(int_index) = ConvI2L(int_offset + int_invar + int_scale * iv)
+// = Convi2L(int_offset) + ConvI2L(int_invar) + ConvI2L(int_scale) * ConvI2L(iv)
+//
+// scale = long_scale * ConvI2L(int_scale)
+// offset = long_offset + long_scale * ConvI2L(int_offset)
+// invar = long_invar + long_scale * ConvI2L(int_invar)
+//
+// pointer = adr + offset + invar + scale * ConvI2L(iv)
+//
class VPointer : public ArenaObj {
protected:
MemNode* const _mem; // My memory reference node
const VLoop& _vloop;
- Node* _base; // null if unsafe nonheap reference
- Node* _adr; // address pointer
+ // Components of the simple form:
+ Node* _base; // Base address of an array OR null if some off-heap memory.
+ Node* _adr; // Same as _base if an array pointer OR some off-heap memory pointer.
int _scale; // multiplier for iv (in bytes), 0 if no loop iv
int _offset; // constant offset (in bytes)
@@ -687,6 +725,13 @@ class VPointer : public ArenaObj {
Node* _debug_invar_scale; // multiplier for invariant
#endif
+ // The int_index components of the compound-long-int form. Used to decide if it is safe to use the
+ // simple form rather than the compound-long-int form that was parsed.
+ bool _has_int_index_after_convI2L;
+ int _int_index_after_convI2L_offset;
+ Node* _int_index_after_convI2L_invar;
+ int _int_index_after_convI2L_scale;
+
Node_Stack* _nstack; // stack used to record a vpointer trace of variants
bool _analyze_only; // Used in loop unrolling only for vpointer trace
uint _stack_idx; // Used in loop unrolling only for vpointer trace
@@ -726,6 +771,8 @@ class VPointer : public ArenaObj {
VPointer(VPointer* p);
NONCOPYABLE(VPointer);
+ bool is_safe_to_use_as_simple_form(Node* base, Node* adr) const;
+
public:
bool valid() const { return _adr != nullptr; }
bool has_iv() const { return _scale != 0; }
@@ -751,10 +798,43 @@ class VPointer : public ArenaObj {
return _invar == q._invar;
}
+ // We compute if and how two VPointers can alias at runtime, i.e. if the two addressed regions of memory can
+ // ever overlap. There are essentially 3 relevant return states:
+ // - NotComparable: Synonymous to "unknown aliasing".
+ // We have no information about how the two VPointers can alias. They could overlap, refer
+ // to another location in the same memory object, or point to a completely different object.
+ // -> Memory edge required. Aliasing unlikely but possible.
+ //
+ // - Less / Greater: Synonymous to "never aliasing".
+ // The two VPointers may point into the same memory object, but be non-aliasing (i.e. we
+ // know both address regions inside the same memory object, but these regions are non-
+ // overlapping), or the VPointers point to entirely different objects.
+ // -> No memory edge required. Aliasing impossible.
+ //
+ // - Equal: Synonymous to "overlap, or point to different memory objects".
+ // The two VPointers either overlap on the same memory object, or point to two different
+ // memory objects.
+ // -> Memory edge required. Aliasing likely.
+ //
+ // In a future refactoring, we can simplify to two states:
+ // - NeverAlias: instead of Less / Greater
+ // - MayAlias: instead of Equal / NotComparable
+ //
+ // Two VPointer are "comparable" (Less / Greater / Equal), iff all of these conditions apply:
+ // 1) Both are valid, i.e. expressible in the compound-long-int or simple form.
+ // 2) The adr are identical, or both are array bases of different arrays.
+ // 3) They have identical scale.
+ // 4) They have identical invar.
+ // 5) The difference in offsets is limited: abs(offset0 - offset1) < 2^31.
int cmp(const VPointer& q) const {
if (valid() && q.valid() &&
(_adr == q._adr || (_base == _adr && q._base == q._adr)) &&
_scale == q._scale && invar_equals(q)) {
+ jlong difference = abs(java_subtract((jlong)_offset, (jlong)q._offset));
+ jlong max_diff = (jlong)1 << 31;
+ if (difference >= max_diff) {
+ return NotComparable;
+ }
bool overlap = q._offset < _offset + memory_size() &&
_offset < q._offset + q.memory_size();
return overlap ? Equal : (_offset < q._offset ? Less : Greater);
@@ -859,6 +939,11 @@ class VPointer : public ArenaObj {
void maybe_add_to_invar(Node* new_invar, bool negate);
+ static bool try_AddI_no_overflow(int offset1, int offset2, int& result);
+ 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);
+
Node* register_if_new(Node* n) const;
};
diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java
index b68ddfe2799..2fdbb0816ad 100644
--- a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2023, Red Hat, Inc. All rights reserved.
+ * 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
@@ -35,7 +36,6 @@ import java.nio.ByteOrder;
* @test
* @bug 8300258
* @key randomness
- * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64")
* @summary C2: vectorization fails on simple ByteBuffer loop
* @modules java.base/jdk.internal.misc
* @library /test/lib /
@@ -147,193 +147,420 @@ public class TestVectorizationMismatchedAccess {
}
@Test
- @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
- public static void testByteLong1(byte[] dest, long[] src) {
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
+ // 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]);
}
}
- @Run(test = "testByteLong1")
- public static void testByteLong1_runner() {
- runAndVerify(() -> testByteLong1(byteArray, longArray), 0);
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 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]);
+ }
}
@Test
- @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
- public static void testByteLong2(byte[] dest, long[] src) {
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"})
+ 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]);
+ }
+ }
+
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 32-bit: address has ConvL2I for cast of long to address, not supported.
+ 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]);
+ }
+ }
+
+ @Run(test = {"testByteLong1a", "testByteLong1b", "testByteLong1c", "testByteLong1d"})
+ public static void testByteLong1_runner() {
+ runAndVerify(() -> testByteLong1a(byteArray, longArray), 0);
+ runAndVerify(() -> testByteLong1b(byteArray, longArray), 0);
+ testByteLong1c(byteArray, longArray);
+ testByteLong1d(byteArray, longArray);
+ }
+
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
+ // 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]);
}
}
- @Run(test = "testByteLong2")
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 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]);
+ }
+ }
+
+ @Run(test = {"testByteLong2a", "testByteLong2b"})
public static void testByteLong2_runner() {
- runAndVerify(() -> testByteLong2(byteArray, longArray), -8);
+ runAndVerify(() -> testByteLong2a(byteArray, longArray), -8);
+ runAndVerify(() -> testByteLong2b(byteArray, longArray), -8);
}
@Test
- @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
- public static void testByteLong3(byte[] dest, long[] src) {
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
+ // 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]);
}
}
- @Run(test = "testByteLong3")
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 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]);
+ }
+ }
+
+ @Run(test = {"testByteLong3a", "testByteLong3b"})
public static void testByteLong3_runner() {
- runAndVerify(() -> testByteLong3(byteArray, longArray), 8);
+ runAndVerify(() -> testByteLong3a(byteArray, longArray), 8);
+ runAndVerify(() -> testByteLong3b(byteArray, longArray), 8);
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"},
applyIf = {"AlignVector", "false"})
+ // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
+ // might get fixed with JDK-8325155.
// AlignVector cannot guarantee that invar is aligned.
- public static void testByteLong4(byte[] dest, long[] src, int start, int stop) {
+ 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]);
}
}
- @Run(test = "testByteLong4")
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"},
+ applyIf = {"AlignVector", "false"})
+ // 32-bit: address has ConvL2I for cast of long to address, not supported.
+ // 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]);
+ }
+ }
+
+ @Run(test = {"testByteLong4a", "testByteLong4b"})
public static void testByteLong4_runner() {
baseOffset = UNSAFE.ARRAY_BYTE_BASE_OFFSET;
- runAndVerify(() -> testByteLong4(byteArray, longArray, 0, size), 0);
+ runAndVerify(() -> testByteLong4a(byteArray, longArray, 0, size), 0);
+ runAndVerify(() -> testByteLong4b(byteArray, longArray, 0, size), 0);
}
@Test
- @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
- public static void testByteLong5(byte[] dest, long[] src, int start, int stop) {
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
+ // 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]);
}
}
- @Run(test = "testByteLong5")
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 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]);
+ }
+ }
+
+ @Run(test = {"testByteLong5a", "testByteLong5b"})
public static void testByteLong5_runner() {
baseOffset = 1;
- runAndVerify(() -> testByteLong5(byteArray, longArray, 0, size-1), 8);
+ runAndVerify(() -> testByteLong5a(byteArray, longArray, 0, size-1), 8);
+ runAndVerify(() -> testByteLong5b(byteArray, longArray, 0, size-1), 8);
}
@Test
- @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
- public static void testByteByte1(byte[] dest, byte[] src) {
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
+ // might get fixed with JDK-8325155.
+ public static void testByteByte1a(byte[] dest, byte[] src) {
for (int i = 0; i < src.length / 8; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
}
}
- @Run(test = "testByteByte1")
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 32-bit: address has ConvL2I for cast of long to address, not supported.
+ public static void testByteByte1b(byte[] dest, byte[] src) {
+ for (int i = 0; i < src.length / 8; i++) {
+ UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
+ }
+ }
+
+ @Run(test = {"testByteByte1a", "testByteByte1b"})
public static void testByteByte1_runner() {
- runAndVerify2(() -> testByteByte1(byteArray, byteArray), 0);
+ runAndVerify2(() -> testByteByte1a(byteArray, byteArray), 0);
+ runAndVerify2(() -> testByteByte1b(byteArray, byteArray), 0);
}
@Test
- @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
- public static void testByteByte2(byte[] dest, byte[] src) {
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
+ // might get fixed with JDK-8325155.
+ public static void testByteByte2a(byte[] dest, byte[] src) {
for (int i = 1; i < src.length / 8; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i - 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
}
}
- @Run(test = "testByteByte2")
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
+ applyIfPlatform = {"64-bit", "true"})
+ // 32-bit: address has ConvL2I for cast of long to address, not supported.
+ public static void testByteByte2b(byte[] dest, byte[] src) {
+ for (int i = 1; i < src.length / 8; i++) {
+ UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i - 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
+ }
+ }
+
+ @Run(test = {"testByteByte2a", "testByteByte2b"})
public static void testByteByte2_runner() {
- runAndVerify2(() -> testByteByte2(byteArray, byteArray), -8);
+ runAndVerify2(() -> testByteByte2a(byteArray, byteArray), -8);
+ runAndVerify2(() -> testByteByte2b(byteArray, byteArray), -8);
}
@Test
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
- public static void testByteByte3(byte[] dest, byte[] src) {
+ public static void testByteByte3a(byte[] dest, byte[] src) {
for (int i = 0; i < src.length / 8 - 1; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
}
}
- @Run(test = "testByteByte3")
+ @Test
+ @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
+ public static void testByteByte3b(byte[] dest, byte[] src) {
+ for (int i = 0; i < src.length / 8 - 1; i++) {
+ UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
+ }
+ }
+
+ @Run(test = {"testByteByte3a", "testByteByte3b"})
public static void testByteByte3_runner() {
- runAndVerify2(() -> testByteByte3(byteArray, byteArray), 8);
+ runAndVerify2(() -> testByteByte3a(byteArray, byteArray), 8);
+ runAndVerify2(() -> testByteByte3b(byteArray, byteArray), 8);
}
@Test
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
- public static void testByteByte4(byte[] dest, byte[] src, int start, int stop) {
+ public static void testByteByte4a(byte[] dest, byte[] src, int start, int stop) {
for (int i = start; i < stop; i++) {
UNSAFE.putLongUnaligned(dest, 8 * i + baseOffset, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
}
}
- @Run(test = "testByteByte4")
+ @Test
+ @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
+ public static void testByteByte4b(byte[] dest, byte[] src, int start, int stop) {
+ for (int i = start; i < stop; i++) {
+ UNSAFE.putLongUnaligned(dest, 8L * i + baseOffset, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
+ }
+ }
+
+ @Run(test = {"testByteByte4a", "testByteByte4b"})
public static void testByteByte4_runner() {
baseOffset = UNSAFE.ARRAY_BYTE_BASE_OFFSET;
- runAndVerify2(() -> testByteByte4(byteArray, byteArray, 0, size), 0);
+ runAndVerify2(() -> testByteByte4a(byteArray, byteArray, 0, size), 0);
+ runAndVerify2(() -> testByteByte4b(byteArray, byteArray, 0, size), 0);
}
@Test
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
- public static void testByteByte5(byte[] dest, byte[] src, int start, int stop) {
+ public static void testByteByte5a(byte[] dest, byte[] src, int start, int stop) {
for (int i = start; i < stop; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + baseOffset), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
}
}
- @Run(test = "testByteByte5")
+ @Test
+ @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
+ public static void testByteByte5b(byte[] dest, byte[] src, int start, int stop) {
+ for (int i = start; i < stop; i++) {
+ UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + baseOffset), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
+ }
+ }
+
+ @Run(test = {"testByteByte5a", "testByteByte5b"})
public static void testByteByte5_runner() {
baseOffset = 1;
- runAndVerify2(() -> testByteByte5(byteArray, byteArray, 0, size-1), 8);
+ runAndVerify2(() -> testByteByte5a(byteArray, byteArray, 0, size-1), 8);
+ runAndVerify2(() -> testByteByte5b(byteArray, byteArray, 0, size-1), 8);
}
@Test
- @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
- public static void testOffHeapLong1(long dest, long[] src) {
+ @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
+ // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
+ // FAILS: adr is CastX2P(dest + 8 * (i + int_con))
+ // 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]);
}
}
- @Run(test = "testOffHeapLong1")
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
+ // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
+ // FAILS: adr is CastX2P(dest + 8L * (i + int_con))
+ // 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]);
+ }
+ }
+
+ @Run(test = {"testOffHeapLong1a", "testOffHeapLong1b"})
public static void testOffHeapLong1_runner() {
- runAndVerify3(() -> testOffHeapLong1(baseOffHeap, longArray), 0);
+ runAndVerify3(() -> testOffHeapLong1a(baseOffHeap, longArray), 0);
+ runAndVerify3(() -> testOffHeapLong1b(baseOffHeap, longArray), 0);
}
@Test
- @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
- public static void testOffHeapLong2(long dest, long[] src) {
+ @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
+ // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
+ // FAILS: adr is CastX2P
+ // 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]);
}
}
- @Run(test = "testOffHeapLong2")
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
+ // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
+ // FAILS: adr is CastX2P
+ // 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]);
+ }
+ }
+
+ @Run(test = {"testOffHeapLong2a", "testOffHeapLong2b"})
public static void testOffHeapLong2_runner() {
- runAndVerify3(() -> testOffHeapLong2(baseOffHeap, longArray), -8);
+ runAndVerify3(() -> testOffHeapLong2a(baseOffHeap, longArray), -8);
+ runAndVerify3(() -> testOffHeapLong2b(baseOffHeap, longArray), -8);
}
@Test
- @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
- public static void testOffHeapLong3(long dest, long[] src) {
+ @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
+ // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
+ // FAILS: adr is CastX2P
+ // 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]);
}
}
- @Run(test = "testOffHeapLong3")
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
+ // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
+ // FAILS: adr is CastX2P
+ // 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]);
+ }
+ }
+
+ @Run(test = {"testOffHeapLong3a", "testOffHeapLong3b"})
public static void testOffHeapLong3_runner() {
- runAndVerify3(() -> testOffHeapLong3(baseOffHeap, longArray), 8);
+ runAndVerify3(() -> testOffHeapLong3a(baseOffHeap, longArray), 8);
+ runAndVerify3(() -> testOffHeapLong3b(baseOffHeap, longArray), 8);
}
@Test
- @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
- applyIf = {"AlignVector", "false"})
+ @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
+ // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ // applyIf = {"AlignVector", "false"})
+ // FAILS: adr is CastX2P
+ // See: JDK-8331576
// AlignVector cannot guarantee that invar is aligned.
- public static void testOffHeapLong4(long dest, long[] src, int start, int stop) {
+ 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]);
}
}
- @Run(test = "testOffHeapLong4")
+ @Test
+ @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
+ // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
+ // applyIf = {"AlignVector", "false"})
+ // FAILS: adr is CastX2P
+ // See: JDK-8331576
+ // 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]);
+ }
+ }
+
+ @Run(test = {"testOffHeapLong4a", "testOffHeapLong4b"})
public static void testOffHeapLong4_runner() {
baseOffset = 8;
- runAndVerify3(() -> testOffHeapLong4(baseOffHeap, longArray, 0, size-1), 8);
+ runAndVerify3(() -> testOffHeapLong4a(baseOffHeap, longArray, 0, size-1), 8);
+ runAndVerify3(() -> testOffHeapLong4b(baseOffHeap, longArray, 0, size-1), 8);
}
}
diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java
index fd5c2969074..c77f4f6fa2e 100644
--- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java
+++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java
@@ -1363,7 +1363,7 @@ public class TestAlignVector {
static Object[] test17a(long[] a) {
// Unsafe: vectorizes with profiling (not xcomp)
for (int i = 0; i < RANGE; i++) {
- int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i;
+ long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i;
long v = UNSAFE.getLongUnaligned(a, adr);
UNSAFE.putLongUnaligned(a, adr, v + 1);
}
@@ -1375,7 +1375,7 @@ public class TestAlignVector {
static Object[] test17b(long[] a) {
// Not alignable
for (int i = 0; i < RANGE-1; i++) {
- int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i + 1;
+ long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i + 1;
long v = UNSAFE.getLongUnaligned(a, adr);
UNSAFE.putLongUnaligned(a, adr, v + 1);
}
@@ -1392,7 +1392,7 @@ public class TestAlignVector {
static Object[] test17c(long[] a) {
// Unsafe: aligned vectorizes
for (int i = 0; i < RANGE-1; i+=4) {
- int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i;
+ long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i;
long v0 = UNSAFE.getLongUnaligned(a, adr + 0);
long v1 = UNSAFE.getLongUnaligned(a, adr + 8);
UNSAFE.putLongUnaligned(a, adr + 0, v0 + 1);
@@ -1422,7 +1422,7 @@ public class TestAlignVector {
static Object[] test17d(long[] a) {
// Not alignable
for (int i = 0; i < RANGE-1; i+=4) {
- int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i + 1;
+ long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i + 1;
long v0 = UNSAFE.getLongUnaligned(a, adr + 0);
long v1 = UNSAFE.getLongUnaligned(a, adr + 8);
UNSAFE.putLongUnaligned(a, adr + 0, v0 + 1);
diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVectorFuzzer.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVectorFuzzer.java
index 7b95781905e..d75db965ea3 100644
--- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVectorFuzzer.java
+++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVectorFuzzer.java
@@ -1090,11 +1090,11 @@ public class TestAlignVectorFuzzer {
int init = init_con_or_var();
int limit = limit_con_or_var();
int stride = stride_con();
- int scale = scale_con();
- int offset = offset1_con_or_var();
+ long scale = scale_con();
+ long offset = offset1_con_or_var();
for (int i = init; i < limit; i += stride) {
- int adr = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset + i * scale;
+ long adr = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset + i * scale;
int v = UNSAFE.getIntUnaligned(a, adr);
UNSAFE.putIntUnaligned(a, adr, v + 1);
}
@@ -1105,19 +1105,19 @@ public class TestAlignVectorFuzzer {
int init = init_con_or_var();
int limit = limit_con_or_var();
int stride = stride_con();
- int scale = scale_con();
- int offset1 = offset1_con_or_var();
- int offset2 = offset2_con_or_var();
- int offset3 = offset3_con_or_var();
+ long scale = scale_con();
+ long offset1 = offset1_con_or_var();
+ long offset2 = offset2_con_or_var();
+ long offset3 = offset3_con_or_var();
int h1 = hand_unrolling1_con();
int h2 = hand_unrolling2_con();
int h3 = hand_unrolling3_con();
for (int i = init; i < limit; i += stride) {
- int adr1 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset1 + i * scale;
- int adr2 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset2 + i * scale;
- int adr3 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset3 + i * scale;
+ long adr1 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset1 + i * scale;
+ long adr2 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset2 + i * scale;
+ long adr3 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset3 + i * scale;
if (h1 >= 1) { UNSAFE.putIntUnaligned(a, adr1 + 0*4, UNSAFE.getIntUnaligned(a, adr1 + 0*4) + 1); }
if (h1 >= 2) { UNSAFE.putIntUnaligned(a, adr1 + 1*4, UNSAFE.getIntUnaligned(a, adr1 + 1*4) + 1); }
diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java
index 65398e8adfd..197ae08b6d8 100644
--- a/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java
+++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java
@@ -172,10 +172,10 @@ public class TestIndependentPacksWithCyclicDependency {
static void test2(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb) {
for (int i = 0; i < RANGE; i+=2) {
// int and float arrays are two slices. But we pretend both are of type int.
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1);
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] + 1);
- dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0);
- dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4);
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1);
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] + 1);
+ dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0);
+ dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4);
}
}
@@ -248,10 +248,10 @@ public class TestIndependentPacksWithCyclicDependency {
for (int i = 0; i < RANGE; i+=2) {
// same as test2, except that reordering leads to different semantics
// explanation analogue to test4
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1); // A
- dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0); // X
- dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4); // Y
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] + 1); // B
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1); // A
+ dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0); // X
+ dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4); // Y
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] + 1); // B
}
}
@@ -275,18 +275,18 @@ public class TestIndependentPacksWithCyclicDependency {
long[] dataLa, long[] dataLb) {
for (int i = 0; i < RANGE; i+=2) {
// Chain of parallelizable op and conversion
- int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
- int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3;
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
- int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
- int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45;
- unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
- unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
- float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
- float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
- unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
- unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
+ int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
+ int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3;
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
+ int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
+ int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45;
+ unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
+ unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
+ float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
+ float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
+ unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
+ unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
}
}
@@ -307,18 +307,18 @@ public class TestIndependentPacksWithCyclicDependency {
long[] dataLa, long[] dataLb) {
for (int i = 0; i < RANGE; i+=2) {
// Cycle involving 3 memory slices
- int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
- int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
- int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45;
- unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
- unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
- float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
- float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
- unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
- unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
- int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // moved down
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
+ int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
+ int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
+ int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45;
+ unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
+ unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
+ float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
+ float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
+ unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
+ unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
+ int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // moved down
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
}
}
@@ -340,19 +340,19 @@ public class TestIndependentPacksWithCyclicDependency {
long[] dataLa, long[] dataLb) {
for (int i = 0; i < RANGE; i+=2) {
// 2-cycle, with more ops after
- int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
- int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
- int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45;
- unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
- unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
- int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3;
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
+ int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
+ int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
+ int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45;
+ unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
+ unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
+ int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3;
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
// more stuff after
- float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
- float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
- unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
- unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
+ float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
+ float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
+ unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
+ unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
}
}
@@ -373,19 +373,19 @@ public class TestIndependentPacksWithCyclicDependency {
long[] dataLa, long[] dataLb) {
for (int i = 0; i < RANGE; i+=2) {
// 2-cycle, with more stuff before
- float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
- float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
- unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
- unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
+ float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
+ float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
+ unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
+ unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
// 2-cycle
- int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
- int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
- int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45;
- unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
- unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
- int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3;
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
+ int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
+ int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
+ int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45;
+ unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
+ unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
+ int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3;
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
}
}
@@ -423,18 +423,18 @@ public class TestIndependentPacksWithCyclicDependency {
//
// The cycle thus does not only go via packs, but also scalar ops.
//
- int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; // A
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
- int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; // R: constant mismatch
- int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) + 43; // S
- unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
- unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
- float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; // U
- float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; // V
- unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
- unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
- int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // B: moved down
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
+ int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; // A
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
+ int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; // R: constant mismatch
+ int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) + 43; // S
+ unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
+ unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
+ float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; // U
+ float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; // V
+ unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
+ unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
+ int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // B: moved down
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
}
}
@@ -463,8 +463,8 @@ public class TestIndependentPacksWithCyclicDependency {
static void verify(String name, float[] data, float[] gold) {
for (int i = 0; i < RANGE; i++) {
- int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i);
- int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i);
+ int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i);
+ int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i);
if (datav != goldv) {
throw new RuntimeException(" Invalid " + name + " result: dataF[" + i + "]: " + datav + " != " + goldv);
}
diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency2.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency2.java
index 32d69689a42..2be7d52c780 100644
--- a/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency2.java
+++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency2.java
@@ -58,18 +58,18 @@ public class TestIndependentPacksWithCyclicDependency2 {
long[] dataLa, long[] dataLb) {
for (int i = 0; i < RANGE; i+=2) {
// For explanation, see test 10 in TestIndependentPacksWithCyclicDependency.java
- int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
- int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
- int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) + 43;
- unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
- unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
- float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
- float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
- unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
- unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
- int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // moved down
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
+ int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
+ int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
+ int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) + 43;
+ unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
+ unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
+ float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
+ float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
+ unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
+ unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
+ int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // moved down
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
}
}
@@ -83,8 +83,8 @@ public class TestIndependentPacksWithCyclicDependency2 {
static void verify(String name, float[] data, float[] gold) {
for (int i = 0; i < RANGE; i++) {
- int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i);
- int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i);
+ int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i);
+ int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i);
if (datav != goldv) {
throw new RuntimeException(" Invalid " + name + " result: dataF[" + i + "]: " + datav + " != " + goldv);
}
diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestScheduleReordersScalarMemops.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestScheduleReordersScalarMemops.java
index f2ed8b6aec2..c54a684c691 100644
--- a/test/hotspot/jtreg/compiler/loopopts/superword/TestScheduleReordersScalarMemops.java
+++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestScheduleReordersScalarMemops.java
@@ -124,10 +124,10 @@ public class TestScheduleReordersScalarMemops {
for (int i = 0; i < RANGE; i+=2) {
// Do the same as test0, but without int-float conversion.
// This should reproduce on machines where conversion is not implemented.
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1); // A +1
- dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0); // X
- dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4); // Y
- unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] * 11); // B *11
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1); // A +1
+ dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0); // X
+ dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4); // Y
+ unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] * 11); // B *11
}
}
From 498a58244d79b6aaffd17300844f9908991d463c Mon Sep 17 00:00:00 2001
From: Matias Saavedra Silva
Date: Fri, 21 Jun 2024 16:48:04 +0000
Subject: [PATCH 5/9] 8311208: Improve CDS Support
Reviewed-by: rhalade, mschoene, ccheung, iklam
---
src/hotspot/share/classfile/verifier.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp
index a66fbf645f5..375570cf196 100644
--- a/src/hotspot/share/classfile/verifier.cpp
+++ b/src/hotspot/share/classfile/verifier.cpp
@@ -32,6 +32,7 @@
#include "classfile/stackMapTableFormat.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
#include "classfile/verifier.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
@@ -212,6 +213,11 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) {
exception_name == vmSymbols::java_lang_ClassFormatError())) {
log_info(verification)("Fail over class verification to old verifier for: %s", klass->external_name());
log_info(class, init)("Fail over class verification to old verifier for: %s", klass->external_name());
+ // Exclude any classes that fail over during dynamic dumping
+ if (CDSConfig::is_dumping_dynamic_archive()) {
+ SystemDictionaryShared::warn_excluded(klass, "Failed over class verification while dynamic dumping");
+ SystemDictionaryShared::set_excluded(klass);
+ }
message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len);
exception_message = message_buffer;
exception_name = inference_verify(
From dadcee1b89515a651d0532d9803c22dfeeb64f9e Mon Sep 17 00:00:00 2001
From: Hai-May Chao
Date: Mon, 1 Jul 2024 19:43:34 +0000
Subject: [PATCH 6/9] 8307383: Enhance DTLS connections
Co-authored-by: Jamil Nimeh
Reviewed-by: rhalade, mschoene, ascarpino
---
.../classes/sun/security/ssl/ClientHello.java | 5 +-
.../sun/security/ssl/DTLSInputRecord.java | 137 +++++++++++++++++-
.../security/ssl/ServerHandshakeContext.java | 3 +-
.../sun/security/ssl/TransportContext.java | 7 +-
.../javax/net/ssl/DTLS/InvalidRecords.java | 32 +++-
.../jdk/javax/net/ssl/TLSCommon/MFLNTest.java | 12 +-
6 files changed, 184 insertions(+), 12 deletions(-)
diff --git a/src/java.base/share/classes/sun/security/ssl/ClientHello.java b/src/java.base/share/classes/sun/security/ssl/ClientHello.java
index babf2bb452d..e75076b11d6 100644
--- a/src/java.base/share/classes/sun/security/ssl/ClientHello.java
+++ b/src/java.base/share/classes/sun/security/ssl/ClientHello.java
@@ -213,8 +213,6 @@ final class ClientHello {
// ignore cookie
hos.putBytes16(getEncodedCipherSuites());
hos.putBytes8(compressionMethod);
- extensions.send(hos); // In TLS 1.3, use of certain
- // extensions is mandatory.
} catch (IOException ioe) {
// unlikely
}
@@ -1426,6 +1424,9 @@ final class ClientHello {
shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO.id,
SSLHandshake.SERVER_HELLO);
+ // Reset the ClientHello non-zero offset fragment allowance
+ shc.acceptCliHelloFragments = false;
+
//
// produce
//
diff --git a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java
index 337cf76f2c2..e0196f3009c 100644
--- a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java
+++ b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -40,12 +40,23 @@ import sun.security.ssl.SSLCipher.SSLReadCipher;
final class DTLSInputRecord extends InputRecord implements DTLSRecord {
private DTLSReassembler reassembler = null;
private int readEpoch;
+ private SSLContextImpl sslContext;
DTLSInputRecord(HandshakeHash handshakeHash) {
super(handshakeHash, SSLReadCipher.nullDTlsReadCipher());
this.readEpoch = 0;
}
+ // Method to set TransportContext
+ public void setTransportContext(TransportContext tc) {
+ this.tc = tc;
+ }
+
+ // Method to set SSLContext
+ public void setSSLContext(SSLContextImpl sslContext) {
+ this.sslContext = sslContext;
+ }
+
@Override
void changeReadCiphers(SSLReadCipher readCipher) {
this.readCipher = readCipher;
@@ -537,6 +548,27 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
}
+ /**
+ * Turn a sufficiently-large initial ClientHello fragment into one that
+ * stops immediately after the compression methods. This is only used
+ * for the initial CH message fragment at offset 0.
+ *
+ * @param srcFrag the fragment actually received by the DTLSReassembler
+ * @param limit the size of the new, cloned/truncated handshake fragment
+ *
+ * @return a truncated handshake fragment that is sized to look like a
+ * complete message, but actually contains only up to the compression
+ * methods (no extensions)
+ */
+ private static HandshakeFragment truncateChFragment(HandshakeFragment srcFrag,
+ int limit) {
+ return new HandshakeFragment(Arrays.copyOf(srcFrag.fragment, limit),
+ srcFrag.contentType, srcFrag.majorVersion,
+ srcFrag.minorVersion, srcFrag.recordEnS, srcFrag.recordEpoch,
+ srcFrag.recordSeq, srcFrag.handshakeType, limit,
+ srcFrag.messageSeq, srcFrag.fragmentOffset, limit);
+ }
+
private static final class HoleDescriptor {
int offset; // fragment_offset
int limit; // fragment_offset + fragment_length
@@ -640,10 +672,17 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// Queue up a handshake message.
void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException {
if (!isDesirable(hsf)) {
- // Not a dedired record, discard it.
+ // Not a desired record, discard it.
return;
}
+ if (hsf.handshakeType == SSLHandshake.CLIENT_HELLO.id) {
+ // validate the first or subsequent ClientHello message
+ if ((hsf = valHello(hsf, hsf.messageSeq == 0)) == null) {
+ return;
+ }
+ }
+
// Clean up the retransmission messages if necessary.
cleanUpRetransmit(hsf);
@@ -769,6 +808,100 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
}
+ private HandshakeFragment valHello(HandshakeFragment hsf,
+ boolean firstHello) {
+ ServerHandshakeContext shc =
+ (ServerHandshakeContext) tc.handshakeContext;
+ // Drop any fragment that is not a zero offset until we've received
+ // a second (or possibly later) CH message that passes the cookie
+ // check.
+ if (shc == null || !shc.acceptCliHelloFragments) {
+ if (hsf.fragmentOffset != 0) {
+ return null;
+ }
+ } else {
+ // Let this fragment through to the DTLSReassembler as-is
+ return hsf;
+ }
+
+ try {
+ ByteBuffer fragmentData = ByteBuffer.wrap(hsf.fragment);
+
+ ProtocolVersion pv = ProtocolVersion.valueOf(
+ Record.getInt16(fragmentData));
+ if (!pv.isDTLS) {
+ return null;
+ }
+ // Read the random (32 bytes)
+ if (fragmentData.remaining() < 32) {
+ if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+ SSLLogger.fine("Rejected client hello fragment (bad random len) " +
+ "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength);
+ }
+ return null;
+ }
+ fragmentData.position(fragmentData.position() + 32);
+
+ // SessionID
+ byte[] sessId = Record.getBytes8(fragmentData);
+ if (sessId.length > 0 &&
+ !SSLConfiguration.enableDtlsResumeCookie) {
+ // If we are in a resumption it is possible that the cookie
+ // exchange will be skipped. This is a server-side setting
+ // and it is NOT the default. If enableDtlsResumeCookie is
+ // false though, then we will buffer fragments since there
+ // is no cookie exchange to execute prior to performing
+ // reassembly.
+ return hsf;
+ }
+
+ // Cookie
+ byte[] cookie = Record.getBytes8(fragmentData);
+ if (firstHello && cookie.length != 0) {
+ if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+ SSLLogger.fine("Rejected initial client hello fragment (bad cookie len) " +
+ "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength);
+ }
+ return null;
+ }
+ // CipherSuites
+ Record.getBytes16(fragmentData);
+ // Compression methods
+ Record.getBytes8(fragmentData);
+
+ // If it's the first fragment, we'll truncate it and push it
+ // through the reassembler.
+ if (firstHello) {
+ return truncateChFragment(hsf, fragmentData.position());
+ } else {
+ HelloCookieManager hcMgr = sslContext.
+ getHelloCookieManager(ProtocolVersion.DTLS10);
+ ByteBuffer msgFragBuf = ByteBuffer.wrap(hsf.fragment, 0,
+ fragmentData.position());
+ ClientHello.ClientHelloMessage chMsg =
+ new ClientHello.ClientHelloMessage(shc, msgFragBuf, null);
+ if (!hcMgr.isCookieValid(shc, chMsg, cookie)) {
+ // Bad cookie check, truncate it and let the ClientHello
+ // consumer recheck, fail and take the appropriate action.
+ return truncateChFragment(hsf, fragmentData.position());
+ } else {
+ // It's a good cookie, return the original handshake
+ // fragment and let it go into the DTLSReassembler like
+ // any other fragment so we can wait for the rest of
+ // the CH message.
+ shc.acceptCliHelloFragments = true;
+ return hsf;
+ }
+ }
+ } catch (IOException ioe) {
+ if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+ SSLLogger.fine("Rejected client hello fragment " +
+ "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength);
+ }
+ return null;
+ }
+ }
+
// Queue up a ChangeCipherSpec message
void queueUpChangeCipherSpec(RecordFragment rf)
throws SSLProtocolException {
diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java
index 829fa2af96c..11b625e5791 100644
--- a/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java
+++ b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -55,6 +55,7 @@ class ServerHandshakeContext extends HandshakeContext {
CertificateMessage.CertificateEntry currentCertEntry;
private static final long DEFAULT_STATUS_RESP_DELAY = 5000L;
final long statusRespTimeout;
+ boolean acceptCliHelloFragments = false;
ServerHandshakeContext(SSLContextImpl sslContext,
diff --git a/src/java.base/share/classes/sun/security/ssl/TransportContext.java b/src/java.base/share/classes/sun/security/ssl/TransportContext.java
index c235da3068c..f65a08dfcfe 100644
--- a/src/java.base/share/classes/sun/security/ssl/TransportContext.java
+++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -156,6 +156,11 @@ final class TransportContext implements ConnectionContext {
this.acc = AccessController.getContext();
this.consumers = new HashMap<>();
+
+ if (inputRecord instanceof DTLSInputRecord dtlsInputRecord) {
+ dtlsInputRecord.setTransportContext(this);
+ dtlsInputRecord.setSSLContext(this.sslContext);
+ }
}
// Dispatch plaintext to a specific consumer.
diff --git a/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java b/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java
index 304cb0695d6..120e6b258e6 100644
--- a/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java
+++ b/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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,7 +26,7 @@
/*
* @test
- * @bug 8043758
+ * @bug 8043758 8307383
* @summary Datagram Transport Layer Security (DTLS)
* @modules java.base/sun.security.util
* @library /test/lib
@@ -36,6 +36,7 @@
import java.net.DatagramPacket;
import java.net.SocketAddress;
+import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -73,11 +74,34 @@ public class InvalidRecords extends DTLSOverDatagram {
// ClientHello with cookie
needInvalidRecords.set(false);
System.out.println("invalidate ClientHello message");
- if (ba[ba.length - 1] == (byte)0xFF) {
- ba[ba.length - 1] = (byte)0xFE;
+ // We will alter the compression method field in order to make the cookie
+ // check fail.
+ ByteBuffer chRec = ByteBuffer.wrap(ba);
+ // Skip 59 bytes past the record header (13), the handshake header (12),
+ // the protocol version (2), and client random (32)
+ chRec.position(59);
+ // Jump past the session ID
+ int len = Byte.toUnsignedInt(chRec.get());
+ chRec.position(chRec.position() + len);
+ // Skip the cookie
+ len = Byte.toUnsignedInt(chRec.get());
+ chRec.position(chRec.position() + len);
+ // Skip past cipher suites
+ len = Short.toUnsignedInt(chRec.getShort());
+ chRec.position(chRec.position() + len);
+ // Read the data on the compression methods, should be at least 1
+ len = Byte.toUnsignedInt(chRec.get());
+ if (len >= 1) {
+ System.out.println("Detected compression methods (count = " + len + ")");
} else {
ba[ba.length - 1] = (byte)0xFF;
+ throw new RuntimeException("Got zero length comp methods");
}
+ // alter the first comp method.
+ int compMethodVal = Byte.toUnsignedInt(chRec.get(chRec.position()));
+ System.out.println("Changing value at position " + chRec.position() +
+ " from " + compMethodVal + " to " + ++compMethodVal);
+ chRec.put(chRec.position(), (byte)compMethodVal);
}
return super.createHandshakePacket(ba, socketAddr);
diff --git a/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java b/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java
index ee50f21ae27..0867925f135 100644
--- a/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java
+++ b/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -34,7 +34,15 @@ public class MFLNTest extends SSLEngineTestCase {
public static void main(String[] args) {
setUpAndStartKDCIfNeeded();
System.setProperty("jsse.enableMFLNExtension", "true");
- for (int mfl = 4096; mfl >= 256; mfl /= 2) {
+ String testMode = System.getProperty("test.mode", "norm");
+ int mflLen;
+ if (testMode.equals("norm_sni")) {
+ mflLen = 512;
+ } else {
+ mflLen = 256;
+ }
+
+ for (int mfl = 4096; mfl >= mflLen; mfl /= 2) {
System.out.println("=============================================="
+ "==============");
System.out.printf("Testsing DTLS handshake with MFL = %d%n", mfl);
From d9f638303737c4e5174e5cf21ad2f634cc0b1494 Mon Sep 17 00:00:00 2001
From: Justin Lu
Date: Thu, 4 Jul 2024 23:09:40 +0000
Subject: [PATCH 7/9] 8331446: Improve deserialization support
Reviewed-by: rhalade, skoivu, rriggs, naoto
---
.../classes/java/text/MessageFormat.java | 60 ++++++++++--
.../MessageFormat/MaxArgumentIndexTest.java | 97 +++++++++++++++++++
.../MessageFormat/SerializationTest.java | 96 ++++++++++++++++++
3 files changed, 243 insertions(+), 10 deletions(-)
create mode 100644 test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java
create mode 100644 test/jdk/java/text/Format/MessageFormat/SerializationTest.java
diff --git a/src/java.base/share/classes/java/text/MessageFormat.java b/src/java.base/share/classes/java/text/MessageFormat.java
index 58872834567..83cc3ec4647 100644
--- a/src/java.base/share/classes/java/text/MessageFormat.java
+++ b/src/java.base/share/classes/java/text/MessageFormat.java
@@ -41,6 +41,7 @@ package java.text;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
+import java.io.ObjectStreamException;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -1181,6 +1182,8 @@ public class MessageFormat extends Format {
maximumArgumentNumber = argumentNumbers[i];
}
}
+
+ // Constructors/applyPattern ensure that resultArray.length < MAX_ARGUMENT_INDEX
Object[] resultArray = new Object[maximumArgumentNumber + 1];
int patternOffset = 0;
@@ -1459,6 +1462,9 @@ public class MessageFormat extends Format {
* @serial
*/
private int[] argumentNumbers = new int[INITIAL_FORMATS];
+ // Implementation limit for ArgumentIndex pattern element. Valid indices must
+ // be less than this value
+ private static final int MAX_ARGUMENT_INDEX = 10000;
/**
* One less than the number of entries in {@code offsets}. Can also be thought of
@@ -1639,6 +1645,11 @@ public class MessageFormat extends Format {
+ argumentNumber);
}
+ if (argumentNumber >= MAX_ARGUMENT_INDEX) {
+ throw new IllegalArgumentException(
+ argumentNumber + " exceeds the ArgumentIndex implementation limit");
+ }
+
// resize format information arrays if necessary
if (offsetNumber >= formats.length) {
int newLength = formats.length * 2;
@@ -2006,24 +2017,53 @@ public class MessageFormat extends Format {
*/
@java.io.Serial
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
- in.defaultReadObject();
- boolean isValid = maxOffset >= -1
- && formats.length > maxOffset
- && offsets.length > maxOffset
- && argumentNumbers.length > maxOffset;
+ ObjectInputStream.GetField fields = in.readFields();
+ if (fields.defaulted("argumentNumbers") || fields.defaulted("offsets")
+ || fields.defaulted("formats") || fields.defaulted("locale")
+ || fields.defaulted("pattern") || fields.defaulted("maxOffset")){
+ throw new InvalidObjectException("Stream has missing data");
+ }
+
+ locale = (Locale) fields.get("locale", null);
+ String patt = (String) fields.get("pattern", null);
+ int maxOff = fields.get("maxOffset", -2);
+ int[] argNums = ((int[]) fields.get("argumentNumbers", null)).clone();
+ int[] offs = ((int[]) fields.get("offsets", null)).clone();
+ Format[] fmts = ((Format[]) fields.get("formats", null)).clone();
+
+ // Check arrays/maxOffset have correct value/length
+ boolean isValid = maxOff >= -1 && argNums.length > maxOff
+ && offs.length > maxOff && fmts.length > maxOff;
+
+ // Check the correctness of arguments and offsets
if (isValid) {
- int lastOffset = pattern.length() + 1;
- for (int i = maxOffset; i >= 0; --i) {
- if ((offsets[i] < 0) || (offsets[i] > lastOffset)) {
+ int lastOffset = patt.length() + 1;
+ for (int i = maxOff; i >= 0; --i) {
+ if (argNums[i] < 0 || argNums[i] >= MAX_ARGUMENT_INDEX
+ || offs[i] < 0 || offs[i] > lastOffset) {
isValid = false;
break;
} else {
- lastOffset = offsets[i];
+ lastOffset = offs[i];
}
}
}
+
if (!isValid) {
- throw new InvalidObjectException("Could not reconstruct MessageFormat from corrupt stream.");
+ throw new InvalidObjectException("Stream has invalid data");
}
+ maxOffset = maxOff;
+ pattern = patt;
+ offsets = offs;
+ formats = fmts;
+ argumentNumbers = argNums;
+ }
+
+ /**
+ * Serialization without data not supported for this class.
+ */
+ @java.io.Serial
+ private void readObjectNoData() throws ObjectStreamException {
+ throw new InvalidObjectException("Deserialized MessageFormat objects need data");
}
}
diff --git a/test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java b/test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java
new file mode 100644
index 00000000000..e12dabb6383
--- /dev/null
+++ b/test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.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
+ * @bug 8331446
+ * @summary Enforce the MAX_ARGUMENT_INDEX(10,000) implementation limit for the
+ * ArgumentIndex element in the MessageFormat pattern syntax. This
+ * should be checked during construction/applyPattern/readObject and should effectively
+ * prevent parse/format from being invoked with values over the limit.
+ * @run junit MaxArgumentIndexTest
+ */
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class MaxArgumentIndexTest {
+
+ // A MessageFormat pattern that contains an ArgumentIndex value
+ // which violates this implementation's limit: MAX_ARGUMENT_INDEX(10,000)
+ // As this check is exclusive, 10,000 will violate the limit
+ private static final String VIOLATES_MAX_ARGUMENT_INDEX = "{10000}";
+
+ // Check String constructor enforces the limit
+ @Test
+ public void constructorTest() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX));
+ }
+
+ // Check String, Locale constructor enforces the limit
+ @ParameterizedTest
+ @MethodSource
+ public void constructorWithLocaleTest(Locale locale) {
+ assertThrows(IllegalArgumentException.class,
+ () -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX, locale));
+ }
+
+ // Provide some basic common locale values
+ private static Stream constructorWithLocaleTest() {
+ return Stream.of(null, Locale.US, Locale.ROOT);
+ }
+
+ // Edge case: Test a locale dependent subformat (with null locale) with a
+ // violating ArgumentIndex. In this instance, the violating ArgumentIndex
+ // will be caught and IAE thrown instead of the NPE
+ @Test
+ public void localeDependentSubFormatTest() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new MessageFormat("{10000,number,short}", null));
+ // For reference
+ assertThrows(NullPointerException.class,
+ () -> new MessageFormat("{999,number,short}", null));
+ }
+
+ // Check that the static format method enforces the limit
+ @Test
+ public void staticFormatTest() {
+ assertThrows(IllegalArgumentException.class,
+ () -> MessageFormat.format(VIOLATES_MAX_ARGUMENT_INDEX, new Object[]{1}));
+ }
+
+ // Check that applyPattern(String) enforces the limit
+ @Test
+ public void applyPatternTest() {
+ MessageFormat mf = new MessageFormat("");
+ assertThrows(IllegalArgumentException.class,
+ () -> mf.applyPattern(VIOLATES_MAX_ARGUMENT_INDEX));
+ }
+}
diff --git a/test/jdk/java/text/Format/MessageFormat/SerializationTest.java b/test/jdk/java/text/Format/MessageFormat/SerializationTest.java
new file mode 100644
index 00000000000..9191c5caef3
--- /dev/null
+++ b/test/jdk/java/text/Format/MessageFormat/SerializationTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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
+ * @bug 8331446
+ * @summary Check correctness of deserialization
+ * @run junit SerializationTest
+ */
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SerializationTest {
+
+ // Ensure basic correctness of serialization round trip
+ @ParameterizedTest
+ @MethodSource
+ public void serializationRoundTrip(MessageFormat expectedMf)
+ throws IOException, ClassNotFoundException {
+ byte[] bytes = ser(expectedMf);
+ MessageFormat actualMf = (MessageFormat) deSer(bytes);
+ assertEquals(expectedMf, actualMf);
+ }
+
+ // Various valid MessageFormats
+ private static Stream serializationRoundTrip() {
+ return Stream.of(
+ // basic pattern
+ new MessageFormat("{0} foo"),
+ // Multiple arguments
+ new MessageFormat("{0} {1} foo"),
+ // duplicate arguments
+ new MessageFormat("{0} {0} {1} foo"),
+ // Non-ascending arguments
+ new MessageFormat("{1} {0} foo"),
+ // With locale
+ new MessageFormat("{1} {0} foo", Locale.UK),
+ // With null locale. (NPE not thrown, if no format defined)
+ new MessageFormat("{1} {0} foo", null),
+ // With formats
+ new MessageFormat("{0,number,short} {0} {1,date,long} foo")
+ );
+ }
+
+ // Utility method to serialize
+ private static byte[] ser(Object obj) throws IOException {
+ ByteArrayOutputStream byteArrayOutputStream = new
+ ByteArrayOutputStream();
+ ObjectOutputStream oos = new
+ ObjectOutputStream(byteArrayOutputStream);
+ oos.writeObject(obj);
+ return byteArrayOutputStream.toByteArray();
+ }
+
+ // Utility method to deserialize
+ private static Object deSer(byte[] bytes) throws
+ IOException, ClassNotFoundException {
+ ByteArrayInputStream byteArrayInputStream = new
+ ByteArrayInputStream(bytes);
+ ObjectInputStream ois = new
+ ObjectInputStream(byteArrayInputStream);
+ return ois.readObject();
+ }
+}
From c89f76c0b9ca085192775af9bd9368562b582dd6 Mon Sep 17 00:00:00 2001
From: Dean Long
Date: Thu, 11 Jul 2024 22:32:41 +0000
Subject: [PATCH 8/9] 8332644: Improve graph optimizations
Co-authored-by: Martin Balao
Co-authored-by: Francisco Ferrari Bihurriet
Reviewed-by: epeter, ahgross, thartmann, rhalade, dlong, roland
---
src/hotspot/share/opto/loopnode.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp
index 6cb50b3dee2..c0e929ca1f4 100644
--- a/src/hotspot/share/opto/loopnode.cpp
+++ b/src/hotspot/share/opto/loopnode.cpp
@@ -2601,7 +2601,7 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) {
const TypeInt* init_t = phase->type(in(Init) )->is_int();
const TypeInt* limit_t = phase->type(in(Limit))->is_int();
- int stride_p;
+ jlong stride_p;
jlong lim, ini;
julong max;
if (stride_con > 0) {
@@ -2610,10 +2610,10 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) {
ini = init_t->_lo;
max = (julong)max_jint;
} else {
- stride_p = -stride_con;
+ stride_p = -(jlong)stride_con;
lim = init_t->_hi;
ini = limit_t->_lo;
- max = (julong)min_jint;
+ max = (julong)(juint)min_jint; // double cast to get 0x0000000080000000, not 0xffffffff80000000
}
julong range = lim - ini + stride_p;
if (range <= max) {
From 490d099e234f27adef7d691d3c5a08ebdb550c5d Mon Sep 17 00:00:00 2001
From: Emanuel Peter
Date: Tue, 23 Jul 2024 09:28:58 +0000
Subject: [PATCH 9/9] 8335713: Enhance vectorization analysis
Co-authored-by: Roland Westrelin
Reviewed-by: rhalade, ahgross, thartmann, kvn
---
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 f013abb38fb..fc4eaccff5c 100644
--- a/src/hotspot/share/opto/vectorization.cpp
+++ b/src/hotspot/share/opto/vectorization.cpp
@@ -943,11 +943,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;
}
@@ -989,7 +1018,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;
}
@@ -1012,15 +1043,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;
}
@@ -1039,12 +1079,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)) {
@@ -1061,6 +1103,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;
@@ -1255,6 +1298,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) {
@@ -1264,6 +1310,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;
+}
+
// We use two comparisons, because a subtraction could underflow.
#define RETURN_CMP_VALUE_IF_NOT_EQUAL(a, b) \
if (a < b) { return -1; } \
diff --git a/src/hotspot/share/opto/vectorization.hpp b/src/hotspot/share/opto/vectorization.hpp
index 7de29444e5f..b084edd44b3 100644
--- a/src/hotspot/share/opto/vectorization.hpp
+++ b/src/hotspot/share/opto/vectorization.hpp
@@ -943,6 +943,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;
};