8007379: Base64.getMimeDecoder().decode() throws IAE for a non-base64 character after padding
8008925: Base64.getMimeDecoder().decode() does not ignore padding chars
Summary: updated implementation and spec for corner cases.
Reviewed-by: alanb
--- a/jdk/src/share/classes/java/util/Base64.java Tue Apr 02 10:32:21 2013 +0200
+++ b/jdk/src/share/classes/java/util/Base64.java Tue Apr 02 10:12:20 2013 -0700
@@ -620,7 +620,10 @@
* required. So if the final unit of the encoded byte data only has
* two or three Base64 characters (without the corresponding padding
* character(s) padded), they are decoded as if followed by padding
- * character(s).
+ * character(s). If there is padding character present in the
+ * final unit, the correct number of padding character(s) must be
+ * present, otherwise {@code IllegalArgumentException} is thrown
+ * during decoding.
*
* <p> Instances of {@link Decoder} class are safe for use by
* multiple concurrent threads.
@@ -1034,23 +1037,26 @@
throw new IllegalArgumentException(
"Input byte[] should at least have 2 bytes for base64 bytes");
}
- if (src[sl - 1] == '=') {
- paddings++;
- if (src[sl - 2] == '=')
- paddings++;
- }
if (isMIME) {
// scan all bytes to fill out all non-alphabet. a performance
// trade-off of pre-scan or Arrays.copyOf
int n = 0;
while (sp < sl) {
int b = src[sp++] & 0xff;
- if (b == '=')
+ if (b == '=') {
+ len -= (sl - sp + 1);
break;
+ }
if ((b = base64[b]) == -1)
n++;
}
len -= n;
+ } else {
+ if (src[sl - 1] == '=') {
+ paddings++;
+ if (src[sl - 2] == '=')
+ paddings++;
+ }
}
if (paddings == 0 && (len & 0x3) != 0)
paddings = 4 - (len & 0x3);
--- a/jdk/test/java/util/Base64/TestBase64.java Tue Apr 02 10:32:21 2013 +0200
+++ b/jdk/test/java/util/Base64/TestBase64.java Tue Apr 02 10:12:20 2013 -0700
@@ -22,7 +22,7 @@
*/
/**
- * @test 4235519 8004212 8005394 8007298 8006295 8006315 8006530
+ * @test 4235519 8004212 8005394 8007298 8006295 8006315 8006530 8007379 8008925
* @summary tests java.util.Base64
*/
@@ -107,6 +107,9 @@
checkIAE(new Runnable() { public void run() {
Base64.getDecoder().decode(ByteBuffer.wrap(decoded), ByteBuffer.allocateDirect(1024)); }});
+ // illegal ending unit
+ checkIAE(new Runnable() { public void run() { Base64.getMimeDecoder().decode("$=#"); }});
+
// test return value from decode(ByteBuffer, ByteBuffer)
testDecBufRet();
@@ -115,7 +118,6 @@
// test decoding of unpadded data
testDecodeUnpadded();
-
// test mime decoding with ignored character after padding
testDecodeIgnoredAfterPadding();
}
@@ -384,6 +386,10 @@
encoded = Arrays.copyOf(encoded, encoded.length + 1);
encoded[encoded.length - 1] = nonBase64;
checkEqual(decM.decode(encoded), src[i], "Non-base64 char is not ignored");
+ byte[] decoded = new byte[src[i].length];
+ decM.decode(encoded, decoded);
+ checkEqual(decoded, src[i], "Non-base64 char is not ignored");
+
try {
dec.decode(encoded);
throw new RuntimeException("No IAE for non-base64 char");