}
}
+ public void testStandardFormat_SmallObject_TruncatedZLibStream()
+ throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
+ byte[] tr = new byte[gz.length - 1];
+ System.arraycopy(gz, 0, tr, 0, tr.length);
+ UnpackedObject.open(new ByteArrayInputStream(tr), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_SmallObject_TrailingGarbage()
+ throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
+ byte[] tr = new byte[gz.length + 1];
+ System.arraycopy(gz, 0, tr, 0, gz.length);
+ UnpackedObject.open(new ByteArrayInputStream(tr), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
public void testStandardFormat_LargeObject_CorruptZLibStream()
throws Exception {
final int type = Constants.OBJ_BLOB;
}
}
+ public void testStandardFormat_LargeObject_TruncatedZLibStream()
+ throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ObjectId id = new ObjectInserter.Formatter().idFor(type, data);
+ byte[] gz = compressStandardFormat(type, data);
+ byte[] tr = new byte[gz.length - 1];
+ System.arraycopy(gz, 0, tr, 0, tr.length);
+
+ write(id, tr);
+
+ ObjectLoader ol;
+ {
+ FileInputStream fs = new FileInputStream(path(id));
+ try {
+ ol = UnpackedObject.open(fs, path(id), id, wc);
+ } finally {
+ fs.close();
+ }
+ }
+
+ byte[] tmp = new byte[data.length];
+ InputStream in = ol.openStream();
+ IO.readFully(in, tmp, 0, tmp.length);
+ try {
+ in.close();
+ fail("close did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_LargeObject_TrailingGarbage()
+ throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ObjectId id = new ObjectInserter.Formatter().idFor(type, data);
+ byte[] gz = compressStandardFormat(type, data);
+ byte[] tr = new byte[gz.length + 1];
+ System.arraycopy(gz, 0, tr, 0, gz.length);
+
+ write(id, tr);
+
+ ObjectLoader ol;
+ {
+ FileInputStream fs = new FileInputStream(path(id));
+ try {
+ ol = UnpackedObject.open(fs, path(id), id, wc);
+ } finally {
+ fs.close();
+ }
+ }
+
+ byte[] tmp = new byte[data.length];
+ InputStream in = ol.openStream();
+ IO.readFully(in, tmp, 0, tmp.length);
+ try {
+ in.close();
+ fail("close did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
public void testPackFormat_SmallObject() throws Exception {
final int type = Constants.OBJ_BLOB;
byte[] data = rng.nextBytes(300);
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipException;
if (isStandardFormat(hdr)) {
in.reset();
- in = inflate(in, wc);
- int avail = readSome(in, hdr, 0, 64);
+ Inflater inf = wc.inflater();
+ InputStream zIn = inflate(in, inf);
+ int avail = readSome(zIn, hdr, 0, 64);
if (avail < 5)
throw new CorruptObjectException(id,
JGitText.get().corruptObjectNoHeader);
int n = avail - p.value;
if (n > 0)
System.arraycopy(hdr, p.value, data, 0, n);
- IO.readFully(in, data, n, data.length - n);
+ IO.readFully(zIn, data, n, data.length - n);
+ checkValidEndOfStream(in, inf, id, hdr);
return new ObjectLoader.SmallObject(type, data);
}
return new LargeObject(type, size, path, id, wc.db);
if (size < wc.getStreamFileThreshold() || path == null) {
in.reset();
IO.skipFully(in, p);
- in = inflate(in, wc);
+ Inflater inf = wc.inflater();
+ InputStream zIn = inflate(in, inf);
byte[] data = new byte[(int) size];
- IO.readFully(in, data, 0, data.length);
+ IO.readFully(zIn, data, 0, data.length);
+ checkValidEndOfStream(in, inf, id, hdr);
return new ObjectLoader.SmallObject(type, data);
}
return new LargeObject(type, size, path, id, wc.db);
}
}
+ private static void checkValidEndOfStream(InputStream in, Inflater inf,
+ AnyObjectId id, final byte[] buf) throws IOException,
+ CorruptObjectException {
+ for (;;) {
+ int r;
+ try {
+ r = inf.inflate(buf);
+ } catch (DataFormatException e) {
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ }
+ if (r != 0)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectIncorrectLength);
+
+ if (inf.finished()) {
+ if (inf.getRemaining() != 0 || in.read() != -1)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ break;
+ }
+
+ if (!inf.needsInput())
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+
+ r = in.read(buf);
+ if (r <= 0)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ inf.setInput(buf, 0, r);
+ }
+ }
+
private static boolean isStandardFormat(final byte[] hdr) {
// Try to determine if this is a standard format loose object or
// a pack style loose object. The standard format is completely
return fb == 0x78 && (((fb << 8) | hdr[1] & 0xff) % 31) == 0;
}
- private static InputStream inflate(InputStream in, final ObjectId id) {
+ private static InputStream inflate(final InputStream in, final long size,
+ final ObjectId id) {
final Inflater inf = InflaterCache.get();
return new InflaterInputStream(in, inf) {
+ private long remaining = size;
+
@Override
public int read(byte[] b, int off, int cnt) throws IOException {
try {
- return super.read(b, off, cnt);
+ int r = super.read(b, off, cnt);
+ if (r > 0)
+ remaining -= r;
+ return r;
} catch (ZipException badStream) {
throw new CorruptObjectException(id,
JGitText.get().corruptObjectBadStream);
@Override
public void close() throws IOException {
- super.close();
- InflaterCache.release(inf);
+ try {
+ if (remaining <= 0)
+ checkValidEndOfStream(in, inf, id, new byte[64]);
+ super.close();
+ } finally {
+ InflaterCache.release(inf);
+ }
}
};
}
- private static InputStream inflate(InputStream in, WindowCursor wc) {
- return new InflaterInputStream(in, wc.inflater(), BUFFER_SIZE);
+ private static InflaterInputStream inflate(InputStream in, Inflater inf) {
+ return new InflaterInputStream(in, inf, BUFFER_SIZE);
}
private static BufferedInputStream buffer(InputStream in) {
if (isStandardFormat(hdr)) {
in.reset();
- in = buffer(inflate(in, id));
+ in = buffer(inflate(in, size, id));
while (0 < in.read())
continue;
} else {
in.reset();
IO.skipFully(in, p);
- in = buffer(inflate(in, id));
+ in = buffer(inflate(in, size, id));
}
ok = true;