提交 0e637254 编写于 作者: A andrew

8179098: Crypto AES/ECB encryption/decryption performance regression (introduced in jdk9b73)

8135248: Add utility methods to check indexes and ranges
8142493: Utility methods to check indexes and ranges doesn't specify behavior when function produces null
8146458: Improve exception reporting for Objects.checkIndex/checkFromToIndex/checkFromIndexSize
8155794: Move Objects.checkIndex BiFunction accepting methods to an internal package
Summary: Do bounds check per encryption/decryption call instead of per block
Reviewed-by: mbalao
上级 794a6b45
/*
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2018, 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
......@@ -38,7 +38,6 @@ package com.sun.crypto.provider;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.util.Objects;
/**
* Rijndael --pronounced Reindaal-- is a symmetric cipher with a 128-bit
......@@ -348,8 +347,8 @@ final class AESCrypt extends SymmetricCipher implements AESConstants
*/
void encryptBlock(byte[] in, int inOffset,
byte[] out, int outOffset) {
cryptBlockCheck(in, inOffset);
cryptBlockCheck(out, outOffset);
// Array bound checks are done in caller code, i.e.
// FeedbackCipher.encrypt/decrypt(...) to improve performance.
implEncryptBlock(in, inOffset, out, outOffset);
}
......@@ -426,8 +425,8 @@ final class AESCrypt extends SymmetricCipher implements AESConstants
*/
void decryptBlock(byte[] in, int inOffset,
byte[] out, int outOffset) {
cryptBlockCheck(in, inOffset);
cryptBlockCheck(out, outOffset);
// Array bound checks are done in caller code, i.e.
// FeedbackCipher.encrypt/decrypt(...) to improve performance.
implDecryptBlock(in, inOffset, out, outOffset);
}
......@@ -588,26 +587,6 @@ final class AESCrypt extends SymmetricCipher implements AESConstants
out[outOffset ] = (byte)(Si[(a0 ) & 0xFF] ^ (t1 ));
}
// Used to perform all checks required by the Java semantics
// (i.e., null checks and bounds checks) on the input parameters
// to encryptBlock and to decryptBlock.
// Normally, the Java Runtime performs these checks, however, as
// encryptBlock and decryptBlock are possibly replaced with
// compiler intrinsics, the JDK performs the required checks instead.
// Does not check accesses to class-internal (private) arrays.
private static void cryptBlockCheck(byte[] array, int offset) {
Objects.requireNonNull(array);
if (offset < 0 || offset >= array.length) {
throw new ArrayIndexOutOfBoundsException(offset);
}
int largestIndex = offset + AES_BLOCK_SIZE - 1;
if (largestIndex < 0 || largestIndex >= array.length) {
throw new ArrayIndexOutOfBoundsException(largestIndex);
}
}
/**
* Expand a user-supplied key material into a session key.
*
......
/*
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2018, 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.security.InvalidKeyException;
import java.security.ProviderException;
import java.util.Objects;
import sun.security.util.ArrayUtil;
/**
* This class represents ciphers in cipher block chaining (CBC) mode.
......@@ -143,9 +144,9 @@ class CipherBlockChaining extends FeedbackCipher {
if (plainLen <= 0) {
return plainLen;
}
cryptBlockSizeCheck(plainLen);
cryptNullAndBoundsCheck(plain, plainOffset, plainLen);
cryptNullAndBoundsCheck(cipher, cipherOffset, plainLen);
ArrayUtil.blockSizeCheck(plainLen, blockSize);
ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
return implEncrypt(plain, plainOffset, plainLen,
cipher, cipherOffset);
}
......@@ -193,9 +194,9 @@ class CipherBlockChaining extends FeedbackCipher {
if (cipherLen <= 0) {
return cipherLen;
}
cryptBlockSizeCheck(cipherLen);
cryptNullAndBoundsCheck(cipher, cipherOffset, cipherLen);
cryptNullAndBoundsCheck(plain, plainOffset, cipherLen);
ArrayUtil.blockSizeCheck(cipherLen, blockSize);
ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, cipherLen);
ArrayUtil.nullAndBoundsCheck(plain, plainOffset, cipherLen);
return implDecrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
}
......@@ -214,23 +215,4 @@ class CipherBlockChaining extends FeedbackCipher {
}
return cipherLen;
}
private void cryptBlockSizeCheck(int len) {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
}
private static void cryptNullAndBoundsCheck(byte[] array, int offset, int len) {
Objects.requireNonNull(array);
if (offset < 0 || offset >= array.length) {
throw new ArrayIndexOutOfBoundsException(offset);
}
int endIndex = offset + len - 1;
if (endIndex < 0 || endIndex >= array.length) {
throw new ArrayIndexOutOfBoundsException(endIndex);
}
}
}
/*
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2018, 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 com.sun.crypto.provider;
import java.security.InvalidKeyException;
import java.security.ProviderException;
import sun.security.util.ArrayUtil;
/**
* This class represents ciphers in cipher-feedback (CFB) mode.
......@@ -149,9 +150,9 @@ final class CipherFeedback extends FeedbackCipher {
*/
int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) {
if ((plainLen % numBytes) != 0) {
throw new ProviderException("Internal error in input buffering");
}
ArrayUtil.blockSizeCheck(plainLen, numBytes);
ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
int nShift = blockSize - numBytes;
int loopCount = plainLen / numBytes;
......@@ -225,9 +226,10 @@ final class CipherFeedback extends FeedbackCipher {
*/
int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset) {
if ((cipherLen % numBytes) != 0) {
throw new ProviderException("Internal error in input buffering");
}
ArrayUtil.blockSizeCheck(cipherLen, numBytes);
ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, cipherLen);
ArrayUtil.nullAndBoundsCheck(plain, plainOffset, cipherLen);
int nShift = blockSize - numBytes;
int loopCount = cipherLen / numBytes;
......
/*
* Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2018, 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 com.sun.crypto.provider;
import java.security.InvalidKeyException;
import sun.security.util.ArrayUtil;
/**
* This class represents ciphers in counter (CTR) mode.
......@@ -173,6 +174,10 @@ final class CounterMode extends FeedbackCipher {
if (len == 0) {
return 0;
}
ArrayUtil.nullAndBoundsCheck(in, inOff, len);
ArrayUtil.nullAndBoundsCheck(out, outOff, len);
int result = len;
while (len-- > 0) {
if (used >= blockSize) {
......
/*
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2018, 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 com.sun.crypto.provider;
import java.security.InvalidKeyException;
import java.security.ProviderException;
import sun.security.util.ArrayUtil;
/**
* This class represents ciphers in electronic codebook (ECB) mode.
......@@ -112,9 +113,10 @@ final class ElectronicCodeBook extends FeedbackCipher {
* @return the length of the encrypted data
*/
int encrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
ArrayUtil.blockSizeCheck(len, blockSize);
ArrayUtil.nullAndBoundsCheck(in, inOff, len);
ArrayUtil.nullAndBoundsCheck(out, outOff, len);
for (int i = len; i >= blockSize; i -= blockSize) {
embeddedCipher.encryptBlock(in, inOff, out, outOff);
inOff += blockSize;
......@@ -141,9 +143,10 @@ final class ElectronicCodeBook extends FeedbackCipher {
* @return the length of the decrypted data
*/
int decrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
ArrayUtil.blockSizeCheck(len, blockSize);
ArrayUtil.nullAndBoundsCheck(in, inOff, len);
ArrayUtil.nullAndBoundsCheck(out, outOff, len);
for (int i = len; i >= blockSize; i -= blockSize) {
embeddedCipher.decryptBlock(in, inOff, out, outOff);
inOff += blockSize;
......
/*
* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2018, 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,6 +30,8 @@ import java.io.*;
import java.security.*;
import javax.crypto.*;
import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
import sun.security.util.ArrayUtil;
/**
* This class represents ciphers in GaloisCounter (GCM) mode.
......@@ -406,8 +408,8 @@ final class GaloisCounterMode extends FeedbackCipher {
/**
* Performs encryption operation.
*
* <p>The input plain text <code>in</code>, starting at <code>inOff</code>
* and ending at <code>(inOff + len - 1)</code>, is encrypted. The result
* <p>The input plain text <code>in</code>, starting at <code>inOfs</code>
* and ending at <code>(inOfs + len - 1)</code>, is encrypted. The result
* is stored in <code>out</code>, starting at <code>outOfs</code>.
*
* @param in the buffer with the input data to be encrypted
......@@ -422,15 +424,18 @@ final class GaloisCounterMode extends FeedbackCipher {
int encrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) {
checkDataLength(processed, len);
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
ArrayUtil.blockSizeCheck(len, blockSize);
processAAD();
if (len > 0) {
ArrayUtil.nullAndBoundsCheck(in, inOfs, len);
ArrayUtil.nullAndBoundsCheck(out, outOfs, len);
gctrPAndC.update(in, inOfs, len, out, outOfs);
processed += len;
ghashAllToS.update(out, outOfs, len);
}
return len;
}
......@@ -450,7 +455,10 @@ final class GaloisCounterMode extends FeedbackCipher {
throw new ShortBufferException
("Can't fit both data and tag into one buffer");
}
if (out.length - outOfs < (len + tagLenBytes)) {
try {
ArrayUtil.nullAndBoundsCheck(out, outOfs,
(len + tagLenBytes));
} catch (ArrayIndexOutOfBoundsException aiobe) {
throw new ShortBufferException("Output buffer too small");
}
......@@ -458,6 +466,8 @@ final class GaloisCounterMode extends FeedbackCipher {
processAAD();
if (len > 0) {
ArrayUtil.nullAndBoundsCheck(in, inOfs, len);
doLastBlock(in, inOfs, len, out, outOfs, true);
}
......@@ -493,15 +503,14 @@ final class GaloisCounterMode extends FeedbackCipher {
int decrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) {
checkDataLength(ibuffer.size(), len);
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
ArrayUtil.blockSizeCheck(len, blockSize);
processAAD();
if (len > 0) {
// store internally until decryptFinal is called because
// spec mentioned that only return recovered data after tag
// is successfully verified
ArrayUtil.nullAndBoundsCheck(in, inOfs, len);
ibuffer.write(in, inOfs, len);
}
return 0;
......@@ -530,22 +539,28 @@ final class GaloisCounterMode extends FeedbackCipher {
if (len < tagLenBytes) {
throw new AEADBadTagException("Input too short - need tag");
}
// do this check here can also catch the potential integer overflow
// scenario for the subsequent output buffer capacity check.
checkDataLength(ibuffer.size(), (len - tagLenBytes));
if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) {
try {
ArrayUtil.nullAndBoundsCheck(out, outOfs,
(ibuffer.size() + len) - tagLenBytes);
} catch (ArrayIndexOutOfBoundsException aiobe) {
throw new ShortBufferException("Output buffer too small");
}
processAAD();
ArrayUtil.nullAndBoundsCheck(in, inOfs, len);
// get the trailing tag bytes from 'in'
byte[] tag = new byte[tagLenBytes];
System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);
len -= tagLenBytes;
if (len != 0) {
if (len > 0) {
ibuffer.write(in, inOfs, len);
}
......
/*
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2018, 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 com.sun.crypto.provider;
import java.security.InvalidKeyException;
import java.security.ProviderException;
import sun.security.util.ArrayUtil;
/**
* This class represents ciphers in output-feedback (OFB) mode.
......@@ -148,10 +149,10 @@ final class OutputFeedback extends FeedbackCipher {
*/
int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) {
ArrayUtil.blockSizeCheck(plainLen, numBytes);
ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
if ((plainLen % numBytes) != 0) {
throw new ProviderException("Internal error in input buffering");
}
int nShift = blockSize - numBytes;
int loopCount = plainLen / numBytes;
......@@ -189,6 +190,9 @@ final class OutputFeedback extends FeedbackCipher {
*/
int encryptFinal(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) {
ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
int oddBytes = plainLen % numBytes;
int len = encrypt(plain, plainOffset, (plainLen - oddBytes),
cipher, cipherOffset);
......
/*
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2018, 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 com.sun.crypto.provider;
import java.security.InvalidKeyException;
import java.security.ProviderException;
import sun.security.util.ArrayUtil;
/**
......@@ -136,9 +137,10 @@ final class PCBC extends FeedbackCipher {
int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
{
if ((plainLen % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
ArrayUtil.blockSizeCheck(plainLen, blockSize);
ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
int i;
int endIndex = plainOffset + plainLen;
......@@ -176,9 +178,10 @@ final class PCBC extends FeedbackCipher {
int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
{
if ((cipherLen % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
ArrayUtil.blockSizeCheck(cipherLen, blockSize);
ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, cipherLen);
ArrayUtil.nullAndBoundsCheck(plain, plainOffset, cipherLen);
int i;
int endIndex = cipherOffset + cipherLen;
......
/*
* Copyright (c) 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.util;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Utility methods to check if state or arguments are correct.
*
*/
public class Preconditions {
/**
* Maps out-of-bounds values to a runtime exception.
*
* @param checkKind the kind of bounds check, whose name may correspond
* to the name of one of the range check methods, checkIndex,
* checkFromToIndex, checkFromIndexSize
* @param args the out-of-bounds arguments that failed the range check.
* If the checkKind corresponds a the name of a range check method
* then the bounds arguments are those that can be passed in order
* to the method.
* @param oobef the exception formatter that when applied with a checkKind
* and a list out-of-bounds arguments returns a runtime exception.
* If {@code null} then, it is as if an exception formatter was
* supplied that returns {@link IndexOutOfBoundsException} for any
* given arguments.
* @return the runtime exception
*/
private static RuntimeException outOfBounds(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobef,
String checkKind,
Integer... args) {
List<Integer> largs = Collections.unmodifiableList(Arrays.asList(args));
RuntimeException e = oobef == null
? null : oobef.apply(checkKind, largs);
return e == null
? new IndexOutOfBoundsException(outOfBoundsMessage(checkKind, largs)) : e;
}
private static RuntimeException outOfBoundsCheckIndex(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobe,
int index, int length) {
return outOfBounds(oobe, "checkIndex", index, length);
}
private static RuntimeException outOfBoundsCheckFromToIndex(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobe,
int fromIndex, int toIndex, int length) {
return outOfBounds(oobe, "checkFromToIndex", fromIndex, toIndex, length);
}
private static RuntimeException outOfBoundsCheckFromIndexSize(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobe,
int fromIndex, int size, int length) {
return outOfBounds(oobe, "checkFromIndexSize", fromIndex, size, length);
}
/**
* Returns an out-of-bounds exception formatter from an given exception
* factory. The exception formatter is a function that formats an
* out-of-bounds message from its arguments and applies that message to the
* given exception factory to produce and relay an exception.
*
* <p>The exception formatter accepts two arguments: a {@code String}
* describing the out-of-bounds range check that failed, referred to as the
* <em>check kind</em>; and a {@code List<Integer>} containing the
* out-of-bound integer values that failed the check. The list of
* out-of-bound values is not modified.
*
* <p>Three check kinds are supported {@code checkIndex},
* {@code checkFromToIndex} and {@code checkFromIndexSize} corresponding
* respectively to the specified application of an exception formatter as an
* argument to the out-of-bounds range check methods
* {@link #checkIndex(int, int, BiFunction) checkIndex},
* {@link #checkFromToIndex(int, int, int, BiFunction) checkFromToIndex}, and
* {@link #checkFromIndexSize(int, int, int, BiFunction) checkFromIndexSize}.
* Thus a supported check kind corresponds to a method name and the
* out-of-bound integer values correspond to method argument values, in
* order, preceding the exception formatter argument (similar in many
* respects to the form of arguments required for a reflective invocation of
* such a range check method).
*
* <p>Formatter arguments conforming to such supported check kinds will
* produce specific exception messages describing failed out-of-bounds
* checks. Otherwise, more generic exception messages will be produced in
* any of the following cases: the check kind is supported but fewer
* or more out-of-bounds values are supplied, the check kind is not
* supported, the check kind is {@code null}, or the list of out-of-bound
* values is {@code null}.
*
* @apiNote
* This method produces an out-of-bounds exception formatter that can be
* passed as an argument to any of the supported out-of-bounds range check
* methods declared by {@code Objects}. For example, a formatter producing
* an {@code ArrayIndexOutOfBoundsException} may be produced and stored on a
* {@code static final} field as follows:
* <pre>{@code
* static final
* BiFunction<String, List<Integer>, ArrayIndexOutOfBoundsException> AIOOBEF =
* outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new);
* }</pre>
* The formatter instance {@code AIOOBEF} may be passed as an argument to an
* out-of-bounds range check method, such as checking if an {@code index}
* is within the bounds of a {@code limit}:
* <pre>{@code
* checkIndex(index, limit, AIOOBEF);
* }</pre>
* If the bounds check fails then the range check method will throw an
* {@code ArrayIndexOutOfBoundsException} with an appropriate exception
* message that is a produced from {@code AIOOBEF} as follows:
* <pre>{@code
* AIOOBEF.apply("checkIndex", List.of(index, limit));
* }</pre>
*
* @param f the exception factory, that produces an exception from a message
* where the message is produced and formatted by the returned
* exception formatter. If this factory is stateless and side-effect
* free then so is the returned formatter.
* Exceptions thrown by the factory are relayed to the caller
* of the returned formatter.
* @param <X> the type of runtime exception to be returned by the given
* exception factory and relayed by the exception formatter
* @return the out-of-bounds exception formatter
*/
public static <X extends RuntimeException>
BiFunction<String, List<Integer>, X> outOfBoundsExceptionFormatter(Function<String, X> f) {
// Use anonymous class to avoid bootstrap issues if this method is
// used early in startup
return new BiFunction<String, List<Integer>, X>() {
@Override
public X apply(String checkKind, List<Integer> args) {
return f.apply(outOfBoundsMessage(checkKind, args));
}
};
}
private static String outOfBoundsMessage(String checkKind, List<Integer> args) {
if (checkKind == null && args == null) {
return String.format("Range check failed");
} else if (checkKind == null) {
return String.format("Range check failed: %s", args);
} else if (args == null) {
return String.format("Range check failed: %s", checkKind);
}
int argSize = 0;
switch (checkKind) {
case "checkIndex":
argSize = 2;
break;
case "checkFromToIndex":
case "checkFromIndexSize":
argSize = 3;
break;
default:
}
// Switch to default if fewer or more arguments than required are supplied
switch ((args.size() != argSize) ? "" : checkKind) {
case "checkIndex":
return String.format("Index %d out-of-bounds for length %d",
args.get(0), args.get(1));
case "checkFromToIndex":
return String.format("Range [%d, %d) out-of-bounds for length %d",
args.get(0), args.get(1), args.get(2));
case "checkFromIndexSize":
return String.format("Range [%d, %<d + %d) out-of-bounds for length %d",
args.get(0), args.get(1), args.get(2));
default:
return String.format("Range check failed: %s %s", checkKind, args);
}
}
/**
* Checks if the {@code index} is within the bounds of the range from
* {@code 0} (inclusive) to {@code length} (exclusive).
*
* <p>The {@code index} is defined to be out-of-bounds if any of the
* following inequalities is true:
* <ul>
* <li>{@code index < 0}</li>
* <li>{@code index >= length}</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* <p>If the {@code index} is out-of-bounds, then a runtime exception is
* thrown that is the result of applying the following arguments to the
* exception formatter: the name of this method, {@code checkIndex};
* and an unmodifiable list integers whose values are, in order, the
* out-of-bounds arguments {@code index} and {@code length}.
*
* @param <X> the type of runtime exception to throw if the arguments are
* out-of-bounds
* @param index the index
* @param length the upper-bound (exclusive) of the range
* @param oobef the exception formatter that when applied with this
* method name and out-of-bounds arguments returns a runtime
* exception. If {@code null} or returns {@code null} then, it is as
* if an exception formatter produced from an invocation of
* {@code outOfBoundsExceptionFormatter(IndexOutOfBounds::new)} is used
* instead (though it may be more efficient).
* Exceptions thrown by the formatter are relayed to the caller.
* @return {@code index} if it is within bounds of the range
* @throws X if the {@code index} is out-of-bounds and the exception
* formatter is non-{@code null}
* @throws IndexOutOfBoundsException if the {@code index} is out-of-bounds
* and the exception formatter is {@code null}
* @since 9
*
* @implNote
* This method is made intrinsic in optimizing compilers to guide them to
* perform unsigned comparisons of the index and length when it is known the
* length is a non-negative value (such as that of an array length or from
* the upper bound of a loop)
*/
public static <X extends RuntimeException>
int checkIndex(int index, int length,
BiFunction<String, List<Integer>, X> oobef) {
if (index < 0 || index >= length)
throw outOfBoundsCheckIndex(oobef, index, length);
return index;
}
/**
* Checks if the sub-range from {@code fromIndex} (inclusive) to
* {@code toIndex} (exclusive) is within the bounds of range from {@code 0}
* (inclusive) to {@code length} (exclusive).
*
* <p>The sub-range is defined to be out-of-bounds if any of the following
* inequalities is true:
* <ul>
* <li>{@code fromIndex < 0}</li>
* <li>{@code fromIndex > toIndex}</li>
* <li>{@code toIndex > length}</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* <p>If the sub-range is out-of-bounds, then a runtime exception is
* thrown that is the result of applying the following arguments to the
* exception formatter: the name of this method, {@code checkFromToIndex};
* and an unmodifiable list integers whose values are, in order, the
* out-of-bounds arguments {@code fromIndex}, {@code toIndex}, and {@code length}.
*
* @param <X> the type of runtime exception to throw if the arguments are
* out-of-bounds
* @param fromIndex the lower-bound (inclusive) of the sub-range
* @param toIndex the upper-bound (exclusive) of the sub-range
* @param length the upper-bound (exclusive) the range
* @param oobef the exception formatter that when applied with this
* method name and out-of-bounds arguments returns a runtime
* exception. If {@code null} or returns {@code null} then, it is as
* if an exception formatter produced from an invocation of
* {@code outOfBoundsExceptionFormatter(IndexOutOfBounds::new)} is used
* instead (though it may be more efficient).
* Exceptions thrown by the formatter are relayed to the caller.
* @return {@code fromIndex} if the sub-range within bounds of the range
* @throws X if the sub-range is out-of-bounds and the exception factory
* function is non-{@code null}
* @throws IndexOutOfBoundsException if the sub-range is out-of-bounds and
* the exception factory function is {@code null}
* @since 9
*/
public static <X extends RuntimeException>
int checkFromToIndex(int fromIndex, int toIndex, int length,
BiFunction<String, List<Integer>, X> oobef) {
if (fromIndex < 0 || fromIndex > toIndex || toIndex > length)
throw outOfBoundsCheckFromToIndex(oobef, fromIndex, toIndex, length);
return fromIndex;
}
/**
* Checks if the sub-range from {@code fromIndex} (inclusive) to
* {@code fromIndex + size} (exclusive) is within the bounds of range from
* {@code 0} (inclusive) to {@code length} (exclusive).
*
* <p>The sub-range is defined to be out-of-bounds if any of the following
* inequalities is true:
* <ul>
* <li>{@code fromIndex < 0}</li>
* <li>{@code size < 0}</li>
* <li>{@code fromIndex + size > length}, taking into account integer overflow</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* <p>If the sub-range is out-of-bounds, then a runtime exception is
* thrown that is the result of applying the following arguments to the
* exception formatter: the name of this method, {@code checkFromIndexSize};
* and an unmodifiable list integers whose values are, in order, the
* out-of-bounds arguments {@code fromIndex}, {@code size}, and
* {@code length}.
*
* @param <X> the type of runtime exception to throw if the arguments are
* out-of-bounds
* @param fromIndex the lower-bound (inclusive) of the sub-interval
* @param size the size of the sub-range
* @param length the upper-bound (exclusive) of the range
* @param oobef the exception formatter that when applied with this
* method name and out-of-bounds arguments returns a runtime
* exception. If {@code null} or returns {@code null} then, it is as
* if an exception formatter produced from an invocation of
* {@code outOfBoundsExceptionFormatter(IndexOutOfBounds::new)} is used
* instead (though it may be more efficient).
* Exceptions thrown by the formatter are relayed to the caller.
* @return {@code fromIndex} if the sub-range within bounds of the range
* @throws X if the sub-range is out-of-bounds and the exception factory
* function is non-{@code null}
* @throws IndexOutOfBoundsException if the sub-range is out-of-bounds and
* the exception factory function is {@code null}
* @since 9
*/
public static <X extends RuntimeException>
int checkFromIndexSize(int fromIndex, int size, int length,
BiFunction<String, List<Integer>, X> oobef) {
if ((length | fromIndex | size) < 0 || size > length - fromIndex)
throw outOfBoundsCheckFromIndexSize(oobef, fromIndex, size, length);
return fromIndex;
}
}
......@@ -25,12 +25,34 @@
package sun.security.util;
import java.util.List;
import java.util.function.BiFunction;
import java.security.*;
import jdk.internal.util.Preconditions;
/**
* This class holds the various utility methods for array range checks.
*/
public final class ArrayUtil {
private static final BiFunction<String, List<Integer>,
ArrayIndexOutOfBoundsException> AIOOBE_SUPPLIER =
Preconditions.outOfBoundsExceptionFormatter
(ArrayIndexOutOfBoundsException::new);
public static void blockSizeCheck(int len, int blockSize) {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
}
public static void nullAndBoundsCheck(byte[] array, int offset, int len) {
// NPE is thrown when array is null
Preconditions.checkFromIndexSize(offset, len, array.length, AIOOBE_SUPPLIER);
}
private static void swap(byte[] arr, int i, int j) {
byte tmp = arr[i];
arr[i] = arr[j];
......@@ -48,4 +70,3 @@ public final class ArrayUtil {
}
}
}
/*
* Copyright (c) 2015, 2016 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
* @summary Objects.checkIndex/jdk.internal.util.Preconditions.checkIndex tests
* @run testng CheckIndex
* @bug 8135248 8142493 8155794
* @modules java.base/jdk.internal.util
*/
import jdk.internal.util.Preconditions;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.IntSupplier;
import static org.testng.Assert.*;
public class CheckIndex {
static class AssertingOutOfBoundsException extends RuntimeException {
public AssertingOutOfBoundsException(String message) {
super(message);
}
}
static BiFunction<String, List<Integer>, AssertingOutOfBoundsException> assertingOutOfBounds(
String message, String expCheckKind, Integer... expArgs) {
return (checkKind, args) -> {
assertEquals(checkKind, expCheckKind);
assertEquals(args, Collections.unmodifiableList(Arrays.asList(expArgs)));
try {
args.clear();
fail("Out of bounds List<Integer> argument should be unmodifiable");
} catch (Exception e) {
}
return new AssertingOutOfBoundsException(message);
};
}
static BiFunction<String, List<Integer>, AssertingOutOfBoundsException> assertingOutOfBoundsReturnNull(
String expCheckKind, Integer... expArgs) {
return (checkKind, args) -> {
assertEquals(checkKind, expCheckKind);
assertEquals(args, Collections.unmodifiableList(Arrays.asList(expArgs)));
return null;
};
}
static final int[] VALUES = {0, 1, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, -1, Integer.MIN_VALUE + 1, Integer.MIN_VALUE};
@DataProvider
static Object[][] checkIndexProvider() {
List<Object[]> l = new ArrayList<>();
for (int index : VALUES) {
for (int length : VALUES) {
boolean withinBounds = index >= 0 &&
length >= 0 &&
index < length;
l.add(new Object[]{index, length, withinBounds});
}
}
return l.toArray(new Object[0][0]);
}
interface X {
int apply(int a, int b, int c);
}
@Test(dataProvider = "checkIndexProvider")
public void testCheckIndex(int index, int length, boolean withinBounds) {
List<Integer> list = Collections.unmodifiableList(Arrays.asList(new Integer[] { index, length }));
String expectedMessage = withinBounds
? null
: Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new).
apply("checkIndex", list).getMessage();
BiConsumer<Class<? extends RuntimeException>, IntSupplier> checker = (ec, s) -> {
try {
int rIndex = s.getAsInt();
if (!withinBounds)
fail(String.format(
"Index %d is out of bounds of [0, %d), but was reported to be within bounds", index, length));
assertEquals(rIndex, index);
}
catch (RuntimeException e) {
assertTrue(ec.isInstance(e));
if (withinBounds)
fail(String.format(
"Index %d is within bounds of [0, %d), but was reported to be out of bounds", index, length));
else
assertEquals(e.getMessage(), expectedMessage);
}
};
checker.accept(AssertingOutOfBoundsException.class,
() -> Preconditions.checkIndex(index, length,
assertingOutOfBounds(expectedMessage, "checkIndex", index, length)));
checker.accept(IndexOutOfBoundsException.class,
() -> Preconditions.checkIndex(index, length,
assertingOutOfBoundsReturnNull("checkIndex", index, length)));
checker.accept(IndexOutOfBoundsException.class,
() -> Preconditions.checkIndex(index, length, null));
checker.accept(ArrayIndexOutOfBoundsException.class,
() -> Preconditions.checkIndex(index, length,
Preconditions.outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new)));
checker.accept(StringIndexOutOfBoundsException.class,
() -> Preconditions.checkIndex(index, length,
Preconditions.outOfBoundsExceptionFormatter(StringIndexOutOfBoundsException::new)));
}
@DataProvider
static Object[][] checkFromToIndexProvider() {
List<Object[]> l = new ArrayList<>();
for (int fromIndex : VALUES) {
for (int toIndex : VALUES) {
for (int length : VALUES) {
boolean withinBounds = fromIndex >= 0 &&
toIndex >= 0 &&
length >= 0 &&
fromIndex <= toIndex &&
toIndex <= length;
l.add(new Object[]{fromIndex, toIndex, length, withinBounds});
}
}
}
return l.toArray(new Object[0][0]);
}
@Test(dataProvider = "checkFromToIndexProvider")
public void testCheckFromToIndex(int fromIndex, int toIndex, int length, boolean withinBounds) {
List<Integer> list = Collections.unmodifiableList(Arrays.asList(new Integer[] { fromIndex, toIndex, length }));
String expectedMessage = withinBounds
? null
: Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new).
apply("checkFromToIndex", list).getMessage();
BiConsumer<Class<? extends RuntimeException>, IntSupplier> check = (ec, s) -> {
try {
int rIndex = s.getAsInt();
if (!withinBounds)
fail(String.format(
"Range [%d, %d) is out of bounds of [0, %d), but was reported to be withing bounds", fromIndex, toIndex, length));
assertEquals(rIndex, fromIndex);
}
catch (RuntimeException e) {
assertTrue(ec.isInstance(e));
if (withinBounds)
fail(String.format(
"Range [%d, %d) is within bounds of [0, %d), but was reported to be out of bounds", fromIndex, toIndex, length));
else
assertEquals(e.getMessage(), expectedMessage);
}
};
check.accept(AssertingOutOfBoundsException.class,
() -> Preconditions.checkFromToIndex(fromIndex, toIndex, length,
assertingOutOfBounds(expectedMessage, "checkFromToIndex", fromIndex, toIndex, length)));
check.accept(IndexOutOfBoundsException.class,
() -> Preconditions.checkFromToIndex(fromIndex, toIndex, length,
assertingOutOfBoundsReturnNull("checkFromToIndex", fromIndex, toIndex, length)));
check.accept(IndexOutOfBoundsException.class,
() -> Preconditions.checkFromToIndex(fromIndex, toIndex, length, null));
check.accept(ArrayIndexOutOfBoundsException.class,
() -> Preconditions.checkFromToIndex(fromIndex, toIndex, length,
Preconditions.outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new)));
check.accept(StringIndexOutOfBoundsException.class,
() -> Preconditions.checkFromToIndex(fromIndex, toIndex, length,
Preconditions.outOfBoundsExceptionFormatter(StringIndexOutOfBoundsException::new)));
}
@DataProvider
static Object[][] checkFromIndexSizeProvider() {
List<Object[]> l = new ArrayList<>();
for (int fromIndex : VALUES) {
for (int size : VALUES) {
for (int length : VALUES) {
// Explicitly convert to long
long lFromIndex = fromIndex;
long lSize = size;
long lLength = length;
// Avoid overflow
long lToIndex = lFromIndex + lSize;
boolean withinBounds = lFromIndex >= 0L &&
lSize >= 0L &&
lLength >= 0L &&
lFromIndex <= lToIndex &&
lToIndex <= lLength;
l.add(new Object[]{fromIndex, size, length, withinBounds});
}
}
}
return l.toArray(new Object[0][0]);
}
@Test(dataProvider = "checkFromIndexSizeProvider")
public void testCheckFromIndexSize(int fromIndex, int size, int length, boolean withinBounds) {
List<Integer> list = Collections.unmodifiableList(Arrays.asList(new Integer[] { fromIndex, size, length }));
String expectedMessage = withinBounds
? null
: Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new).
apply("checkFromIndexSize", list).getMessage();
BiConsumer<Class<? extends RuntimeException>, IntSupplier> check = (ec, s) -> {
try {
int rIndex = s.getAsInt();
if (!withinBounds)
fail(String.format(
"Range [%d, %d + %d) is out of bounds of [0, %d), but was reported to be withing bounds", fromIndex, fromIndex, size, length));
assertEquals(rIndex, fromIndex);
}
catch (RuntimeException e) {
assertTrue(ec.isInstance(e));
if (withinBounds)
fail(String.format(
"Range [%d, %d + %d) is within bounds of [0, %d), but was reported to be out of bounds", fromIndex, fromIndex, size, length));
else
assertEquals(e.getMessage(), expectedMessage);
}
};
check.accept(AssertingOutOfBoundsException.class,
() -> Preconditions.checkFromIndexSize(fromIndex, size, length,
assertingOutOfBounds(expectedMessage, "checkFromIndexSize", fromIndex, size, length)));
check.accept(IndexOutOfBoundsException.class,
() -> Preconditions.checkFromIndexSize(fromIndex, size, length,
assertingOutOfBoundsReturnNull("checkFromIndexSize", fromIndex, size, length)));
check.accept(IndexOutOfBoundsException.class,
() -> Preconditions.checkFromIndexSize(fromIndex, size, length, null));
check.accept(ArrayIndexOutOfBoundsException.class,
() -> Preconditions.checkFromIndexSize(fromIndex, size, length,
Preconditions.outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new)));
check.accept(StringIndexOutOfBoundsException.class,
() -> Preconditions.checkFromIndexSize(fromIndex, size, length,
Preconditions.outOfBoundsExceptionFormatter(StringIndexOutOfBoundsException::new)));
}
@Test
public void uniqueMessagesForCheckKinds() {
BiFunction<String, List<Integer>, IndexOutOfBoundsException> f =
Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new);
List<String> messages = new ArrayList<>();
List<Integer> arg1 = Collections.unmodifiableList(Arrays.asList(new Integer[] { -1 }));
List<Integer> arg2 = Collections.unmodifiableList(Arrays.asList(new Integer[] { -1, 0 }));
List<Integer> arg3 = Collections.unmodifiableList(Arrays.asList(new Integer[] { -1, 0, 0 }));
List<Integer> arg4 = Collections.unmodifiableList(Arrays.asList(new Integer[] { -1, 0, 0, 0 }));
// Exact arguments
messages.add(f.apply("checkIndex", arg2).getMessage());
messages.add(f.apply("checkFromToIndex", arg3).getMessage());
messages.add(f.apply("checkFromIndexSize", arg3).getMessage());
// Unknown check kind
messages.add(f.apply("checkUnknown", arg3).getMessage());
// Known check kind with more arguments
messages.add(f.apply("checkIndex", arg3).getMessage());
messages.add(f.apply("checkFromToIndex", arg4).getMessage());
messages.add(f.apply("checkFromIndexSize", arg4).getMessage());
// Known check kind with fewer arguments
messages.add(f.apply("checkIndex", arg1).getMessage());
messages.add(f.apply("checkFromToIndex", arg2).getMessage());
messages.add(f.apply("checkFromIndexSize", arg2).getMessage());
// Null arguments
messages.add(f.apply(null, null).getMessage());
messages.add(f.apply("checkNullArguments", null).getMessage());
messages.add(f.apply(null, arg1).getMessage());
assertEquals(messages.size(), messages.stream().distinct().count());
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册