This is an automated email from the ASF dual-hosted git repository. bodewig pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-compress.git
The following commit(s) were added to refs/heads/master by this push: new 1793167 COMPRESS-520 don't rely on extra fields to actually be what you think they are 1793167 is described below commit 1793167ce82c43b8ca15cee53fd96e164345607a Author: Stefan Bodewig <bode...@apache.org> AuthorDate: Sat May 23 20:03:21 2020 +0200 COMPRESS-520 don't rely on extra fields to actually be what you think they are --- src/changes/changes.xml | 5 ++++ .../archivers/zip/ZipArchiveInputStream.java | 10 +++++-- .../archivers/zip/ZipArchiveOutputStream.java | 16 +++++------ .../commons/compress/archivers/zip/ZipFile.java | 8 ++++-- .../archivers/zip/ZipArchiveInputStreamTest.java | 32 ++++++++++++++++++++++ 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index dcad6f0..41f79f6 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -76,6 +76,11 @@ The <action> type attribute can be add,update,fix,remove. <action issue="COMPRESS-517" type="fix" date="2020-05-23"> Improved parsing of X5455_ExtendedTimestamp ZIP extra field. </action> + <action issue="COMPRESS-518" type="fix" date="2020-05-23"> + ZipArchiveInputStream and ZipFile will now throw an + IOException rather than a RuntimeException if the zip64 extra + field of an enty could not be parsed. + </action> </release> <release version="1.20" date="2020-02-08" description="Release 1.20"> diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java index 0f27b14..7d15345 100644 --- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java +++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java @@ -413,10 +413,14 @@ public class ZipArchiveInputStream extends ArchiveInputStream implements InputSt * information from it if sizes are 0xFFFFFFFF and the entry * doesn't use a data descriptor. */ - private void processZip64Extra(final ZipLong size, final ZipLong cSize) { - final Zip64ExtendedInformationExtraField z64 = - (Zip64ExtendedInformationExtraField) + private void processZip64Extra(final ZipLong size, final ZipLong cSize) throws ZipException { + final ZipExtraField extra = current.entry.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID); + if (extra != null && !(extra instanceof Zip64ExtendedInformationExtraField)) { + throw new ZipException("archive contains unparseable zip64 extra field"); + } + final Zip64ExtendedInformationExtraField z64 = + (Zip64ExtendedInformationExtraField) extra; current.usesZip64 = z64 != null; if (!current.hasDataDescriptor) { if (z64 != null // same as current.usesZip64 but avoids NPE warning diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java index 234df74..22f2a54 100644 --- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java +++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java @@ -1135,11 +1135,12 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream { private byte[] createLocalFileHeader(final ZipArchiveEntry ze, final ByteBuffer name, final boolean encodable, final boolean phased, long archiveOffset) { - ResourceAlignmentExtraField oldAlignmentEx = - (ResourceAlignmentExtraField) ze.getExtraField(ResourceAlignmentExtraField.ID); - if (oldAlignmentEx != null) { + ZipExtraField oldEx = ze.getExtraField(ResourceAlignmentExtraField.ID); + if (oldEx != null) { ze.removeExtraField(ResourceAlignmentExtraField.ID); } + ResourceAlignmentExtraField oldAlignmentEx = + oldEx instanceof ResourceAlignmentExtraField ? (ResourceAlignmentExtraField) oldEx : null; int alignment = ze.getAlignment(); if (alignment <= 0 && oldAlignmentEx != null) { @@ -1768,10 +1769,9 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream { entry.causedUseOfZip64 = !hasUsedZip64; } hasUsedZip64 = true; - Zip64ExtendedInformationExtraField z64 = - (Zip64ExtendedInformationExtraField) - ze.getExtraField(Zip64ExtendedInformationExtraField - .HEADER_ID); + ZipExtraField extra = ze.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID); + Zip64ExtendedInformationExtraField z64 = extra instanceof Zip64ExtendedInformationExtraField + ? (Zip64ExtendedInformationExtraField) extra : null; if (z64 == null) { /* System.err.println("Adding z64 for " + ze.getName() @@ -1797,7 +1797,7 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream { private boolean hasZip64Extra(final ZipArchiveEntry ze) { return ze.getExtraField(Zip64ExtendedInformationExtraField .HEADER_ID) - != null; + instanceof Zip64ExtendedInformationExtraField; } /** diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java index 641297d..45862a8 100644 --- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java +++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java @@ -833,9 +833,13 @@ public class ZipFile implements Closeable { */ private void setSizesAndOffsetFromZip64Extra(final ZipArchiveEntry ze) throws IOException { - final Zip64ExtendedInformationExtraField z64 = - (Zip64ExtendedInformationExtraField) + final ZipExtraField extra = ze.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID); + if (extra != null && !(extra instanceof Zip64ExtendedInformationExtraField)) { + throw new ZipException("archive contains unparseable zip64 extra field"); + } + final Zip64ExtendedInformationExtraField z64 = + (Zip64ExtendedInformationExtraField) extra; if (z64 != null) { final boolean hasUncompressedSize = ze.getSize() == ZIP64_MAGIC; final boolean hasCompressedSize = ze.getCompressedSize() == ZIP64_MAGIC; diff --git a/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java index def1463..fb468f1 100644 --- a/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java +++ b/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java @@ -40,6 +40,8 @@ import java.util.Arrays; import java.util.zip.ZipException; import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.ArchiveStreamFactory; import org.apache.commons.compress.utils.IOUtils; import org.junit.Assert; import org.junit.Rule; @@ -677,6 +679,23 @@ public class ZipArchiveInputStreamTest { } } + @Test + /** + * @see https://issues.apache.org/jira/browse/COMPRESS-518 + */ + public void throwsIfZip64ExtraCouldNotBeUnderstood() throws Exception { + thrown.expect(ZipException.class); + fuzzingTest(new int[] { + 0x50, 0x4b, 0x03, 0x04, 0x2e, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0x84, 0xb6, 0xba, 0x46, 0x72, 0xb6, 0xfe, 0x77, 0x63, 0x00, + 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x1c, 0x00, + 0x62, 0x62, 0x62, 0x01, 0x00, 0x09, 0x00, 0x03, 0xe7, 0xce, + 0x64, 0x55, 0xf3, 0xce, 0x64, 0x55, 0x75, 0x78, 0x0b, 0x00, + 0x01, 0x04, 0x5c, 0xf9, 0x01, 0x00, 0x04, 0x88, 0x13, 0x00, + 0x00 + }); + } + private static byte[] readEntry(ZipArchiveInputStream zip, ZipArchiveEntry zae) throws IOException { final int len = (int)zae.getSize(); final byte[] buff = new byte[len]; @@ -700,4 +719,17 @@ public class ZipArchiveInputStreamTest { assertEquals(expected, ze.getNameSource()); } } + + private void fuzzingTest(final int[] bytes) throws Exception { + final int len = bytes.length; + final byte[] input = new byte[len]; + for (int i = 0; i < len; i++) { + input[i] = (byte) bytes[i]; + } + try (ArchiveInputStream ais = new ArchiveStreamFactory() + .createArchiveInputStream("zip", new ByteArrayInputStream(input))) { + ais.getNextEntry(); + IOUtils.toByteArray(ais); + } + } }