To ensure we didn't read a partial file, or a file that has a corrupt content (e.g. due power failure on a buggy unjournaled filesystem) we now validate the entire content of the DIRC file through its final SHA1 checksum at the end of the stream. This slightly slows down the read path, as we now have to process a lot of small buffers (info and path objects) through the digest, but we're better off ensuring the content is reasonably sane. Signed-off-by: Shawn O. Pearce <spearce@xxxxxxxxxxx> --- This isn't strictly necessary to fix the bug you identified, but I was in the neighboorhood and stronger data validation on input is potentially worthwhile... .../src/org/spearce/jgit/dircache/DirCache.java | 12 +++++++++++- .../org/spearce/jgit/dircache/DirCacheEntry.java | 13 +++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java index 5df0f59..58da014 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java +++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java @@ -47,6 +47,7 @@ import java.nio.ByteBuffer; import java.security.DigestOutputStream; import java.security.MessageDigest; +import java.util.Arrays; import java.util.Comparator; import org.spearce.jgit.errors.CorruptObjectException; @@ -344,11 +345,13 @@ public void clear() { private void readFrom(final FileInputStream inStream) throws IOException, CorruptObjectException { final BufferedInputStream in = new BufferedInputStream(inStream); + final MessageDigest md = Constants.newMessageDigest(); // Read the index header and verify we understand it. // final byte[] hdr = new byte[20]; NB.readFully(in, hdr, 0, 12); + md.update(hdr, 0, 12); if (!is_DIRC(hdr)) throw new CorruptObjectException("Not a DIRC file."); final int ver = NB.decodeInt32(hdr, 4); @@ -363,7 +366,7 @@ private void readFrom(final FileInputStream inStream) throws IOException, final byte[] infos = new byte[INFO_LEN * entryCnt]; sortedEntries = new DirCacheEntry[entryCnt]; for (int i = 0; i < entryCnt; i++) - sortedEntries[i] = new DirCacheEntry(infos, i * INFO_LEN, in); + sortedEntries[i] = new DirCacheEntry(infos, i * INFO_LEN, in, md); lastModified = liveFile.lastModified(); // After the file entries are index extensions, and then a footer. @@ -381,8 +384,10 @@ private void readFrom(final FileInputStream inStream) throws IOException, switch (NB.decodeInt32(hdr, 0)) { case EXT_TREE: { final byte[] raw = new byte[NB.decodeInt32(hdr, 4)]; + md.update(hdr, 0, 8); NB.skipFully(in, 8); NB.readFully(in, raw, 0, raw.length); + md.update(raw, 0, raw.length); tree = new DirCacheTree(raw, new MutableInteger(), null); break; } @@ -405,6 +410,11 @@ private void readFrom(final FileInputStream inStream) throws IOException, } } } + + final byte[] exp = md.digest(); + if (!Arrays.equals(exp, hdr)) { + throw new CorruptObjectException("DIRC checksum mismatch"); + } } private static boolean is_DIRC(final byte[] hdr) { diff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java index 6d46648..47b1cc5 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java +++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java @@ -43,6 +43,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; +import java.security.MessageDigest; import java.util.Arrays; import org.spearce.jgit.lib.AnyObjectId; @@ -113,17 +114,19 @@ final byte[] path; DirCacheEntry(final byte[] sharedInfo, final int infoAt, - final InputStream in) throws IOException { + final InputStream in, final MessageDigest md) throws IOException { info = sharedInfo; infoOffset = infoAt; NB.readFully(in, info, infoOffset, INFO_LEN); + md.update(info, infoOffset, INFO_LEN); int pathLen = NB.decodeUInt16(info, infoOffset + P_FLAGS) & NAME_MASK; int skipped = 0; if (pathLen < NAME_MASK) { path = new byte[pathLen]; NB.readFully(in, path, 0, pathLen); + md.update(path, 0, pathLen); } else { final ByteArrayOutputStream tmp = new ByteArrayOutputStream(); { @@ -142,6 +145,8 @@ DirCacheEntry(final byte[] sharedInfo, final int infoAt, path = tmp.toByteArray(); pathLen = path.length; skipped = 1; // we already skipped 1 '\0' above to break the loop. + md.update(path, 0, pathLen); + md.update((byte) 0); } // Index records are padded out to the next 8 byte alignment @@ -149,7 +154,11 @@ DirCacheEntry(final byte[] sharedInfo, final int infoAt, // final int actLen = INFO_LEN + pathLen; final int expLen = (actLen + 8) & ~7; - NB.skipFully(in, expLen - actLen - skipped); + final int padLen = expLen - actLen - skipped; + if (padLen > 0) { + NB.skipFully(in, padLen); + md.update(nullpad, 0, padLen); + } } /** -- 1.6.2.2.600.g24c24 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html