Browse Source

Fix ArrayIndexOutOfBoundsException in DirCacheIterator

If the 'TREE' extension contains an invalid subtree that has
been removed, DirCacheIterator still tried to access it due to
an invalid childCnt field within the parent DirCacheTree object.
This is easy for a user to do, they just need to move all files
out of a subdirectory.

For example, the input for the JUnit test case for this bug was
built using the following C Git sequence:

  mkdir -p a/b
  touch a/b/c q
  git add a/b/c q
  git write-tree
  git mv a/b/c a/a

After the last step, the subdirectory a/b is empty, as its only
file was moved into the parent directory.  Because of the earlier
`git write-tree` operation, there is a 'TREE' extension present, but
the a and a/b subdirectories have been marked invalid by the rename.

When JGit tried to iterate over the a tree, it tried to correct
childCnt to be zero as a/b no longer exists, but it failed to
update childCnt.

Change-Id: I7a0f78fc48a36b1a83252d354618f6807fca0426
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
stable-0.11
Shawn O. Pearce 14 years ago
parent
commit
6533994bc9
  1. BIN
      org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/dircache.testRemovedSubtree
  2. 25
      org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java
  3. 13
      org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java

BIN
org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/dircache.testRemovedSubtree

Binary file not shown.

25
org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java vendored

@ -43,12 +43,15 @@
package org.eclipse.jgit.dircache; package org.eclipse.jgit.dircache;
import java.io.File;
import java.util.Collections; import java.util.Collections;
import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.RepositoryTestCase; import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup; import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.JGitTestUtil;
public class DirCacheIteratorTest extends RepositoryTestCase { public class DirCacheIteratorTest extends RepositoryTestCase {
public void testEmptyTree_NoTreeWalk() throws Exception { public void testEmptyTree_NoTreeWalk() throws Exception {
@ -269,4 +272,26 @@ public class DirCacheIteratorTest extends RepositoryTestCase {
assertFalse(tw.next()); assertFalse(tw.next());
} }
} }
public void testRemovedSubtree() throws Exception {
final File path = JGitTestUtil
.getTestResourceFile("dircache.testRemovedSubtree");
final DirCache dc = DirCache.read(path, FS.DETECTED);
assertEquals(2, dc.getEntryCount());
final TreeWalk tw = new TreeWalk(db);
tw.setRecursive(true);
tw.addTree(new DirCacheIterator(dc));
assertTrue(tw.next());
assertEquals("a/a", tw.getPathString());
assertSame(FileMode.REGULAR_FILE, tw.getFileMode(0));
assertTrue(tw.next());
assertEquals("q", tw.getPathString());
assertSame(FileMode.REGULAR_FILE, tw.getFileMode(0));
assertFalse(tw.next());
}
} }

13
org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java vendored

@ -484,14 +484,11 @@ public class DirCacheTree {
stIdx++; stIdx++;
} }
if (stIdx < childCnt) { // None of our remaining children can be in this tree
// None of our remaining children can be in this tree // as the current cache entry is after our own name.
// as the current cache entry is after our own name. //
// while (stIdx < childCnt)
final DirCacheTree[] dct = new DirCacheTree[stIdx]; removeChild(childCnt - 1);
System.arraycopy(children, 0, dct, 0, stIdx);
children = dct;
}
} }
private void insertChild(final int stIdx, final DirCacheTree st) { private void insertChild(final int stIdx, final DirCacheTree st) {

Loading…
Cancel
Save