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 2021/05/15 20:36:33 UTC
[commons-compress] 02/02: COMPRESS-542 add some extra sanity checks
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 c51de6cfaec75b21566374158f25e1734c3a94cb
Author: Stefan Bodewig <st...@innoq.com>
AuthorDate: Sat May 15 22:36:02 2021 +0200
COMPRESS-542 add some extra sanity checks
---
.../compress/archivers/sevenz/SevenZFile.java | 88 ++++++++++++++++++++--
1 file changed, 83 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java b/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
index 288cfd4..a08c02a 100644
--- a/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
+++ b/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
@@ -503,7 +503,7 @@ public class SevenZFile implements Closeable {
startHeader.nextHeaderSize = channel.size() - pos;
final Archive result = initializeArchive(startHeader, password, false);
// Sanity check: There must be some data...
- if (result.packSizes != null && result.files.length > 0) {
+ if (result.packSizes.length > 0 && result.files.length > 0) {
return result;
}
} catch (final Exception ignore) {
@@ -572,6 +572,7 @@ public class SevenZFile implements Closeable {
private void readHeader(final ByteBuffer header, final Archive archive) throws IOException {
final int pos = header.position();
final ArchiveStatistics stats = sanityCheckAndCollectStatistics(header);
+ stats.assertValidity();
header.position(pos);
int nid = getUnsignedByte(header);
@@ -662,10 +663,20 @@ public class SevenZFile implements Closeable {
private ByteBuffer readEncodedHeader(final ByteBuffer header, final Archive archive,
final byte[] password) throws IOException {
final int pos = header.position();
- sanityCheckStreamsInfo(header, new ArchiveStatistics());
+ ArchiveStatistics stats = new ArchiveStatistics();
+ sanityCheckStreamsInfo(header, stats);
+ stats.assertValidity();
header.position(pos);
+
readStreamsInfo(header, archive);
+ if (archive.folders.length == 0) {
+ throw new IOException("no folders, can't read encoded header");
+ }
+ if (archive.packSizes.length == 0) {
+ throw new IOException("no packed streams, can't read encoded header");
+ }
+
// FIXME: merge with buildDecodingStream()/buildDecoderStack() at some stage?
final Folder folder = archive.folders[0];
final int firstPackStreamIndex = 0;
@@ -832,6 +843,12 @@ public class SevenZFile implements Closeable {
numberOfOutputStreamsPerFolder.add(sanityCheckFolder(header, stats));
}
+ final long totalNumberOfBindPairs = stats.numberOfOutStreams - stats.numberOfFolders;
+ final long packedStreamsRequiredByFolders = stats.numberOfInStreams - totalNumberOfBindPairs;
+ if (packedStreamsRequiredByFolders < stats.numberOfPackedStreams) {
+ throw new IOException("archive doesn't contain enough packed streams");
+ }
+
nid = getUnsignedByte(header);
if (nid != NID.kCodersUnpackSize) {
throw new IOException("Expected kCodersUnpackSize, got " + nid);
@@ -1058,6 +1075,7 @@ public class SevenZFile implements Closeable {
throws IOException {
final int numCoders = assertFitsIntoNonNegativeInt("numCoders", readUint64(header));
+ stats.numberOfCoders += numCoders;
long totalOutStreams = 0;
long totalInStreams = 0;
@@ -1094,6 +1112,8 @@ public class SevenZFile implements Closeable {
}
assertFitsIntoNonNegativeInt("totalInStreams", totalInStreams);
assertFitsIntoNonNegativeInt("totalOutStreams", totalOutStreams);
+ stats.numberOfOutStreams += totalOutStreams;
+ stats.numberOfInStreams += totalInStreams;
if (totalOutStreams == 0) {
throw new IOException("Total output streams can't be 0");
@@ -1119,6 +1139,7 @@ public class SevenZFile implements Closeable {
final int numPackedStreams =
assertFitsIntoNonNegativeInt("numPackedStreams", totalInStreams - numBindPairs);
+
if (numPackedStreams == 1) {
if (inStreamsBound.nextClearBit(0) == -1) {
throw new IOException("Couldn't find stream's bind pair index");
@@ -1127,8 +1148,8 @@ public class SevenZFile implements Closeable {
for (int i = 0; i < numPackedStreams; i++) {
final int packedStreamIndex =
assertFitsIntoNonNegativeInt("packedStreamIndex", readUint64(header));
- if (packedStreamIndex >= stats.numberOfPackedStreams) {
- throw new IOException("packedStreamIndex is bigger than number of packed streams");
+ if (packedStreamIndex >= totalInStreams) {
+ throw new IOException("packedStreamIndex is bigger than number of totalInStreams");
}
}
}
@@ -1375,6 +1396,7 @@ public class SevenZFile implements Closeable {
}
}
}
+ stats.numberOfEntriesWithStream = stats.numberOfEntries - (emptyStreams > 0 ? emptyStreams : 0);
}
private void readFilesInfo(final ByteBuffer header, final Archive archive) throws IOException {
@@ -1580,7 +1602,7 @@ public class SevenZFile implements Closeable {
}
long nextPackStreamOffset = 0;
- final int numPackSizes = archive.packSizes != null ? archive.packSizes.length : 0;
+ final int numPackSizes = archive.packSizes.length;
streamMap.packStreamOffsets = new long[numPackSizes];
for (int i = 0; i < numPackSizes; i++) {
streamMap.packStreamOffsets[i] = nextPackStreamOffset;
@@ -2096,8 +2118,64 @@ public class SevenZFile implements Closeable {
private static class ArchiveStatistics {
private long packPos;
private int numberOfPackedStreams;
+ private long numberOfCoders;
+ private long numberOfOutStreams;
+ private long numberOfInStreams;
private int numberOfFolders;
private BitSet folderHasCrc;
private int numberOfEntries;
+ private int numberOfEntriesWithStream;
+
+ @Override
+ public String toString() {
+ return "Archive with " + numberOfEntries + " entries in " + numberOfFolders
+ + " folders. Estimated size " + (estimateSize()/1024l) + " kB.";
+ }
+
+ long estimateSize() {
+ long lowerBound = 16l * numberOfPackedStreams /* packSizes, packCrcs in Archive */
+ + numberOfPackedStreams / 8 /* packCrcsDefined in Archive */
+ + numberOfFolders * folderSize() /* folders in Archive */
+ + numberOfCoders * coderSize() /* coders in Folder */
+ + (numberOfOutStreams - numberOfFolders) * bindPairSize() /* bindPairs in Folder */
+ + 8l * (numberOfInStreams - numberOfOutStreams + numberOfFolders) /* packedStreams in Folder */
+ + 8l * numberOfOutStreams /* unpackSizes in Folder */
+ + numberOfEntries * entrySize() /* files in Archive */
+ + streamMapSize()
+ ;
+ return 2 * lowerBound /* conservative guess */;
+ }
+
+ void assertValidity() throws IOException {
+ if (numberOfEntriesWithStream > 0 && numberOfFolders == 0) {
+ throw new IOException("archive with entries but no folders");
+ }
+ }
+
+ private long folderSize() {
+ return 30; /* nested arrays are accounted for separately */
+ }
+
+ private long coderSize() {
+ return 2 /* methodId is between 1 and four bytes currently, COPY and LZMA2 are the most common with 1 */
+ + 16
+ + 4 /* properties, guess */
+ ;
+ }
+
+ private long bindPairSize() {
+ return 16;
+ }
+
+ private long entrySize() {
+ return 100; /* real size depends on name length, everything without name is about 70 bytes */
+ }
+
+ private long streamMapSize() {
+ return 8 * numberOfFolders /* folderFirstPackStreamIndex, folderFirstFileIndex */
+ + 8 * numberOfPackedStreams /* packStreamOffsets */
+ + 4 * numberOfEntries /* fileFolderIndex */
+ ;
+ }
}
}