Skip to content

Commit

Permalink
epam#58 Fixed infinity loop on broken file
Browse files Browse the repository at this point in the history
epam#56 Fixed infinity loop on empty or incomplete input stream
  • Loading branch information
xantorohara committed Dec 29, 2020
1 parent 430cca9 commit bd0c048
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 13 deletions.
43 changes: 30 additions & 13 deletions src/main/java/com/epam/parso/impl/SasFileParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -359,16 +359,40 @@ private void processSasFileHeader(String builderEncoding) throws IOException {
}

if (sasFileStream != null) {
skipBytes(sasFileProperties.getHeaderLength() - currentFilePosition);
currentFilePosition = 0;
}
}

/**
* Skip specified number of bytes of data from the input stream,
* or fail if there are not enough left.
*
* @param numberOfBytesToSkip the number of bytes to skip
* @throws IOException if the number of bytes skipped was incorrect
*/
private void skipBytes(long numberOfBytesToSkip) throws IOException {
byte[] skipBuffer = new byte[8192];

long remainBytes = numberOfBytesToSkip;
long readBytes;
while (remainBytes > 0) {
try {
int bytesLeft = sasFileProperties.getHeaderLength() - currentFilePosition;
long actuallySkipped = 0;
while (actuallySkipped < bytesLeft) {
actuallySkipped += sasFileStream.skip(bytesLeft - actuallySkipped);
readBytes = sasFileStream.read(skipBuffer, 0, (int) Math.min(remainBytes, skipBuffer.length));
if (readBytes < 0) { // EOF
break;
}
currentFilePosition = 0;
} catch (IOException e) {
throw new IOException(EMPTY_INPUT_STREAM);
}
remainBytes -= readBytes;
}

long actuallySkipped = numberOfBytesToSkip - remainBytes;

if (actuallySkipped != numberOfBytesToSkip) {
throw new IOException("Expected to skip " + numberOfBytesToSkip
+ " to the end of the header, but skipped " + actuallySkipped + " instead.");
}
}

Expand Down Expand Up @@ -893,14 +917,7 @@ private List<byte[]> getBytesFromFile(Long[] offset, Integer[] length) throws IO
if (cachedPage == null) {
for (int i = 0; i < offset.length; i++) {
byte[] temp = new byte[length[i]];
long actuallySkipped = 0;
while (actuallySkipped < offset[i] - currentFilePosition) {
try {
actuallySkipped += sasFileStream.skip(offset[i] - currentFilePosition - actuallySkipped);
} catch (IOException e) {
throw new IOException(EMPTY_INPUT_STREAM);
}
}
skipBytes(offset[i] - currentFilePosition);
try {
sasFileStream.readFully(temp, 0, length[i]);
} catch (EOFException e) {
Expand Down
77 changes: 77 additions & 0 deletions src/test/java/com/epam/parso/BugsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
import com.epam.parso.impl.SasFileReaderImpl;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -139,4 +143,77 @@ public void testCompressionMethodsIssue55() throws Exception {
assertThat(sasFileReader.getSasFileProperties().getCompressionMethod()).isNull();
}
}

@Test(timeout = 1000)
public void testInfinityLoopOnEmptyStreamIssue56() {
ByteArrayInputStream emptyStream = new ByteArrayInputStream(new byte[]{});
SasFileReaderImpl sasFileReader = new SasFileReaderImpl(emptyStream);
assertThat(sasFileReader.getSasFileProperties().getRowCount()).isEqualTo(0);
}

@Test(timeout = 1000)
public void testInfinityLoopOnIncompleteStreamIssue56() {
ByteArrayInputStream emptyStream = new ByteArrayInputStream(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9});
SasFileReaderImpl sasFileReader = new SasFileReaderImpl(emptyStream);
assertThat(sasFileReader.getSasFileProperties().getRowCount()).isEqualTo(0);
}

/**
* This has invalid page and header lengths:
* - page length = 31612 (7C 7B 00 00)
* - header length = 32126 (7E 7D 00 00)
* See this row in a hex editor:
* 000000C0: 00 00 00 00 7E 7D 00 00 │ 7C 7B 00 00 B8 00 00 00
* <p>
* File size is 17408 - it is smaller than specified lengths, so both lengths refer
* to the out of file bounds offsets.
*/
private static final String INVALID_FILE_NAME = "invalid_lengths.sas7bdat";

@Test(timeout = 1000)
public void testBrokenFileOnFileChannelIssue58() throws Exception {
// These skips were performed performed for the FileChannel by the parser before the fix:
// [32, 2, 1, 32, 21, 16, 8, 17120, 0, 0, 0, ... infinity loop of zeros]
try (InputStream is = Files.newInputStream(
Paths.get(this.getClass().getResource("/bugs/" + INVALID_FILE_NAME).toURI()))) {
SasFileReader sasFileReader = new SasFileReaderImpl(is);
assertThat(sasFileReader.getSasFileProperties().getRowCount()).isEqualTo(0);
}
}

@Test(timeout = 1000)
public void testBrokenFileOnBufferedFileInputStreamIssue58() throws Exception {
// These skips were performed performed for the buffered stream by the parser before the fix:
// [32, 2, 1, 32, 21, 16, 8, 7936, 23902]
try (InputStream is = this.getClass().getResourceAsStream("/bugs/" + INVALID_FILE_NAME)) {
SasFileReader sasFileReader = new SasFileReaderImpl(is);
assertThat(sasFileReader.getSasFileProperties().getRowCount()).isEqualTo(0);
}
}

@Test(timeout = 1000)
public void testBrokenFileOnUnbufferedFileInputStreamIssue58() throws Exception {
// These skips were performed for the buffered stream by the parser before the fix:
// [32, 2, 1, 32, 21, 16, 8, 31838]
try (InputStream is = new FileInputStream("target/test-classes/bugs/" + INVALID_FILE_NAME)) {
SasFileReader sasFileReader = new SasFileReaderImpl(is);
assertThat(sasFileReader.getSasFileProperties().getRowCount()).isEqualTo(0);
}
}

@Test(timeout = 1000)
public void testInfinityLoopBufferedIssue58() throws Exception {
try (InputStream is = this.getClass().getResourceAsStream("/bugs/sas_infinite_loop.sas7bdat")) {
SasFileReader sasFileReader = new SasFileReaderImpl(is);
assertThat(sasFileReader.getSasFileProperties().getRowCount()).isEqualTo(0);
}
}

@Test(timeout = 1000)
public void testInfinityLoopUnbufferedIssue58() throws Exception {
try (InputStream is = new FileInputStream("target/test-classes/bugs/sas_infinite_loop.sas7bdat")) {
SasFileReader sasFileReader = new SasFileReaderImpl(is);
assertThat(sasFileReader.getSasFileProperties().getRowCount()).isEqualTo(0);
}
}
}
Binary file not shown.
Binary file not shown.

0 comments on commit bd0c048

Please sign in to comment.