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 ef765af COMPRESS-575 extract and harden parsing of GNU stuct_sparse entries ef765af is described below commit ef765af60786caf58be3efcf1fe84620bf4f2872 Author: Stefan Bodewig <bode...@apache.org> AuthorDate: Sat May 1 11:01:13 2021 +0200 COMPRESS-575 extract and harden parsing of GNU stuct_sparse entries I believe the code is wrong and we should drop empty structs even if their offset is non-0 but this makes tests fail in non-obvious ways, will revisit it later. --- .../compress/archivers/tar/TarArchiveEntry.java | 12 ++------- .../archivers/tar/TarArchiveSparseEntry.java | 12 +-------- .../commons/compress/archivers/tar/TarUtils.java | 30 ++++++++++++++++++++++ 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java index 12da7bb..fdeb0de 100644 --- a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java +++ b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java @@ -1577,16 +1577,8 @@ public class TarArchiveEntry implements ArchiveEntry, TarConstants, EntryStreamO offset += OFFSETLEN_GNU; offset += LONGNAMESLEN_GNU; offset += PAD2LEN_GNU; - sparseHeaders = new ArrayList<>(); - for (int i = 0; i < SPARSE_HEADERS_IN_OLDGNU_HEADER; i++) { - final TarArchiveStructSparse sparseHeader = TarUtils.parseSparse(header, - offset + i * (SPARSE_OFFSET_LEN + SPARSE_NUMBYTES_LEN)); - - // some sparse headers are empty, we need to skip these sparse headers - if(sparseHeader.getOffset() > 0 || sparseHeader.getNumbytes() > 0) { - sparseHeaders.add(sparseHeader); - } - } + sparseHeaders = + new ArrayList<>(TarUtils.readSparseStructs(header, offset, SPARSE_HEADERS_IN_OLDGNU_HEADER)); offset += SPARSELEN_GNU; isExtended = TarUtils.parseBoolean(header, offset); offset += ISEXTENDEDLEN_GNU; diff --git a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveSparseEntry.java b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveSparseEntry.java index b738de1..6973194 100644 --- a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveSparseEntry.java +++ b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveSparseEntry.java @@ -57,17 +57,7 @@ public class TarArchiveSparseEntry implements TarConstants { */ public TarArchiveSparseEntry(final byte[] headerBuf) throws IOException { int offset = 0; - sparseHeaders = new ArrayList<>(); - for(int i = 0; i < SPARSE_HEADERS_IN_EXTENSION_HEADER;i++) { - final TarArchiveStructSparse sparseHeader = TarUtils.parseSparse(headerBuf, - offset + i * (SPARSE_OFFSET_LEN + SPARSE_NUMBYTES_LEN)); - - // some sparse headers are empty, we need to skip these sparse headers - if(sparseHeader.getOffset() > 0 || sparseHeader.getNumbytes() > 0) { - sparseHeaders.add(sparseHeader); - } - } - + sparseHeaders = new ArrayList<>(TarUtils.readSparseStructs(headerBuf, 0, SPARSE_HEADERS_IN_EXTENSION_HEADER)); offset += SPARSELEN_GNU_SPARSE; isExtended = TarUtils.parseBoolean(headerBuf, offset); } diff --git a/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java b/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java index ea4741a..fe6fe4d 100644 --- a/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java +++ b/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java @@ -25,6 +25,7 @@ import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -327,6 +328,35 @@ public class TarUtils { } /** + * @since 1.21 + */ + static List<TarArchiveStructSparse> readSparseStructs(final byte[] buffer, final int offset, final int entries) + throws IOException { + final List<TarArchiveStructSparse> sparseHeaders = new ArrayList<>(); + for (int i = 0; i < entries; i++) { + try { + final TarArchiveStructSparse sparseHeader = + parseSparse(buffer, offset + i * (SPARSE_OFFSET_LEN + SPARSE_NUMBYTES_LEN)); + + if (sparseHeader.getOffset() < 0) { + throw new IOException("Corrupted TAR archive, sparse entry with negative offset"); + } + if (sparseHeader.getNumbytes() < 0) { + throw new IOException("Corrupted TAR archive, sparse entry with negative numbytes"); + } + // some sparse headers are empty, we need to skip these sparse headers + if (sparseHeader.getOffset() > 0 || sparseHeader.getNumbytes() > 0) { + sparseHeaders.add(sparseHeader); + } + } catch (IllegalArgumentException ex) { + // thrown internally by parseOctalOrBinary + throw new IOException("Corrupted TAR archive, sparse entry is invalid", ex); + } + } + return Collections.unmodifiableList(sparseHeaders); + } + + /** * Copy a name into a buffer. * Copies characters from the name into the buffer * starting at the specified offset.