summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2013-08-06 14:52:30 -0700
committerPaul Kocialkowski <contact@paulk.fr>2014-11-16 13:53:05 +0100
commiteba634cef22dce6df432c640009df7a4fbbfae16 (patch)
tree29b8621b27dcb87279326df4e2ad4dacdc5dda96
parent71cdd5497ef83c44e587117aa99fdbb2cf3fbf3c (diff)
downloadlibcore-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.java13
-rw-r--r--luni/src/main/java/java/util/zip/ZipFile.java20
-rw-r--r--luni/src/test/java/libcore/java/util/zip/ZipFileTest.java66
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";