mirror of https://github.com/apache/poi.git
Bug 66425: Avoid exceptions found via poi-fuzz
We try to avoid throwing NullPointerException, ClassCastExceptions and StackOverflowException, but it was possible to trigger them Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=61562 https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=62068 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1912383 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
836512cc1f
commit
88bbfbb3f7
|
@ -198,6 +198,9 @@ public abstract class POIXMLRelation {
|
||||||
* @since 3.16-beta3
|
* @since 3.16-beta3
|
||||||
*/
|
*/
|
||||||
public InputStream getContents(PackagePart corePart) throws IOException, InvalidFormatException {
|
public InputStream getContents(PackagePart corePart) throws IOException, InvalidFormatException {
|
||||||
|
if (corePart == null) {
|
||||||
|
throw new IllegalArgumentException("Core-Part cannot be empty");
|
||||||
|
}
|
||||||
PackageRelationshipCollection prc =
|
PackageRelationshipCollection prc =
|
||||||
corePart.getRelationshipsByType(getRelation());
|
corePart.getRelationshipsByType(getRelation());
|
||||||
Iterator<PackageRelationship> it = prc.iterator();
|
Iterator<PackageRelationship> it = prc.iterator();
|
||||||
|
|
|
@ -229,7 +229,7 @@ public final class POIXMLExtractorFactory implements ExtractorProvider {
|
||||||
|
|
||||||
// Grab the core document part, and try to identify from that
|
// Grab the core document part, and try to identify from that
|
||||||
final PackagePart corePart = pkg.getPart(core.getRelationship(0));
|
final PackagePart corePart = pkg.getPart(core.getRelationship(0));
|
||||||
final String contentType = corePart.getContentType();
|
final String contentType = corePart == null ? null : corePart.getContentType();
|
||||||
|
|
||||||
// Is it XSSF?
|
// Is it XSSF?
|
||||||
for (XSSFRelation rel : XSSFExcelExtractor.SUPPORTED_TYPES) {
|
for (XSSFRelation rel : XSSFExcelExtractor.SUPPORTED_TYPES) {
|
||||||
|
|
|
@ -22,8 +22,6 @@ import java.lang.reflect.Method;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
import javax.xml.transform.OutputKeys;
|
import javax.xml.transform.OutputKeys;
|
||||||
import javax.xml.transform.Transformer;
|
import javax.xml.transform.Transformer;
|
||||||
import javax.xml.transform.dom.DOMSource;
|
import javax.xml.transform.dom.DOMSource;
|
||||||
|
@ -53,10 +51,11 @@ import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
@Beta
|
@Beta
|
||||||
public class WordToTextConverter extends AbstractWordConverter
|
public class WordToTextConverter extends AbstractWordConverter {
|
||||||
{
|
|
||||||
private static final Logger LOG = LogManager.getLogger(WordToTextConverter.class);
|
private static final Logger LOG = LogManager.getLogger(WordToTextConverter.class);
|
||||||
|
|
||||||
|
private static final int MAX_NESTED_CHILD_NODES = 500;
|
||||||
|
|
||||||
public static String getText( DirectoryNode root ) throws Exception
|
public static String getText( DirectoryNode root ) throws Exception
|
||||||
{
|
{
|
||||||
final HWPFDocumentCore wordDocument = AbstractWordUtils.loadDoc( root );
|
final HWPFDocumentCore wordDocument = AbstractWordUtils.loadDoc( root );
|
||||||
|
@ -109,7 +108,7 @@ public class WordToTextConverter extends AbstractWordConverter
|
||||||
serializer.transform( domSource, streamResult );
|
serializer.transform( domSource, streamResult );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Document process( File docFile ) throws IOException, ParserConfigurationException {
|
private static Document process( File docFile ) throws IOException {
|
||||||
try (final HWPFDocumentCore wordDocument = AbstractWordUtils.loadDoc( docFile )) {
|
try (final HWPFDocumentCore wordDocument = AbstractWordUtils.loadDoc( docFile )) {
|
||||||
WordToTextConverter wordToTextConverter = new WordToTextConverter(
|
WordToTextConverter wordToTextConverter = new WordToTextConverter(
|
||||||
XMLHelper.newDocumentBuilder().newDocument());
|
XMLHelper.newDocumentBuilder().newDocument());
|
||||||
|
@ -118,7 +117,7 @@ public class WordToTextConverter extends AbstractWordConverter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AtomicInteger noteCounters = new AtomicInteger( 1 );
|
private final AtomicInteger noteCounters = new AtomicInteger( 1 );
|
||||||
|
|
||||||
private Element notes;
|
private Element notes;
|
||||||
|
|
||||||
|
@ -130,11 +129,8 @@ public class WordToTextConverter extends AbstractWordConverter
|
||||||
* Creates new instance of {@link WordToTextConverter}. Can be used for
|
* Creates new instance of {@link WordToTextConverter}. Can be used for
|
||||||
* output several {@link HWPFDocument}s into single text document.
|
* output several {@link HWPFDocument}s into single text document.
|
||||||
*
|
*
|
||||||
* @throws ParserConfigurationException
|
|
||||||
* if an internal {@link DocumentBuilder} cannot be created
|
|
||||||
*/
|
*/
|
||||||
public WordToTextConverter() throws ParserConfigurationException
|
public WordToTextConverter() {
|
||||||
{
|
|
||||||
this.textDocumentFacade = new TextDocumentFacade(
|
this.textDocumentFacade = new TextDocumentFacade(
|
||||||
XMLHelper.newDocumentBuilder().newDocument() );
|
XMLHelper.newDocumentBuilder().newDocument() );
|
||||||
}
|
}
|
||||||
|
@ -312,6 +308,12 @@ public class WordToTextConverter extends AbstractWordConverter
|
||||||
Element note = textDocumentFacade.createBlock();
|
Element note = textDocumentFacade.createBlock();
|
||||||
notes.appendChild( note );
|
notes.appendChild( note );
|
||||||
|
|
||||||
|
// avoid StackOverflowException with very deeply nested files (mostly synthetic test files via fuzzing)
|
||||||
|
// if this limit is reached in real-word documents, we can make this configurable
|
||||||
|
if (note.getParentNode() != null && note.getParentNode().getChildNodes().getLength() > MAX_NESTED_CHILD_NODES) {
|
||||||
|
throw new IllegalStateException("Had more than the limit of " + MAX_NESTED_CHILD_NODES + " nested child notes");
|
||||||
|
}
|
||||||
|
|
||||||
note.appendChild( textDocumentFacade.createText( "^" + noteIndex
|
note.appendChild( textDocumentFacade.createText( "^" + noteIndex
|
||||||
+ "\t " ) );
|
+ "\t " ) );
|
||||||
processCharacters( wordDocument, Integer.MIN_VALUE, noteTextRange, note );
|
processCharacters( wordDocument, Integer.MIN_VALUE, noteTextRange, note );
|
||||||
|
|
|
@ -35,6 +35,7 @@ import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.poi.POIDataSamples;
|
import org.apache.poi.POIDataSamples;
|
||||||
import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
|
import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
|
||||||
|
import org.apache.poi.hslf.exceptions.HSLFException;
|
||||||
import org.apache.poi.hslf.exceptions.OldPowerPointFormatException;
|
import org.apache.poi.hslf.exceptions.OldPowerPointFormatException;
|
||||||
import org.apache.poi.util.IOUtils;
|
import org.apache.poi.util.IOUtils;
|
||||||
import org.apache.commons.io.output.NullPrintStream;
|
import org.apache.commons.io.output.NullPrintStream;
|
||||||
|
@ -65,6 +66,7 @@ public abstract class BaseTestPPTIterating {
|
||||||
EXCLUDED.put("clusterfuzz-testcase-minimized-POIHSLFFuzzer-6710128412590080.ppt", RuntimeException.class);
|
EXCLUDED.put("clusterfuzz-testcase-minimized-POIHSLFFuzzer-6710128412590080.ppt", RuntimeException.class);
|
||||||
EXCLUDED.put("clusterfuzz-testcase-minimized-POIFuzzer-5429732352851968.ppt", FileNotFoundException.class);
|
EXCLUDED.put("clusterfuzz-testcase-minimized-POIFuzzer-5429732352851968.ppt", FileNotFoundException.class);
|
||||||
EXCLUDED.put("clusterfuzz-testcase-minimized-POIFuzzer-5681320547975168.ppt", FileNotFoundException.class);
|
EXCLUDED.put("clusterfuzz-testcase-minimized-POIFuzzer-5681320547975168.ppt", FileNotFoundException.class);
|
||||||
|
EXCLUDED.put("clusterfuzz-testcase-minimized-POIHSLFFuzzer-5962760801091584.ppt", RuntimeException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Stream<Arguments> files() {
|
public static Stream<Arguments> files() {
|
||||||
|
|
|
@ -50,6 +50,8 @@ public final class EscherContainerRecord extends EscherRecord implements Iterabl
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger(EscherContainerRecord.class);
|
private static final Logger LOGGER = LogManager.getLogger(EscherContainerRecord.class);
|
||||||
|
|
||||||
|
private static final int MAX_NESTED_CHILD_NODES = 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* in case if document contains any charts we have such document structure:
|
* in case if document contains any charts we have such document structure:
|
||||||
* BOF
|
* BOF
|
||||||
|
@ -86,12 +88,26 @@ public final class EscherContainerRecord extends EscherRecord implements Iterabl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int fillFields(byte[] data, int pOffset, EscherRecordFactory recordFactory) {
|
public int fillFields(byte[] data, int pOffset, EscherRecordFactory recordFactory) {
|
||||||
|
return fillFields(data, pOffset, recordFactory, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int fillFields(byte[] data, int pOffset, EscherRecordFactory recordFactory, int nesting) {
|
||||||
|
if (nesting > MAX_NESTED_CHILD_NODES) {
|
||||||
|
throw new IllegalStateException("Had more than the limit of " + MAX_NESTED_CHILD_NODES + " nested child notes");
|
||||||
|
}
|
||||||
int bytesRemaining = readHeader(data, pOffset);
|
int bytesRemaining = readHeader(data, pOffset);
|
||||||
int bytesWritten = 8;
|
int bytesWritten = 8;
|
||||||
int offset = pOffset + 8;
|
int offset = pOffset + 8;
|
||||||
while (bytesRemaining > 0 && offset < data.length) {
|
while (bytesRemaining > 0 && offset < data.length) {
|
||||||
EscherRecord child = recordFactory.createRecord(data, offset);
|
EscherRecord child = recordFactory.createRecord(data, offset);
|
||||||
int childBytesWritten = child.fillFields(data, offset, recordFactory);
|
|
||||||
|
final int childBytesWritten;
|
||||||
|
if (child instanceof EscherContainerRecord) {
|
||||||
|
childBytesWritten = ((EscherContainerRecord)child).fillFields(data, offset, recordFactory, nesting + 1);
|
||||||
|
} else {
|
||||||
|
childBytesWritten = child.fillFields(data, offset, recordFactory);
|
||||||
|
}
|
||||||
|
|
||||||
bytesWritten += childBytesWritten;
|
bytesWritten += childBytesWritten;
|
||||||
offset += childBytesWritten;
|
offset += childBytesWritten;
|
||||||
bytesRemaining -= childBytesWritten;
|
bytesRemaining -= childBytesWritten;
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue