You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by bo...@apache.org on 2013/01/22 13:45:25 UTC
svn commit: r1436879 - in /commons/proper/compress/trunk/src: changes/
main/java/org/apache/commons/compress/archivers/zip/
test/java/org/apache/commons/compress/archivers/
test/java/org/apache/commons/compress/archivers/zip/ test/resources/
Author: bodewig
Date: Tue Jan 22 12:45:24 2013
New Revision: 1436879
URL: http://svn.apache.org/viewvc?rev=1436879&view=rev
Log:
consume remainder of ZIP when last LF-entry has been read
Added:
commons/proper/compress/trunk/src/test/resources/archive_with_trailer.zip
- copied, changed from r1435891, commons/proper/compress/trunk/src/test/resources/bla.zip
Modified:
commons/proper/compress/trunk/src/changes/changes.xml
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java
Modified: commons/proper/compress/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/changes/changes.xml?rev=1436879&r1=1436878&r2=1436879&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/changes/changes.xml (original)
+++ commons/proper/compress/trunk/src/changes/changes.xml Tue Jan 22 12:45:24 2013
@@ -143,6 +143,10 @@ The <action> type attribute can be add,u
original input stream when it reaches the end of the
archive.
</action>
+ <action type="fix" date="2013-01-22">
+ ZipArchiveInputStream now consumes the remainder of the
+ archive when getNextZipEntry returns null.
+ </action>
</release>
<release version="1.4.1" date="2012-05-23"
description="Release 1.4.1">
@@ -150,7 +154,6 @@ The <action> type attribute can be add,u
Ported libbzip2's fallback sort algorithm to
BZip2CompressorOutputStream to speed up compression in certain
edge cases.
-
Using specially crafted inputs this can be used as a denial
of service attack. See the security reports page for details.
</action>
Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java?rev=1436879&r1=1436878&r2=1436879&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java (original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java Tue Jan 22 12:45:24 2013
@@ -126,14 +126,38 @@ public class ZipArchiveInputStream exten
extra field length 2 bytes
*/
+ private static final int CFH_LEN = 46;
+ /*
+ central file header signature 4 bytes (0x02014b50)
+ version made by 2 bytes
+ version needed to extract 2 bytes
+ general purpose bit flag 2 bytes
+ compression method 2 bytes
+ last mod file time 2 bytes
+ last mod file date 2 bytes
+ crc-32 4 bytes
+ compressed size 4 bytes
+ uncompressed size 4 bytes
+ file name length 2 bytes
+ extra field length 2 bytes
+ file comment length 2 bytes
+ disk number start 2 bytes
+ internal file attributes 2 bytes
+ external file attributes 4 bytes
+ relative offset of local header 4 bytes
+ */
+
private static final long TWO_EXP_32 = ZIP64_MAGIC + 1;
// cached buffers - must only be used locally in the class (COMPRESS-172 - reduce garbage collection)
private final byte[] LFH_BUF = new byte[LFH_LEN];
private final byte[] SKIP_BUF = new byte[1024];
+ private final byte[] SHORT_BUF = new byte[SHORT];
private final byte[] WORD_BUF = new byte[WORD];
private final byte[] TWO_DWORD_BUF = new byte[2 * DWORD];
+ private int entriesRead = 0;
+
public ZipArchiveInputStream(InputStream inputStream) {
this(inputStream, ZipEncodingHelper.UTF8);
}
@@ -204,9 +228,9 @@ public class ZipArchiveInputStream exten
}
ZipLong sig = new ZipLong(LFH_BUF);
- if (sig.equals(ZipLong.CFH_SIG)) {
+ if (sig.equals(ZipLong.CFH_SIG) || sig.equals(ZipLong.AED_SIG)) {
hitCentralDirectory = true;
- return null;
+ skipRemainderOfArchive();
}
if (!sig.equals(ZipLong.LFH_SIG)) {
return null;
@@ -271,6 +295,7 @@ public class ZipArchiveInputStream exten
}
processZip64Extra(size, cSize);
+ entriesRead++;
return current.entry;
}
@@ -834,6 +859,122 @@ public class ZipArchiveInputStream exten
pushedBackBytes(length);
}
+ // End of Central Directory Record
+ // end of central dir signature 4 bytes (0x06054b50)
+ // number of this disk 2 bytes
+ // number of the disk with the
+ // start of the central directory 2 bytes
+ // total number of entries in the
+ // central directory on this disk 2 bytes
+ // total number of entries in
+ // the central directory 2 bytes
+ // size of the central directory 4 bytes
+ // offset of start of central
+ // directory with respect to
+ // the starting disk number 4 bytes
+ // .ZIP file comment length 2 bytes
+ // .ZIP file comment (variable size)
+ //
+
+ /**
+ * Reads the stream until it find the "End of central directory
+ * record" and consumes it as well.
+ */
+ private void skipRemainderOfArchive() throws IOException {
+ // skip over central directory. One LFH has been read too much
+ // already. The calculation discounts file names and extra
+ // data so it will be too short.
+ realSkip(entriesRead * CFH_LEN - LFH_LEN);
+ findEocdRecord();
+ realSkip(ZipFile.MIN_EOCD_SIZE
+ - WORD /* signature */ - SHORT /* comment len */);
+ readFully(SHORT_BUF);
+ // file comment
+ realSkip(ZipShort.getValue(SHORT_BUF));
+ }
+
+ /**
+ * Reads forward until the signature of the "End of central
+ * directory" recod is found.
+ */
+ private void findEocdRecord() throws IOException {
+ int currentByte = -1;
+ boolean skipReadCall = false;
+ while (skipReadCall || (currentByte = readOneByte()) > -1) {
+ skipReadCall = false;
+ if (!isFirstByteOfEocdSig(currentByte)) {
+ continue;
+ }
+ currentByte = readOneByte();
+ if (currentByte != ZipArchiveOutputStream.EOCD_SIG[1]) {
+ if (currentByte == -1) {
+ break;
+ }
+ skipReadCall = isFirstByteOfEocdSig(currentByte);
+ continue;
+ }
+ currentByte = readOneByte();
+ if (currentByte != ZipArchiveOutputStream.EOCD_SIG[2]) {
+ if (currentByte == -1) {
+ break;
+ }
+ skipReadCall = isFirstByteOfEocdSig(currentByte);
+ continue;
+ }
+ currentByte = readOneByte();
+ if (currentByte == -1
+ || currentByte == ZipArchiveOutputStream.EOCD_SIG[3]) {
+ break;
+ }
+ skipReadCall = isFirstByteOfEocdSig(currentByte);
+ }
+ }
+
+ /**
+ * Skips bytes by reading from the underlying stream rather than
+ * the (potentially inflating) archive stream - which {@link
+ * #skip} would do.
+ *
+ * Also updates bytes-read counter.
+ */
+ private void realSkip(long value) throws IOException {
+ if (value >= 0) {
+ long skipped = 0;
+ while (skipped < value) {
+ long rem = value - skipped;
+ int x = in.read(SKIP_BUF, 0,
+ (int) (SKIP_BUF.length > rem ? rem
+ : SKIP_BUF.length));
+ if (x == -1) {
+ return;
+ }
+ count(x);
+ skipped += x;
+ }
+ return;
+ }
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Reads bytes by reading from the underlying stream rather than
+ * the (potentially inflating) archive stream - which {@link
+ * #read} would do.
+ *
+ * Also updates bytes-read counter.
+ */
+ private int readOneByte() throws IOException {
+ int b = in.read();
+ if (b != -1) {
+ count(1);
+ }
+ return b;
+ }
+
+ private boolean isFirstByteOfEocdSig(int b) {
+ return b == ZipArchiveOutputStream.EOCD_SIG[0];
+ }
+
/**
* Structure collecting information for the entry that is
* currently being read.
Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java?rev=1436879&r1=1436878&r2=1436879&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java (original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java Tue Jan 22 12:45:24 2013
@@ -571,7 +571,7 @@ public class ZipFile {
* supposed to be the last structure of the archive - without file
* comment.
*/
- private static final int MIN_EOCD_SIZE =
+ static final int MIN_EOCD_SIZE =
/* end of central dir signature */ WORD
/* number of this disk */ + SHORT
/* number of the disk with the */
@@ -685,6 +685,9 @@ public class ZipFile {
* finds the "Zip64 end of central directory record" using the
* parsed information, parses that and positions the stream at the
* first central directory record.
+ *
+ * Expects stream to be positioned right behind the "Zip64
+ * end of central directory locator"'s signature.
*/
private void positionAtCentralDirectory64()
throws IOException {
@@ -704,9 +707,11 @@ public class ZipFile {
}
/**
- * Searches for the "End of central dir record", parses
- * it and positions the stream at the first central directory
- * record.
+ * Parses the "End of central dir record" and positions
+ * the stream at the first central directory record.
+ *
+ * Expects stream to be positioned at the beginning of the
+ * "End of central dir record".
*/
private void positionAtCentralDirectory32()
throws IOException {
Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java?rev=1436879&r1=1436878&r2=1436879&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java (original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java Tue Jan 22 12:45:24 2013
@@ -81,6 +81,12 @@ public final class ZipLong implements Cl
new ZipLong(0X30304B50L);
/**
+ * Archive extra data record signature.</p>
+ * @since 1.5
+ */
+ public static final ZipLong AED_SIG = new ZipLong(0X08064B50L);
+
+ /**
* Create instance from a number.
* @param value the long to store as a ZipLong
*/
Modified: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java?rev=1436879&r1=1436878&r2=1436879&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java (original)
+++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java Tue Jan 22 12:45:24 2013
@@ -167,6 +167,7 @@ public final class ZipTestCase extends A
try {
assertNull(zip.getNextZipEntry());
} catch (IOException e) {
+ e.printStackTrace();
fail("COMPRESS-93: Unable to skip an unsupported zip entry");
}
} finally {
Modified: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java?rev=1436879&r1=1436878&r2=1436879&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java (original)
+++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java Tue Jan 22 12:45:24 2013
@@ -18,12 +18,14 @@
package org.apache.commons.compress.archivers.zip;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.InputStream;
import java.net.URI;
import java.net.URL;
@@ -79,4 +81,20 @@ public class ZipArchiveInputStreamTest {
}
}
+ @Test
+ public void shouldConsumeArchiveCompletely() throws Exception {
+ InputStream is = ZipArchiveInputStreamTest.class
+ .getResourceAsStream("/archive_with_trailer.zip");
+ ZipArchiveInputStream zip = new ZipArchiveInputStream(is);
+ while (zip.getNextZipEntry() != null) {
+ // just consume the archive
+ ;
+ }
+ byte[] expected = new byte[] {
+ 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n'
+ };
+ byte[] actual = new byte[expected.length];
+ is.read(actual);
+ assertArrayEquals(expected, actual);
+ }
}
Copied: commons/proper/compress/trunk/src/test/resources/archive_with_trailer.zip (from r1435891, commons/proper/compress/trunk/src/test/resources/bla.zip)
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/resources/archive_with_trailer.zip?p2=commons/proper/compress/trunk/src/test/resources/archive_with_trailer.zip&p1=commons/proper/compress/trunk/src/test/resources/bla.zip&r1=1435891&r2=1436879&rev=1436879&view=diff
==============================================================================
Binary files - no diff available.