Skip to content

Commit

Permalink
make Archive slightly more robust against corrupt files. Add (#8)
Browse files Browse the repository at this point in the history
unit test file from Apache Tika.
  • Loading branch information
tballison authored and beothorn committed May 31, 2018
1 parent 00610e6 commit ad8d0ba
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 15 deletions.
57 changes: 42 additions & 15 deletions src/main/java/com/github/junrar/Archive.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand Down Expand Up @@ -62,6 +64,8 @@
public class Archive implements Closeable {
private static Logger logger = Logger.getLogger(Archive.class.getName());

private static int MAX_HEADER_SIZE = 20971520;//20MB

private IReadOnlyAccess rof;

private final UnrarCallback unrarCallback;
Expand Down Expand Up @@ -229,11 +233,13 @@ private void readHeaders(long fileLength) throws IOException, RarException {
headers.clear();
currentHeaderIndex = 0;
int toRead = 0;

//keep track of positions already processed for
//more robustness against corrupt files
Set<Long> processedPositions = new HashSet<Long>();
while (true) {
int size = 0;
long newpos = 0;
byte[] baseBlockBuffer = new byte[BaseBlock.BaseBlockSize];
byte[] baseBlockBuffer = safelyAllocate(BaseBlock.BaseBlockSize, MAX_HEADER_SIZE);

long position = rof.getPosition();

Expand Down Expand Up @@ -266,7 +272,7 @@ private void readHeaders(long fileLength) throws IOException, RarException {
case MainHeader:
toRead = block.hasEncryptVersion() ? MainHeader.mainHeaderSizeWithEnc
: MainHeader.mainHeaderSize;
byte[] mainbuff = new byte[toRead];
byte[] mainbuff = safelyAllocate(toRead, MAX_HEADER_SIZE);
rof.readFully(mainbuff, toRead);
MainHeader mainhead = new MainHeader(block, mainbuff);
headers.add(mainhead);
Expand All @@ -280,7 +286,7 @@ private void readHeaders(long fileLength) throws IOException, RarException {

case SignHeader:
toRead = SignHeader.signHeaderSize;
byte[] signBuff = new byte[toRead];
byte[] signBuff = safelyAllocate(toRead, MAX_HEADER_SIZE);
rof.readFully(signBuff, toRead);
SignHeader signHead = new SignHeader(block, signBuff);
headers.add(signHead);
Expand All @@ -290,7 +296,7 @@ private void readHeaders(long fileLength) throws IOException, RarException {

case AvHeader:
toRead = AVHeader.avHeaderSize;
byte[] avBuff = new byte[toRead];
byte[] avBuff = safelyAllocate(toRead, MAX_HEADER_SIZE);
rof.readFully(avBuff, toRead);
AVHeader avHead = new AVHeader(block, avBuff);
headers.add(avHead);
Expand All @@ -299,14 +305,18 @@ private void readHeaders(long fileLength) throws IOException, RarException {

case CommHeader:
toRead = CommentHeader.commentHeaderSize;
byte[] commBuff = new byte[toRead];
byte[] commBuff = safelyAllocate(toRead, MAX_HEADER_SIZE);
rof.readFully(commBuff, toRead);
CommentHeader commHead = new CommentHeader(block, commBuff);
headers.add(commHead);
// logger.info("method: "+commHead.getUnpMethod()+"; 0x"+
// Integer.toHexString(commHead.getUnpMethod()));
newpos = commHead.getPositionInFile()
+ commHead.getHeaderSize();
if (processedPositions.contains(newpos)) {
throw new RarException(RarExceptionType.badRarArchive);
}
processedPositions.add(newpos);
rof.setPosition(newpos);

break;
Expand All @@ -321,7 +331,7 @@ private void readHeaders(long fileLength) throws IOException, RarException {
}
EndArcHeader endArcHead;
if (toRead > 0) {
byte[] endArchBuff = new byte[toRead];
byte[] endArchBuff = safelyAllocate(toRead, MAX_HEADER_SIZE);
rof.readFully(endArchBuff, toRead);
endArcHead = new EndArcHeader(block, endArchBuff);
// logger.info("HeaderType: endarch\ndatacrc:"+
Expand All @@ -335,7 +345,7 @@ private void readHeaders(long fileLength) throws IOException, RarException {
return;

default:
byte[] blockHeaderBuffer = new byte[BlockHeader.blockHeaderSize];
byte[] blockHeaderBuffer = safelyAllocate(BlockHeader.blockHeaderSize, MAX_HEADER_SIZE);
rof.readFully(blockHeaderBuffer, BlockHeader.blockHeaderSize);
BlockHeader blockHead = new BlockHeader(block,
blockHeaderBuffer);
Expand All @@ -346,40 +356,47 @@ private void readHeaders(long fileLength) throws IOException, RarException {
toRead = blockHead.getHeaderSize()
- BlockHeader.BaseBlockSize
- BlockHeader.blockHeaderSize;
byte[] fileHeaderBuffer = new byte[toRead];
byte[] fileHeaderBuffer = safelyAllocate(toRead, MAX_HEADER_SIZE);
rof.readFully(fileHeaderBuffer, toRead);

FileHeader fh = new FileHeader(blockHead, fileHeaderBuffer);
headers.add(fh);
newpos = fh.getPositionInFile() + fh.getHeaderSize()
+ fh.getFullPackSize();
if (processedPositions.contains(newpos)) {
throw new RarException(RarExceptionType.badRarArchive);
}
processedPositions.add(newpos);
rof.setPosition(newpos);
break;

case ProtectHeader:
toRead = blockHead.getHeaderSize()
- BlockHeader.BaseBlockSize
- BlockHeader.blockHeaderSize;
byte[] protectHeaderBuffer = new byte[toRead];
byte[] protectHeaderBuffer = safelyAllocate(toRead, MAX_HEADER_SIZE);
rof.readFully(protectHeaderBuffer, toRead);
ProtectHeader ph = new ProtectHeader(blockHead,
protectHeaderBuffer);

newpos = ph.getPositionInFile() + ph.getHeaderSize()
+ ph.getDataSize();
if (processedPositions.contains(newpos)) {
throw new RarException(RarExceptionType.badRarArchive);
}
processedPositions.add(newpos);
rof.setPosition(newpos);
break;

case SubHeader: {
byte[] subHeadbuffer = new byte[SubBlockHeader.SubBlockHeaderSize];
byte[] subHeadbuffer = safelyAllocate(SubBlockHeader.SubBlockHeaderSize, MAX_HEADER_SIZE);
rof.readFully(subHeadbuffer,
SubBlockHeader.SubBlockHeaderSize);
SubBlockHeader subHead = new SubBlockHeader(blockHead,
subHeadbuffer);
subHead.print();
switch (subHead.getSubType()) {
case MAC_HEAD: {
byte[] macHeaderbuffer = new byte[MacInfoHeader.MacInfoHeaderSize];
byte[] macHeaderbuffer = safelyAllocate(MacInfoHeader.MacInfoHeaderSize, MAX_HEADER_SIZE);
rof.readFully(macHeaderbuffer,
MacInfoHeader.MacInfoHeaderSize);
MacInfoHeader macHeader = new MacInfoHeader(subHead,
Expand All @@ -393,7 +410,7 @@ private void readHeaders(long fileLength) throws IOException, RarException {
case BEEA_HEAD:
break;
case EA_HEAD: {
byte[] eaHeaderBuffer = new byte[EAHeader.EAHeaderSize];
byte[] eaHeaderBuffer = safelyAllocate(EAHeader.EAHeaderSize, MAX_HEADER_SIZE);
rof.readFully(eaHeaderBuffer, EAHeader.EAHeaderSize);
EAHeader eaHeader = new EAHeader(subHead,
eaHeaderBuffer);
Expand All @@ -411,7 +428,7 @@ private void readHeaders(long fileLength) throws IOException, RarException {
toRead -= BaseBlock.BaseBlockSize;
toRead -= BlockHeader.blockHeaderSize;
toRead -= SubBlockHeader.SubBlockHeaderSize;
byte[] uoHeaderBuffer = new byte[toRead];
byte[] uoHeaderBuffer = safelyAllocate(toRead, MAX_HEADER_SIZE);
rof.readFully(uoHeaderBuffer, toRead);
UnixOwnersHeader uoHeader = new UnixOwnersHeader(
subHead, uoHeaderBuffer);
Expand All @@ -434,6 +451,16 @@ private void readHeaders(long fileLength) throws IOException, RarException {
}
}

private static byte[] safelyAllocate(long len, int maxSize) throws RarException {
if (maxSize < 0) {
throw new IllegalArgumentException("maxsize must be >= 0");
}
if (len < 0 || len > (long)maxSize) {
throw new RarException(RarExceptionType.badRarArchive);
}
return new byte[(int)len];
}

/**
* Extract the file specified by the given header and write it to the
* supplied output stream
Expand Down
60 changes: 60 additions & 0 deletions src/test/java/com/github/junrar/testUtil/SimpleTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.junrar.testUtil;

import com.github.junrar.Archive;
import com.github.junrar.rarfile.FileHeader;
import org.junit.Test;

import java.io.File;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class SimpleTest {

@Test
public void testTikaDocs() throws Exception {
String[] expected = {"testEXCEL.xls", "13824",
"testHTML.html", "167",
"testOpenOffice2.odt", "26448",
"testPDF.pdf", "34824",
"testPPT.ppt", "16384",
"testRTF.rtf", "3410",
"testTXT.txt", "49",
"testWORD.doc", "19456",
"testXML.xml", "766"};


File f = new File(getClass().getResource("test-documents.rar").toURI());
Archive archive = null;
try {
archive = new Archive(f);

FileHeader fileHeader = archive.nextFileHeader();
int i = 0;
while (fileHeader != null) {
assertTrue(fileHeader.getFileNameString().contains(expected[i++]));
assertEquals(Long.parseLong(expected[i++]), fileHeader.getUnpSize());
fileHeader = archive.nextFileHeader();
}
} finally {
archive.close();
}

}
}
Binary file not shown.

0 comments on commit ad8d0ba

Please sign in to comment.