diff options
author | Paulo Casanova <pasc@google.com> | 2017-11-28 18:37:47 +0000 |
---|---|---|
committer | TreeHugger Robot <treehugger-gerrit@google.com> | 2017-12-06 14:24:11 +0000 |
commit | 8efe1e8b1b126421aef6998f64f01dd466db07d3 (patch) | |
tree | 32936fb9ce936224b0bc720a2aebca8c4fb46e09 /src/main | |
parent | f19d56d468ba1eeb8542e23766563d38886aae0b (diff) | |
download | apkzlib-8efe1e8b1b126421aef6998f64f01dd466db07d3.tar.gz |
Improve overlapping detection in ZFile.
ZFile now reports when files overlap in a zip. It previously
failed with a VerifyException and now, at least in cases where
the local header can still be read correctly (for example, a
file contained inside another file), it reports a nice error
that allows users to understand what the problem is.
Bug: 69835362
Change-Id: Ib34df31f97805f1d5ec9ba917dce7374ea6a7d22
Test: included
Diffstat (limited to 'src/main')
-rw-r--r-- | src/main/java/com/android/apkzlib/zip/FileUseMap.java | 37 | ||||
-rw-r--r-- | src/main/java/com/android/apkzlib/zip/ZFile.java | 47 |
2 files changed, 83 insertions, 1 deletions
diff --git a/src/main/java/com/android/apkzlib/zip/FileUseMap.java b/src/main/java/com/android/apkzlib/zip/FileUseMap.java index a72a956..8a76878 100644 --- a/src/main/java/com/android/apkzlib/zip/FileUseMap.java +++ b/src/main/java/com/android/apkzlib/zip/FileUseMap.java @@ -538,6 +538,43 @@ class FileUseMap { return map.lower(entry); } + /** + * Obtains the entry that is located after the one provided. + * + * @param entry the map entry to get the next one for; must belong to the map + * @return the entry after the provided one, {@code null} if {@code entry} is the last entry in + * the map + */ + @Nullable + FileUseMapEntry<?> after(@Nonnull FileUseMapEntry<?> entry) { + Preconditions.checkNotNull(entry, "entry == null"); + + return map.higher(entry); + } + + /** + * Obtains the entry at the given offset. + * + * @param offset the offset to look for + * @return the entry found or {@code null} if there is no entry (not even a free one) at the + * given offset + */ + @Nullable + FileUseMapEntry<?> at(long offset) { + Preconditions.checkArgument(offset >= 0, "offset < 0"); + Preconditions.checkArgument(offset < size, "offset >= size"); + + FileUseMapEntry<?> entry = map.floor(FileUseMapEntry.makeFree(offset, offset + 1)); + if (entry == null) { + return null; + } + + Verify.verify(entry.getStart() <= offset); + Verify.verify(entry.getEnd() > offset); + + return entry; + } + @Override public String toString() { StringJoiner j = new StringJoiner(", "); diff --git a/src/main/java/com/android/apkzlib/zip/ZFile.java b/src/main/java/com/android/apkzlib/zip/ZFile.java index 1bdb410..9034f4c 100644 --- a/src/main/java/com/android/apkzlib/zip/ZFile.java +++ b/src/main/java/com/android/apkzlib/zip/ZFile.java @@ -597,7 +597,7 @@ public class ZFile implements Closeable { readCentralDirectory(); /* - * Compute where the last file ends. We will need this to compute thee extra offset. + * Go over all files and create the usage map, verifying there is no overlap in the files. */ long entryEndOffset; long directoryStartOffset; @@ -625,6 +625,51 @@ public class ZFile implements Closeable { * file. */ + Verify.verify(start >= 0, "start < 0"); + Verify.verify(end < map.size(), "end >= map.size()"); + + FileUseMapEntry<?> found = map.at(start); + Verify.verifyNotNull(found); + + // We've got a problem if the found entry is not free or is a free entry but + // doesn't cover the whole file. + if (!found.isFree() || found.getEnd() < end) { + if (found.isFree()) { + found = map.after(found); + Verify.verify(found != null && !found.isFree()); + } + + Object foundEntry = found.getStore(); + Verify.verify(foundEntry != null); + + // Obtains a custom description of an entry. + IOExceptionFunction<StoredEntry, String> describe = + e -> + String.format( + "'%s' (offset: %d, size: %d)", + e.getCentralDirectoryHeader().getName(), + e.getCentralDirectoryHeader().getOffset(), + e.getInFileSize()); + + String overlappingEntryDescription; + if (foundEntry instanceof StoredEntry) { + StoredEntry foundStored = (StoredEntry) foundEntry; + overlappingEntryDescription = describe.apply((StoredEntry) foundEntry); + } else { + overlappingEntryDescription = + "Central Directory / EOCD: " + + found.getStart() + + " - " + + found.getEnd(); + } + + throw new IOException( + "Cannot read entry " + + describe.apply(entry) + + " because it overlaps with " + + overlappingEntryDescription); + } + FileUseMapEntry<StoredEntry> mapEntry = map.add(start, end, entry); entries.put(entry.getCentralDirectoryHeader().getName(), mapEntry); |