This is an automated email from the ASF dual-hosted git repository.
ggregory 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 f64639c6c Test duplicate nodes
f64639c6c is described below
commit f64639c6c4e3fc47662c0b3f4572204a0bcf7c3d
Author: Gary Gregory <[email protected]>
AuthorDate: Sat Feb 10 18:40:31 2024 -0500
Test duplicate nodes
---
.../archivers/dump/DumpArchiveInputStream.java | 28 +++++++++++----------
.../archivers/dump/DumpArchiveInputStreamTest.java | 27 +++++++++++++++++++-
.../commons/compress/dump/looping_inodes-fail.dump | Bin 0 -> 85237 bytes
.../commons/compress/dump/reclen_zero-fail.dump | Bin 0 -> 85829 bytes
4 files changed, 41 insertions(+), 14 deletions(-)
diff --git
a/src/main/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStream.java
b/src/main/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStream.java
index f76df65d2..0050b35ca 100644
---
a/src/main/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStream.java
+++
b/src/main/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStream.java
@@ -22,6 +22,7 @@ import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
@@ -291,42 +292,40 @@ public class DumpArchiveInputStream extends
ArchiveInputStream<DumpArchiveEntry>
*
* @param entry
* @return full path for specified archive entry, or null if there's a gap.
+ * @throws DumpArchiveException Infinite loop detected in directory
entries.
*/
- private String getPath(final DumpArchiveEntry entry) {
+ private String getPath(final DumpArchiveEntry entry) throws
DumpArchiveException {
// build the stack of elements. It's possible that we're
// still missing an intermediate value and if so we
final Stack<String> elements = new Stack<>();
+ final BitSet visited = new BitSet();
Dirent dirent = null;
-
for (int i = entry.getIno();; i = dirent.getParentIno()) {
if (!names.containsKey(i)) {
elements.clear();
break;
}
-
+ if (visited.get(i)) {
+ throw new DumpArchiveException("Duplicate node " + i);
+ }
dirent = names.get(i);
+ visited.set(i);
elements.push(dirent.getName());
-
if (dirent.getIno() == dirent.getParentIno()) {
break;
}
}
-
// if an element is missing defer the work and read next entry.
if (elements.isEmpty()) {
pending.put(entry.getIno(), entry);
-
return null;
}
-
// generate full path from stack of elements.
final StringBuilder sb = new StringBuilder(elements.pop());
-
while (!elements.isEmpty()) {
sb.append('/');
sb.append(elements.pop());
}
-
return sb.toString();
}
@@ -491,6 +490,9 @@ public class DumpArchiveInputStream extends
ArchiveInputStream<DumpArchiveEntry>
for (int i = 0; i < datalen - 8 && i < size - 8; i += reclen) {
final int ino = DumpArchiveUtil.convert32(blockBuffer, i);
reclen = DumpArchiveUtil.convert16(blockBuffer, i + 4);
+ if (reclen == 0) {
+ throw new DumpArchiveException("reclen cannot be 0");
+ }
final byte type = blockBuffer[i + 6];
@@ -510,15 +512,15 @@ public class DumpArchiveInputStream extends
ArchiveInputStream<DumpArchiveEntry>
names.put(ino, d);
// check whether this allows us to fill anything in the
pending list.
- pending.forEach((k, v) -> {
+ for (final Map.Entry<Integer, DumpArchiveEntry> mapEntry :
pending.entrySet()) {
+ final DumpArchiveEntry v = mapEntry.getValue();
final String path = getPath(v);
-
if (path != null) {
v.setName(path);
- v.setSimpleName(names.get(k).getName());
+
v.setSimpleName(names.get(mapEntry.getKey()).getName());
queue.add(v);
}
- });
+ }
// remove anything that we found. (We can't do it earlier
// because of concurrent modification exceptions.)
diff --git
a/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java
b/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java
index a57909441..5b158c54c 100644
---
a/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java
+++
b/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java
@@ -23,14 +23,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.InputStream;
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
import org.apache.commons.compress.AbstractTest;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.api.Timeout.ThreadMode;
public class DumpArchiveInputStreamTest extends AbstractTest {
@@ -52,7 +57,7 @@ public class DumpArchiveInputStreamTest extends AbstractTest {
@Test
public void testDirectoryNullBytes() throws Exception {
try (InputStream is =
newInputStream("org/apache/commons/compress/dump/directory_null_bytes-fail.dump");
- DumpArchiveInputStream archive = new DumpArchiveInputStream(is)) {
+ DumpArchiveInputStream archive = new
DumpArchiveInputStream(is)) {
assertThrows(InvalidFormatException.class, archive::getNextEntry);
}
}
@@ -65,6 +70,16 @@ public class DumpArchiveInputStreamTest extends AbstractTest
{
}
}
+ @Test
+ @Timeout(value = 15,unit = TimeUnit.SECONDS, threadMode =
ThreadMode.SEPARATE_THREAD)
+ public void testLoopingInodes() throws Exception {
+ try (InputStream is =
newInputStream("org/apache/commons/compress/dump/looping_inodes-fail.dump");
+ DumpArchiveInputStream archive = new
DumpArchiveInputStream(is)) {
+ archive.getNextEntry();
+ assertThrows(DumpArchiveException.class, archive::getNextEntry);
+ }
+ }
+
@Test
public void testMultiByteReadConsistentlyReturnsMinusOneAtEof() throws
Exception {
final byte[] buf = new byte[2];
@@ -93,6 +108,16 @@ public class DumpArchiveInputStreamTest extends
AbstractTest {
}
}
+ @Test
+ public void testRecLenZeroLongExecution() throws Exception {
+ try (InputStream is =
newInputStream("org/apache/commons/compress/dump/reclen_zero-fail.dump");
+ DumpArchiveInputStream archive = new
DumpArchiveInputStream(is)) {
+ assertTimeoutPreemptively(Duration.ofSeconds(20), () -> {
+ assertThrows(DumpArchiveException.class,
archive::getNextEntry);
+ });
+ }
+ }
+
@Test
public void testSingleByteReadConsistentlyReturnsMinusOneAtEof() throws
Exception {
try (InputStream in = newInputStream("bla.dump");
diff --git
a/src/test/resources/org/apache/commons/compress/dump/looping_inodes-fail.dump
b/src/test/resources/org/apache/commons/compress/dump/looping_inodes-fail.dump
new file mode 100644
index 000000000..2a334099d
Binary files /dev/null and
b/src/test/resources/org/apache/commons/compress/dump/looping_inodes-fail.dump
differ
diff --git
a/src/test/resources/org/apache/commons/compress/dump/reclen_zero-fail.dump
b/src/test/resources/org/apache/commons/compress/dump/reclen_zero-fail.dump
new file mode 100644
index 000000000..42c4faf71
Binary files /dev/null and
b/src/test/resources/org/apache/commons/compress/dump/reclen_zero-fail.dump
differ