summaryrefslogtreecommitdiff
path: root/src/main/java/com
diff options
context:
space:
mode:
authorPaulo Casanova <pasc@google.com>2017-11-28 18:37:47 +0000
committerTreeHugger Robot <treehugger-gerrit@google.com>2017-12-06 14:24:11 +0000
commit8efe1e8b1b126421aef6998f64f01dd466db07d3 (patch)
tree32936fb9ce936224b0bc720a2aebca8c4fb46e09 /src/main/java/com
parentf19d56d468ba1eeb8542e23766563d38886aae0b (diff)
downloadapkzlib-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/java/com')
-rw-r--r--src/main/java/com/android/apkzlib/zip/FileUseMap.java37
-rw-r--r--src/main/java/com/android/apkzlib/zip/ZFile.java47
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);