mirror of https://github.com/apache/poi.git
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
b5e74d4b64
commit
34cb860998
|
@ -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.
|
* the end of the stream.
|
||||||
*
|
*
|
||||||
* @param in inputstream
|
* @param in inputstream
|
||||||
|
@ -370,11 +370,22 @@ public final class IOUtils {
|
||||||
* @throws IOException on IOException
|
* @throws IOException on IOException
|
||||||
*/
|
*/
|
||||||
public static long skipFully(InputStream in, long len) throws IOException {
|
public static long skipFully(InputStream in, long len) throws IOException {
|
||||||
int total = 0;
|
long total = 0;
|
||||||
while (true) {
|
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);
|
long got = in.skip(len-total);
|
||||||
if (got < 0) {
|
if (got < 0) {
|
||||||
return -1L;
|
return -1L;
|
||||||
|
} else if (got == 0) {
|
||||||
|
got = fallBackToReadFully(len-total, in);
|
||||||
|
if (got < 0) {
|
||||||
|
return -1L;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
total += got;
|
total += got;
|
||||||
if (total == len) {
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
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.HemfRecord;
|
||||||
import org.apache.poi.hemf.record.HemfRecordType;
|
import org.apache.poi.hemf.record.HemfRecordType;
|
||||||
import org.apache.poi.hemf.record.HemfText;
|
import org.apache.poi.hemf.record.HemfText;
|
||||||
|
import org.apache.poi.util.IOUtils;
|
||||||
|
import org.apache.poi.util.RecordFormatException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class HemfExtractorTest {
|
public class HemfExtractorTest {
|
||||||
|
@ -160,8 +164,37 @@ public class HemfExtractorTest {
|
||||||
assertEquals(expectedParts.size(), foundExpected);
|
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
|
govdocs1 064213.doc-0.emf contains an example of extextouta
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Loading…
Reference in New Issue