Browse Source

Bug 66425: Avoid a ClassCastException found via oss-fuzz

We try to avoid throwing NullPointerException, but it was possible
to trigger one here with a specially crafted input-file

Should fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=61266

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1911523 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_5_2_4
Dominik Stadler 9 months ago
parent
commit
163ff25594

+ 21
- 0
poi-examples/src/test/java/org/apache/poi/integration/TestXLSX2CSV.java View File

package org.apache.poi.integration; package org.apache.poi.integration;


import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;


import java.io.PrintStream; import java.io.PrintStream;


import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream; import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
import org.apache.poi.examples.xssf.eventusermodel.XLSX2CSV; import org.apache.poi.examples.xssf.eventusermodel.XLSX2CSV;
import org.apache.poi.ooxml.POIXMLException;
import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.XSSFTestDataSamples;
assertTrue(output.contains(",\"hello, xssf\",,\"hello, xssf\""), "Had: " + output); assertTrue(output.contains(",\"hello, xssf\",,\"hello, xssf\""), "Had: " + output);
} }


@Test
public void testInvalidSampleFile() throws Exception {
final UnsynchronizedByteArrayOutputStream outputBytes = UnsynchronizedByteArrayOutputStream.builder().get();
PrintStream out = new PrintStream(outputBytes, true, StandardCharsets.UTF_8.name());

// The package open is instantaneous, as it should be.
try (OPCPackage p = OPCPackage.open(XSSFTestDataSamples.getSampleFile("clusterfuzz-testcase-minimized-XLSX2CSVFuzzer-5025401116950528.xlsx").getAbsolutePath(), PackageAccess.READ)) {
XLSX2CSV xlsx2csv = new XLSX2CSV(p, out, -1);
assertThrows(POIXMLException.class,
xlsx2csv::process);
}

String errorOutput = errorBytes.toString(StandardCharsets.UTF_8);
assertEquals("", errorOutput);

String output = outputBytes.toString(StandardCharsets.UTF_8);
assertEquals("", output, "Had: " + output);
}

@Test @Test
public void testMinColumns() throws Exception { public void testMinColumns() throws Exception {
final UnsynchronizedByteArrayOutputStream outputBytes = UnsynchronizedByteArrayOutputStream.builder().get(); final UnsynchronizedByteArrayOutputStream outputBytes = UnsynchronizedByteArrayOutputStream.builder().get();

+ 1
- 1
poi-integration/src/test/java/org/apache/poi/stress/TestAllFiles.java View File

@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static void verify(String file, Executable exec, Class<? extends Throwable> exClass, String exMessage, String password, private static void verify(String file, Executable exec, Class<? extends Throwable> exClass, String exMessage, String password,
FileHandler fileHandler) { FileHandler fileHandler) {
final String errPrefix = file + " - failed for handler " + fileHandler.getClass().getSimpleName() + ": ";
final String errPrefix = file.replace("\\", "/") + " - failed for handler " + fileHandler.getClass().getSimpleName() + ": ";
// this also removes the password for non encrypted files // this also removes the password for non encrypted files
Biff8EncryptionKey.setCurrentUserPassword(password); Biff8EncryptionKey.setCurrentUserPassword(password);
if (exClass != null && AssertionFailedError.class.isAssignableFrom(exClass)) { if (exClass != null && AssertionFailedError.class.isAssignableFrom(exClass)) {

+ 9
- 3
poi-ooxml/src/main/java/org/apache/poi/xssf/eventusermodel/ReadOnlySharedStringsTable.java View File

this.strings = new ArrayList<>(this.uniqueCount); this.strings = new ArrayList<>(this.uniqueCount);
characters = new StringBuilder(64); characters = new StringBuilder(64);
} else if ("si".equals(localName)) { } else if ("si".equals(localName)) {
characters.setLength(0);
if (characters != null) {
characters.setLength(0);
}
} else if ("t".equals(localName)) { } else if ("t".equals(localName)) {
tIsOpen = true; tIsOpen = true;
} else if ("rPh".equals(localName)) { } else if ("rPh".equals(localName)) {
} }


if ("si".equals(localName)) { if ("si".equals(localName)) {
strings.add(characters.toString());
if (strings != null && characters != null) {
strings.add(characters.toString());
}
} else if ("t".equals(localName)) { } else if ("t".equals(localName)) {
tIsOpen = false; tIsOpen = false;
} else if ("rPh".equals(localName)) { } else if ("rPh".equals(localName)) {
if (inRPh && includePhoneticRuns) { if (inRPh && includePhoneticRuns) {
characters.append(ch, start, length); characters.append(ch, start, length);
} else if (! inRPh){ } else if (! inRPh){
characters.append(ch, start, length);
if (characters != null) {
characters.append(ch, start, length);
}
} }
} }
} }

+ 52
- 33
poi-ooxml/src/test/java/org/apache/poi/xssf/eventusermodel/TestReadOnlySharedStringsTable.java View File

package org.apache.poi.xssf.eventusermodel; package org.apache.poi.xssf.eventusermodel;


import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;


import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
* Tests for {@link org.apache.poi.xssf.eventusermodel.XSSFReader} * Tests for {@link org.apache.poi.xssf.eventusermodel.XSSFReader}
*/ */
public final class TestReadOnlySharedStringsTable { public final class TestReadOnlySharedStringsTable {
private static POIDataSamples _ssTests = POIDataSamples.getSpreadSheetInstance();
private static final POIDataSamples _ssTests = POIDataSamples.getSpreadSheetInstance();


@Test @Test
void testParse() throws Exception { void testParse() throws Exception {
List<PackagePart> parts = pkg.getPartsByName(Pattern.compile("/xl/sharedStrings.xml")); List<PackagePart> parts = pkg.getPartsByName(Pattern.compile("/xl/sharedStrings.xml"));
assertEquals(1, parts.size()); assertEquals(1, parts.size());


SharedStringsTable stbl = new SharedStringsTable(parts.get(0));
ReadOnlySharedStringsTable rtbl = new ReadOnlySharedStringsTable(parts.get(0));
ReadOnlySharedStringsTable rtbl2;
try (InputStream stream = parts.get(0).getInputStream()){
rtbl2 = new ReadOnlySharedStringsTable(stream);
}

assertEquals(stbl.getCount(), rtbl.getCount());
assertEquals(stbl.getUniqueCount(), rtbl.getUniqueCount());
assertEquals(stbl.getUniqueCount(), rtbl2.getUniqueCount());

assertEquals(stbl.getCount(), stbl.getUniqueCount());
assertEquals(rtbl.getCount(), rtbl.getUniqueCount());
assertEquals(rtbl.getCount(), rtbl2.getUniqueCount());
for (int i = 0; i < stbl.getUniqueCount(); i++) {
RichTextString i1 = stbl.getItemAt(i);
assertEquals(i1.getString(), rtbl.getItemAt(i).getString());
assertEquals(i1.getString(), rtbl2.getItemAt(i).getString());
try (SharedStringsTable stbl = new SharedStringsTable(parts.get(0))) {
ReadOnlySharedStringsTable rtbl = new ReadOnlySharedStringsTable(parts.get(0));
ReadOnlySharedStringsTable rtbl2;
try (InputStream stream = parts.get(0).getInputStream()) {
rtbl2 = new ReadOnlySharedStringsTable(stream);
}

assertEquals(stbl.getCount(), rtbl.getCount());
assertEquals(stbl.getUniqueCount(), rtbl.getUniqueCount());
assertEquals(stbl.getUniqueCount(), rtbl2.getUniqueCount());

assertEquals(stbl.getCount(), stbl.getUniqueCount());
assertEquals(rtbl.getCount(), rtbl.getUniqueCount());
assertEquals(rtbl.getCount(), rtbl2.getUniqueCount());
for (int i = 0; i < stbl.getUniqueCount(); i++) {
RichTextString i1 = stbl.getItemAt(i);
assertEquals(i1.getString(), rtbl.getItemAt(i).getString());
assertEquals(i1.getString(), rtbl2.getItemAt(i).getString());
}
} }
} }
} }
List<PackagePart> parts = pkg.getPartsByName(Pattern.compile("/xl/sharedStrings.xml")); List<PackagePart> parts = pkg.getPartsByName(Pattern.compile("/xl/sharedStrings.xml"));
assertEquals(1, parts.size()); assertEquals(1, parts.size());


SharedStringsTable stbl = new SharedStringsTable(parts.get(0));
ReadOnlySharedStringsTable rtbl = new ReadOnlySharedStringsTable(parts.get(0));
ReadOnlySharedStringsTable rtbl2;
try (InputStream stream = parts.get(0).getInputStream()) {
rtbl2 = new ReadOnlySharedStringsTable(stream);
}

assertEquals(stbl.getCount(), rtbl.getCount());
assertEquals(stbl.getUniqueCount(), rtbl.getUniqueCount());
assertEquals(stbl.getUniqueCount(), rtbl2.getUniqueCount());
for (int i = 0; i < stbl.getUniqueCount(); i++) {
RichTextString i1 = stbl.getItemAt(i);
assertEquals(i1.getString(), rtbl.getItemAt(i).getString());
assertEquals(i1.getString(), rtbl2.getItemAt(i).getString());
try (SharedStringsTable stbl = new SharedStringsTable(parts.get(0))) {
ReadOnlySharedStringsTable rtbl = new ReadOnlySharedStringsTable(parts.get(0));
ReadOnlySharedStringsTable rtbl2;
try (InputStream stream = parts.get(0).getInputStream()) {
rtbl2 = new ReadOnlySharedStringsTable(stream);
}

assertEquals(stbl.getCount(), rtbl.getCount());
assertEquals(stbl.getUniqueCount(), rtbl.getUniqueCount());
assertEquals(stbl.getUniqueCount(), rtbl2.getUniqueCount());
for (int i = 0; i < stbl.getUniqueCount(); i++) {
RichTextString i1 = stbl.getItemAt(i);
assertEquals(i1.getString(), rtbl.getItemAt(i).getString());
assertEquals(i1.getString(), rtbl2.getItemAt(i).getString());
}
} }
} }
} }
} }
} }


@Test
void testNullPointerException() throws Exception {
try (OPCPackage pkg = OPCPackage.open(_ssTests.openResourceAsStream("clusterfuzz-testcase-minimized-XLSX2CSVFuzzer-5025401116950528.xlsx"))) {
assertEmptySST(pkg);
}

try (OPCPackage pkg = OPCPackage.open(_ssTests.openResourceAsStream("clusterfuzz-testcase-minimized-XLSX2CSVFuzzer-5025401116950528.xlsx"))) {
List<PackagePart> parts = pkg.getPartsByName(Pattern.compile("/xl/sharedStrings.xml"));
assertEquals(1, parts.size());

//noinspection resource
assertThrows(IOException.class,
() -> new SharedStringsTable(parts.get(0)));
}
}

private void assertEmptySST(OPCPackage pkg) throws IOException, SAXException { private void assertEmptySST(OPCPackage pkg) throws IOException, SAXException {
ReadOnlySharedStringsTable sst = new ReadOnlySharedStringsTable(pkg); ReadOnlySharedStringsTable sst = new ReadOnlySharedStringsTable(pkg);
assertEquals(0, sst.getCount()); assertEquals(0, sst.getCount());

BIN
test-data/spreadsheet/clusterfuzz-testcase-minimized-XLSX2CSVFuzzer-5025401116950528.xlsx View File


BIN
test-data/spreadsheet/stress.xls View File


Loading…
Cancel
Save