bug 61294 -- prevent infinite loop in IOUtils' skipFully.
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1801844 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d1a51f76f0
commit
278a235b08
@ -361,7 +361,7 @@ public final class IOUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips bytes from a stream. Returns -1L if EOF was hit before
|
||||
* Skips bytes from a stream. Returns -1L if len > available() or if EOF was hit before
|
||||
* the end of the stream.
|
||||
*
|
||||
* @param in inputstream
|
||||
@ -370,11 +370,22 @@ public final class IOUtils {
|
||||
* @throws IOException on IOException
|
||||
*/
|
||||
public static long skipFully(InputStream in, long len) throws IOException {
|
||||
int total = 0;
|
||||
long total = 0;
|
||||
while (true) {
|
||||
long toSkip = len-total;
|
||||
//check that the stream has the toSkip available
|
||||
//FileInputStream can mis-report 20k skipped on a 10k file
|
||||
if (toSkip > in.available()) {
|
||||
return -1L;
|
||||
}
|
||||
long got = in.skip(len-total);
|
||||
if (got < 0) {
|
||||
return -1L;
|
||||
} else if (got == 0) {
|
||||
got = fallBackToReadFully(len-total, in);
|
||||
if (got < 0) {
|
||||
return -1L;
|
||||
}
|
||||
}
|
||||
total += got;
|
||||
if (total == len) {
|
||||
@ -382,4 +393,24 @@ public final class IOUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//an InputStream can return 0 whether or not it hits EOF
|
||||
//if it returns 0, back off to readFully to test for -1
|
||||
private static long fallBackToReadFully(long lenToRead, InputStream in) throws IOException {
|
||||
byte[] buffer = new byte[8192];
|
||||
long readSoFar = 0;
|
||||
|
||||
while (true) {
|
||||
int toSkip = (lenToRead > Integer.MAX_VALUE ||
|
||||
(lenToRead-readSoFar) > buffer.length) ? buffer.length : (int)(lenToRead-readSoFar);
|
||||
long readNow = readFully(in, buffer, 0, toSkip);
|
||||
if (readNow < toSkip) {
|
||||
return -1L;
|
||||
}
|
||||
readSoFar += readNow;
|
||||
if (readSoFar == lenToRead) {
|
||||
return readSoFar;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ import static org.apache.poi.POITestCase.assertContains;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@ -34,6 +36,8 @@ import org.apache.poi.hemf.record.HemfHeader;
|
||||
import org.apache.poi.hemf.record.HemfRecord;
|
||||
import org.apache.poi.hemf.record.HemfRecordType;
|
||||
import org.apache.poi.hemf.record.HemfText;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.RecordFormatException;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HemfExtractorTest {
|
||||
@ -160,8 +164,37 @@ public class HemfExtractorTest {
|
||||
assertEquals(expectedParts.size(), foundExpected);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test(expected = RecordFormatException.class)
|
||||
public void testInfiniteLoopOnFile() throws Exception {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("61294.emf");
|
||||
|
||||
HemfExtractor ex = new HemfExtractor(is);
|
||||
for (HemfRecord record : ex) {
|
||||
|
||||
}
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = RecordFormatException.class)
|
||||
public void testInfiniteLoopOnByteArray() throws Exception {
|
||||
InputStream is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("61294.emf");
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
IOUtils.copy(is, bos);
|
||||
is.close();
|
||||
|
||||
HemfExtractor ex = new HemfExtractor(new ByteArrayInputStream(bos.toByteArray()));
|
||||
for (HemfRecord record : ex) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
govdocs1 064213.doc-0.emf contains an example of extextouta
|
||||
*/
|
||||
|
||||
}
|
136
src/testcases/org/apache/poi/util/TestIOUtils.java
Normal file
136
src/testcases/org/apache/poi/util/TestIOUtils.java
Normal file
@ -0,0 +1,136 @@
|
||||
/* ====================================================================
|
||||
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 org.apache.poi.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Class to test IOUtils
|
||||
*/
|
||||
public final class TestIOUtils {
|
||||
static File TMP = null;
|
||||
static long SEED = new Random().nextLong();
|
||||
static Random RANDOM = new Random(SEED);
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws IOException {
|
||||
TMP = File.createTempFile("poi-ioutils-", "");
|
||||
OutputStream os = new FileOutputStream(TMP);
|
||||
for (int i = 0; i < RANDOM.nextInt(10000); i++) {
|
||||
os.write(RANDOM.nextInt((byte)127));
|
||||
}
|
||||
os.flush();
|
||||
os.close();
|
||||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDown() throws IOException {
|
||||
TMP.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipFully() throws IOException {
|
||||
InputStream is = new FileInputStream(TMP);
|
||||
long skipped = IOUtils.skipFully(is, 20000L);
|
||||
assertEquals("seed: "+SEED, -1L, skipped);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipFullyGtIntMax() throws IOException {
|
||||
InputStream is = new FileInputStream(TMP);
|
||||
long skipped = IOUtils.skipFully(is, Integer.MAX_VALUE + 20000L);
|
||||
assertEquals("seed: "+SEED, -1L, skipped);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipFullyByteArray() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
InputStream is = new FileInputStream(TMP);
|
||||
IOUtils.copy(is, bos);
|
||||
long skipped = IOUtils.skipFully(new ByteArrayInputStream(bos.toByteArray()), 20000L);
|
||||
assertEquals("seed: "+SEED, -1L, skipped);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipFullyByteArrayGtIntMax() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
InputStream is = new FileInputStream(TMP);
|
||||
IOUtils.copy(is, bos);
|
||||
long skipped = IOUtils.skipFully(new ByteArrayInputStream(bos.toByteArray()), Integer.MAX_VALUE+ 20000L);
|
||||
assertEquals("seed: "+SEED, -1L, skipped);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWonkyInputStream() throws IOException {
|
||||
long skipped = IOUtils.skipFully(new WonkyInputStream(), 10000);
|
||||
assertEquals("seed: "+SEED, 10000, skipped);
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns 0 for the first call to skip and then reads
|
||||
* as requested. This tests that the fallback to read() works.
|
||||
*/
|
||||
private static class WonkyInputStream extends InputStream {
|
||||
int skipCalled = 0;
|
||||
int readCalled = 0;
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
readCalled++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] arr, int offset, int len) throws IOException {
|
||||
readCalled++;
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long len) throws IOException {
|
||||
skipCalled++;
|
||||
if (skipCalled == 1) {
|
||||
return 0;
|
||||
} else if (skipCalled > 100) {
|
||||
return len;
|
||||
} else {
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() {
|
||||
return 100000;
|
||||
}
|
||||
}
|
||||
}
|
BIN
test-data/spreadsheet/61294.emf
Normal file
BIN
test-data/spreadsheet/61294.emf
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user