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
commit 4eea95853e15e832104118b392a6a1cd0ce05841 Author: Stefan Bodewig <bode...@apache.org> AuthorDate: Fri Jun 11 23:08:42 2021 +0200 ZipFile could use some sanity checks as well --- .../commons/compress/archivers/zip/ZipFile.java | 59 +++++++++++++++++++--- .../commons/compress/archivers/ZipTestCase.java | 4 +- 2 files changed, 54 insertions(+), 9 deletions(-) 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 3ae2ecf..0f2bb53 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 @@ -160,6 +160,9 @@ public class ZipFile implements Closeable { private final ByteBuffer cfhBbuf = ByteBuffer.wrap(cfhBuf); private final ByteBuffer shortBbuf = ByteBuffer.wrap(shortBuf); + private long centralDirectoryStartDiskNumber, centralDirectoryStartRelativeOffset; + private long centralDirectoryStartOffset; + /** * Opens the given file for reading, assuming "UTF8" for file names. * @@ -708,6 +711,7 @@ public class ZipFile implements Closeable { new HashMap<>(); positionAtCentralDirectory(); + centralDirectoryStartOffset = archive.position(); wordBbuf.rewind(); IOUtils.readFully(archive, wordBbuf); @@ -791,12 +795,21 @@ public class ZipFile implements Closeable { final int fileNameLen = ZipShort.getValue(cfhBuf, off); off += SHORT; + if (fileNameLen < 0) { + throw new IOException("broken archive, entry with negative fileNameLen"); + } final int extraLen = ZipShort.getValue(cfhBuf, off); off += SHORT; + if (extraLen < 0) { + throw new IOException("broken archive, entry with negative extraLen"); + } final int commentLen = ZipShort.getValue(cfhBuf, off); off += SHORT; + if (commentLen < 0) { + throw new IOException("broken archive, entry with negative commentLen"); + } ze.setDiskNumberStart(ZipShort.getValue(cfhBuf, off)); off += SHORT; @@ -827,6 +840,7 @@ public class ZipFile implements Closeable { } setSizesAndOffsetFromZip64Extra(ze); + sanityCheckLFHOffset(ze); final byte[] comment = new byte[commentLen]; IOUtils.readFully(archive, ByteBuffer.wrap(comment)); @@ -839,6 +853,28 @@ public class ZipFile implements Closeable { ze.setStreamContiguous(true); } + private void sanityCheckLFHOffset(final ZipArchiveEntry ze) throws IOException { + if (ze.getDiskNumberStart() < 0) { + throw new IOException("broken archive, entry with negative disk number"); + } + if (ze.getLocalHeaderOffset() < 0) { + throw new IOException("broken archive, entry with negative local file header offset"); + } + if (isSplitZipArchive) { + if (ze.getDiskNumberStart() > centralDirectoryStartDiskNumber) { + throw new IOException("local file header for " + ze.getName() + " starts on a later disk than central directory"); + } + if (ze.getDiskNumberStart() == centralDirectoryStartDiskNumber + && ze.getLocalHeaderOffset() > centralDirectoryStartRelativeOffset) { + throw new IOException("local file header for " + ze.getName() + " starts after central directory"); + } + } else { + if (ze.getLocalHeaderOffset() > centralDirectoryStartOffset) { + throw new IOException("local file header for " + ze.getName() + " starts after central directory"); + } + } + } + /** * If the entry holds a Zip64 extended information extra field, * read sizes from there if the entry's sizes are set to @@ -1115,21 +1151,23 @@ public class ZipFile implements Closeable { - WORD /* signature has already been read */); wordBbuf.rewind(); IOUtils.readFully(archive, wordBbuf); - final long diskNumberOfCFD = ZipLong.getValue(wordBuf); + centralDirectoryStartDiskNumber = ZipLong.getValue(wordBuf); skipBytes(ZIP64_EOCD_CFD_LOCATOR_RELATIVE_OFFSET); dwordBbuf.rewind(); IOUtils.readFully(archive, dwordBbuf); - final long relativeOffsetOfCFD = ZipEightByteInteger.getLongValue(dwordBuf); + centralDirectoryStartRelativeOffset = ZipEightByteInteger.getLongValue(dwordBuf); ((ZipSplitReadOnlySeekableByteChannel) archive) - .position(diskNumberOfCFD, relativeOffsetOfCFD); + .position(centralDirectoryStartDiskNumber, centralDirectoryStartRelativeOffset); } else { skipBytes(ZIP64_EOCD_CFD_LOCATOR_OFFSET - WORD /* signature has already been read */); dwordBbuf.rewind(); IOUtils.readFully(archive, dwordBbuf); - archive.position(ZipEightByteInteger.getLongValue(dwordBuf)); + centralDirectoryStartDiskNumber = 0; + centralDirectoryStartRelativeOffset = ZipEightByteInteger.getLongValue(dwordBuf); + archive.position(centralDirectoryStartRelativeOffset); } } @@ -1146,20 +1184,22 @@ public class ZipFile implements Closeable { skipBytes(CFD_DISK_OFFSET); shortBbuf.rewind(); IOUtils.readFully(archive, shortBbuf); - final int diskNumberOfCFD = ZipShort.getValue(shortBuf); + centralDirectoryStartDiskNumber = ZipShort.getValue(shortBuf); skipBytes(CFD_LOCATOR_RELATIVE_OFFSET); wordBbuf.rewind(); IOUtils.readFully(archive, wordBbuf); - final long relativeOffsetOfCFD = ZipLong.getValue(wordBuf); + centralDirectoryStartRelativeOffset = ZipLong.getValue(wordBuf); ((ZipSplitReadOnlySeekableByteChannel) archive) - .position(diskNumberOfCFD, relativeOffsetOfCFD); + .position(centralDirectoryStartDiskNumber, centralDirectoryStartRelativeOffset); } else { skipBytes(CFD_LOCATOR_OFFSET); wordBbuf.rewind(); IOUtils.readFully(archive, wordBbuf); - archive.position(ZipLong.getValue(wordBuf)); + centralDirectoryStartDiskNumber = 0; + centralDirectoryStartRelativeOffset = ZipLong.getValue(wordBuf); + archive.position(centralDirectoryStartRelativeOffset); } } @@ -1313,6 +1353,9 @@ public class ZipFile implements Closeable { final int extraFieldLen = ZipShort.getValue(shortBuf); ze.setDataOffset(offset + LFH_OFFSET_FOR_FILENAME_LENGTH + SHORT + SHORT + fileNameLen + extraFieldLen); + if (ze.getDataOffset() + ze.getCompressedSize() > centralDirectoryStartOffset) { + throw new IOException("data for " + ze.getName() + " overlaps with central directory."); + } return new int[] { fileNameLen, extraFieldLen }; } diff --git a/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java b/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java index 1d92fbd..ec717a1 100644 --- a/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java +++ b/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java @@ -416,7 +416,9 @@ public final class ZipTestCase extends AbstractTestCase { final ZipArchiveEntry archiveEntry = new ZipArchiveEntry("fred"); archiveEntry.setUnixMode(0664); - archiveEntry.setMethod(ZipEntry.DEFLATED); + archiveEntry.setMethod(ZipEntry.STORED); + archiveEntry.setSize(3); + archiveEntry.setCompressedSize(3); zos.addRawArchiveEntry(archiveEntry, new ByteArrayInputStream("fud".getBytes())); }