diff options
author | Elliott Hughes <enh@google.com> | 2013-08-06 14:52:30 -0700 |
---|---|---|
committer | Paul Kocialkowski <contact@paulk.fr> | 2014-11-16 13:53:05 +0100 |
commit | eba634cef22dce6df432c640009df7a4fbbfae16 (patch) | |
tree | 29b8621b27dcb87279326df4e2ad4dacdc5dda96 | |
parent | 71cdd5497ef83c44e587117aa99fdbb2cf3fbf3c (diff) | |
download | libcore-eba634cef22dce6df432c640009df7a4fbbfae16.zip libcore-eba634cef22dce6df432c640009df7a4fbbfae16.tar.gz libcore-eba634cef22dce6df432c640009df7a4fbbfae16.tar.bz2 |
Bumper ZipFile/ZipEntry backport.
Bug: https://code.google.com/p/android/issues/detail?id=58465
Bug: 8219321
Bug: 8476102
Bug: 8617715
Bug: 9695860
Bug: 9950697
Bug: 10148349
Bug: 10227498
Change-Id: I94c3e9664a429c94c336115618a46283a13996e0
-rw-r--r-- | luni/src/main/java/java/util/zip/ZipEntry.java | 13 | ||||
-rw-r--r-- | luni/src/main/java/java/util/zip/ZipFile.java | 20 | ||||
-rw-r--r-- | luni/src/test/java/libcore/java/util/zip/ZipFileTest.java | 66 |
3 files changed, 68 insertions, 31 deletions
diff --git a/luni/src/main/java/java/util/zip/ZipEntry.java b/luni/src/main/java/java/util/zip/ZipEntry.java index 685c1be..ab48625 100644 --- a/luni/src/main/java/java/util/zip/ZipEntry.java +++ b/luni/src/main/java/java/util/zip/ZipEntry.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.ByteOrder; import java.nio.charset.Charsets; +import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -391,6 +392,9 @@ public class ZipEntry implements ZipConstants, Cloneable { byte[] nameBytes = new byte[nameLength]; Streams.readFully(in, nameBytes, 0, nameBytes.length); + if (containsNulByte(nameBytes)) { + throw new ZipException("Filename contains NUL byte: " + Arrays.toString(nameBytes)); + } name = new String(nameBytes, 0, nameBytes.length, Charsets.UTF_8); // The RI has always assumed UTF-8. (If GPBF_UTF8_FLAG isn't set, the encoding is @@ -406,4 +410,13 @@ public class ZipEntry implements ZipConstants, Cloneable { Streams.readFully(in, extra, 0, extraLength); } } + + private static boolean containsNulByte(byte[] bytes) { + for (byte b : bytes) { + if (b == 0) { + return true; + } + } + return false; + } } diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java index 7decdc5..2f2284a 100644 --- a/luni/src/main/java/java/util/zip/ZipFile.java +++ b/luni/src/main/java/java/util/zip/ZipFile.java @@ -269,19 +269,23 @@ public class ZipFile implements ZipConstants { throw new ZipException("Invalid General Purpose Bit Flag: " + gpbf); } - // At position 28 we find the length of the extra data. In some cases - // this length differs from the one coming in the central header. - is.skipBytes(20); - int localExtraLenOrWhatever = Short.reverseBytes(is.readShort()) & 0xffff; + // Offset 26 has the file name length, and offset 28 has the extra field length. + // These lengths can differ from the ones in the central header. + is.skipBytes(18); + int fileNameLength = Short.reverseBytes(is.readShort()) & 0xffff; + int extraFieldLength = Short.reverseBytes(is.readShort()) & 0xffff; is.close(); - // Skip the name and this "extra" data or whatever it is: - rafStream.skip(entry.nameLength + localExtraLenOrWhatever); - rafStream.mLength = rafStream.mOffset + entry.compressedSize; + // Skip the variable-size file name and extra field data. + rafStream.skip(fileNameLength + extraFieldLength); + + // The compressed or stored file data follows immediately after. if (entry.compressionMethod == ZipEntry.DEFLATED) { - int bufSize = Math.max(1024, (int)Math.min(entry.getSize(), 65535L)); + rafStream.mLength = rafStream.mOffset + entry.compressedSize; + int bufSize = Math.max(1024, (int) Math.min(entry.getSize(), 65535L)); return new ZipInflaterInputStream(rafStream, new Inflater(true), bufSize, entry); } else { + rafStream.mLength = rafStream.mOffset + entry.size; return rafStream; } } diff --git a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java index 49dc050..b6d41a7 100644 --- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java +++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java @@ -33,7 +33,6 @@ import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import junit.framework.TestCase; -import libcore.io.IoUtils; public final class ZipFileTest extends TestCase { /** @@ -57,7 +56,7 @@ public final class ZipFileTest extends TestCase { zipFile.close(); } - private static void replaceBytes(byte[] original, byte[] replacement, byte[] buffer) { + private static void replaceBytes(byte[] buffer, byte[] original, byte[] replacement) { // Gotcha here: original and replacement must be the same length assertEquals(original.length, replacement.length); boolean found; @@ -80,37 +79,38 @@ public final class ZipFileTest extends TestCase { } } + private static void writeBytes(File f, byte[] bytes) throws IOException { + FileOutputStream out = new FileOutputStream(f); + out.write(bytes); + out.close(); + } + /** * Make sure we don't fail silently for duplicate entries. * b/8219321 */ - public void testDuplicateEntries() throws IOException { - String entryName = "test_file_name1"; - String tmpName = "test_file_name2"; - - // create the template data - ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); - ZipOutputStream out = new ZipOutputStream(bytesOut); - ZipEntry ze1 = new ZipEntry(tmpName); - out.putNextEntry(ze1); + public void testDuplicateEntries() throws Exception { + String name1 = "test_file_name1"; + String name2 = "test_file_name2"; + + // Create the good zip file. + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ZipOutputStream out = new ZipOutputStream(baos); + out.putNextEntry(new ZipEntry(name2)); out.closeEntry(); - ZipEntry ze2 = new ZipEntry(entryName); - out.putNextEntry(ze2); + out.putNextEntry(new ZipEntry(name1)); out.closeEntry(); out.close(); - // replace the bytes we don't like - byte[] buf = bytesOut.toByteArray(); - replaceBytes(tmpName.getBytes(), entryName.getBytes(), buf); + // Rewrite one of the filenames. + byte[] buffer = baos.toByteArray(); + replaceBytes(buffer, name2.getBytes(), name1.getBytes()); - // write the result to a file - File badZip = File.createTempFile("badzip", "zip"); - badZip.deleteOnExit(); - FileOutputStream outstream = new FileOutputStream(badZip); - outstream.write(buf); - outstream.close(); + // Write the result to a file. + File badZip = createTemporaryZipFile(); + writeBytes(badZip, buffer); - // see if we can still handle it + // Check that we refuse to load the modified file. try { ZipFile bad = new ZipFile(badZip); fail(); @@ -316,6 +316,26 @@ public final class ZipFileTest extends TestCase { return sb.toString(); } + // https://code.google.com/p/android/issues/detail?id=58465 + public void test_NUL_in_filename() throws Exception { + File file = createTemporaryZipFile(); + + // We allow creation of a ZipEntry whose name contains a NUL byte, + // mainly because it's not likely to happen by accident and it's useful for testing. + ZipOutputStream out = createZipOutputStream(file); + out.putNextEntry(new ZipEntry("hello")); + out.putNextEntry(new ZipEntry("hello\u0000")); + out.close(); + + // But you can't open a ZIP file containing such an entry, because we reject it + // when we find it in the central directory. + try { + ZipFile zipFile = new ZipFile(file); + fail(); + } catch (ZipException expected) { + } + } + public void testComment() throws Exception { String expectedFileComment = "1 \u0666 2"; String expectedEntryComment = "a \u0666 b"; |