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

Reply via email to