# HG changeset patch # User nishjain # Date 1549441639 -19800 # Node ID c6e8196e4b543c717a5fde7fc071004416c01a16 # Parent 7054249afee570915fff25a81b7b2a4368c640f0 8217969: Base64.Decoder.decode methods do not need to throw OOME due to integer overflow 8218265: java/util/Base64/TestEncodingDecodingLength.java failing Reviewed-by: rriggs, naoto diff -r 7054249afee5 -r c6e8196e4b54 src/java.base/share/classes/java/util/Base64.java --- a/src/java.base/share/classes/java/util/Base64.java Tue Feb 05 20:18:00 2019 -0500 +++ b/src/java.base/share/classes/java/util/Base64.java Wed Feb 06 13:57:19 2019 +0530 @@ -251,7 +251,7 @@ * @return length of the encoded bytes, or -1 if the length overflows * */ - private final int outLength(int srclen, boolean throwOOME) { + private final int encodedOutLength(int srclen, boolean throwOOME) { int len = 0; try { if (doPadding) { @@ -286,7 +286,7 @@ * encoded bytes. */ public byte[] encode(byte[] src) { - int len = outLength(src.length, true); // dst array size + int len = encodedOutLength(src.length, true); // dst array size byte[] dst = new byte[len]; int ret = encode0(src, 0, src.length, dst); if (ret != dst.length) @@ -314,7 +314,7 @@ * space for encoding all input bytes. */ public int encode(byte[] src, byte[] dst) { - int len = outLength(src.length, false); // dst array size + int len = encodedOutLength(src.length, false); // dst array size if (dst.length < len || len == -1) throw new IllegalArgumentException( "Output byte array is too small for encoding all input bytes"); @@ -359,7 +359,7 @@ * @return A newly-allocated byte buffer containing the encoded bytes. */ public ByteBuffer encode(ByteBuffer buffer) { - int len = outLength(buffer.remaining(), true); + int len = encodedOutLength(buffer.remaining(), true); byte[] dst = new byte[len]; int ret = 0; if (buffer.hasArray()) { @@ -560,7 +560,7 @@ * if {@code src} is not in valid Base64 scheme */ public byte[] decode(byte[] src) { - byte[] dst = new byte[outLength(src, 0, src.length, true)]; + byte[] dst = new byte[decodedOutLength(src, 0, src.length)]; int ret = decode0(src, 0, src.length, dst); if (ret != dst.length) { dst = Arrays.copyOf(dst, ret); @@ -613,7 +613,7 @@ * does not have enough space for decoding all input bytes. */ public int decode(byte[] src, byte[] dst) { - int len = outLength(src, 0, src.length, false); + int len = decodedOutLength(src, 0, src.length); if (dst.length < len || len == -1) throw new IllegalArgumentException( "Output byte array is too small for decoding all input bytes"); @@ -657,7 +657,7 @@ sp = 0; sl = src.length; } - byte[] dst = new byte[outLength(src, sp, sl, true)]; + byte[] dst = new byte[decodedOutLength(src, sp, sl)]; return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst)); } catch (IllegalArgumentException iae) { buffer.position(pos0); @@ -691,13 +691,11 @@ * @param src the byte array to decode * @param sp the source position * @param sl the source limit - * @param throwOOME if true, throws OutOfMemoryError if the length of - * the decoded bytes overflows; else returns the - * length - * @return length of the decoded bytes, or -1 if the length overflows + * + * @return length of the decoded bytes * */ - private int outLength(byte[] src, int sp, int sl, boolean throwOOME) { + private int decodedOutLength(byte[] src, int sp, int sl) { int[] base64 = isURL ? fromBase64URL : fromBase64; int paddings = 0; int len = sl - sp; @@ -733,18 +731,12 @@ if (paddings == 0 && (len & 0x3) != 0) paddings = 4 - (len & 0x3); - try { - len = Math.multiplyExact(3, (Math.addExact(len, 3) / 4)) - paddings; - } catch (ArithmeticException ex) { - if (throwOOME) { - throw new OutOfMemoryError("Decoded size is too large"); - } else { - // let the caller know that the decoded bytes length - // is too large - len = -1; - } - } - return len; + // If len is near to Integer.MAX_VALUE, (len + 3) + // can possibly overflow, perform this operation as + // long and cast it back to integer when the value comes under + // integer limit. The final value will always be in integer + // limits + return 3 * (int) ((len + 3L) / 4) - paddings; } private int decode0(byte[] src, int sp, int sl, byte[] dst) { diff -r 7054249afee5 -r c6e8196e4b54 test/jdk/java/util/Base64/TestEncodingDecodingLength.java --- a/test/jdk/java/util/Base64/TestEncodingDecodingLength.java Tue Feb 05 20:18:00 2019 -0500 +++ b/test/jdk/java/util/Base64/TestEncodingDecodingLength.java Wed Feb 06 13:57:19 2019 +0530 @@ -22,22 +22,23 @@ */ import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Base64; /** * @test - * @bug 8210583 - * @summary Tests Base64.Encoder.encode/Decoder.decode for the large size - * of resulting bytes which can not be allocated - * @requires os.maxMemory >= 6g - * @run main/othervm -Xms4g -Xmx6g TestEncodingDecodingLength + * @bug 8210583 8217969 8218265 + * @summary Tests Base64.Encoder.encode and Base64.Decoder.decode + * with the large size of input array/buffer + * @requires os.maxMemory >= 8g + * @run main/othervm -Xms6g -Xmx8g TestEncodingDecodingLength * */ public class TestEncodingDecodingLength { public static void main(String[] args) { - int size = Integer.MAX_VALUE - 2; + int size = Integer.MAX_VALUE - 8; byte[] inputBytes = new byte[size]; byte[] outputBytes = new byte[size]; @@ -46,13 +47,15 @@ checkOOM("encode(byte[])", () -> encoder.encode(inputBytes)); checkIAE("encode(byte[] byte[])", () -> encoder.encode(inputBytes, outputBytes)); checkOOM("encodeToString(byte[])", () -> encoder.encodeToString(inputBytes)); - checkOOM("encode(ByteBuffer)", () -> encoder.encode(ByteBuffer.allocate(size))); + checkOOM("encode(ByteBuffer)", () -> encoder.encode(ByteBuffer.wrap(inputBytes))); - // Check decoder with large array length + // Check decoder with large array length, + // should not throw any exception + Arrays.fill(inputBytes, (byte) 86); Base64.Decoder decoder = Base64.getDecoder(); - checkOOM("decode(byte[])", () -> decoder.decode(inputBytes)); - checkIAE("decode(byte[], byte[])", () -> decoder.decode(inputBytes, outputBytes)); - checkOOM("decode(ByteBuffer)", () -> decoder.decode(ByteBuffer.allocate(size))); + decoder.decode(inputBytes); + decoder.decode(inputBytes, outputBytes); + decoder.decode(ByteBuffer.wrap(inputBytes)); } private static final void checkOOM(String methodName, Runnable r) {