Add check for too large allocation in SharedFormulaGroup

A malformed spreadsheet could trigger a very large allocation.

Can be overruled by users via IOUtils.setByteArrayMaxOverride().

Fixes https://issues.oss-fuzz.com/issues/476431391
This commit is contained in:
Dominik Stadler 2026-01-18 17:38:48 +01:00
parent d1f0a88ea1
commit d1f3f9489f
8 changed files with 17 additions and 5 deletions

View File

@ -31,6 +31,7 @@ import org.apache.poi.hssf.record.TableRecord;
import org.apache.poi.ss.formula.ptg.ExpPtg; import org.apache.poi.ss.formula.ptg.ExpPtg;
import org.apache.poi.hssf.util.CellRangeAddress8Bit; import org.apache.poi.hssf.util.CellRangeAddress8Bit;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.IOUtils;
/** /**
* Manages various auxiliary records while constructing a * Manages various auxiliary records while constructing a
@ -42,6 +43,7 @@ import org.apache.poi.ss.util.CellReference;
* </ul> * </ul>
*/ */
public final class SharedValueManager { public final class SharedValueManager {
private static final int MAX_NUMBER_AGGS = 10_000;
private static final class SharedFormulaGroup { private static final class SharedFormulaGroup {
private final SharedFormulaRecord _sfr; private final SharedFormulaRecord _sfr;
@ -63,7 +65,12 @@ public final class SharedValueManager {
_firstCell = firstCell; _firstCell = firstCell;
int width = sfr.getLastColumn() - sfr.getFirstColumn() + 1; int width = sfr.getLastColumn() - sfr.getFirstColumn() + 1;
int height = sfr.getLastRow() - sfr.getFirstRow() + 1; int height = sfr.getLastRow() - sfr.getFirstRow() + 1;
_frAggs = new FormulaRecordAggregate[width * height];
// ensure we do not try to initialize a very large amount of formula-record-aggregates
int allocateSize = width * height;
IOUtils.safelyAllocateCheck(allocateSize, MAX_NUMBER_AGGS);
_frAggs = new FormulaRecordAggregate[allocateSize];
_numberOfFormulas = 0; _numberOfFormulas = 0;
} }

View File

@ -460,7 +460,7 @@ public final class IOUtils {
public static long copy(InputStream srcStream, File destFile) throws IOException { public static long copy(InputStream srcStream, File destFile) throws IOException {
File destDirectory = destFile.getParentFile(); File destDirectory = destFile.getParentFile();
if (!(destDirectory.exists() || destDirectory.mkdirs())) { if (!(destDirectory.exists() || destDirectory.mkdirs())) {
throw new IllegalStateException("Can't create destination directory: "+destDirectory); throw new IllegalStateException("Can't create destination directory: " + destDirectory);
} }
try (OutputStream destStream = Files.newOutputStream(destFile.toPath())) { try (OutputStream destStream = Files.newOutputStream(destFile.toPath())) {
return IOUtils.copy(srcStream, destStream); return IOUtils.copy(srcStream, destStream);
@ -581,7 +581,7 @@ public final class IOUtils {
throw new RecordFormatException("Can't allocate an array of length < 0, but had " + length + " and " + maxLength); throw new RecordFormatException("Can't allocate an array of length < 0, but had " + length + " and " + maxLength);
} }
if (length > (long)Integer.MAX_VALUE) { if (length > (long)Integer.MAX_VALUE) {
throw new RecordFormatException("Can't allocate an array > "+Integer.MAX_VALUE); throw new RecordFormatException("Can't allocate an array > " + Integer.MAX_VALUE);
} }
checkLength(length, maxLength); checkLength(length, maxLength);
} }
@ -643,7 +643,7 @@ public final class IOUtils {
throw new RecordFormatException(String.format(Locale.ROOT, "Tried to allocate an array of length %,d" + throw new RecordFormatException(String.format(Locale.ROOT, "Tried to allocate an array of length %,d" +
", but the maximum length for this record type is %,d.%n" + ", but the maximum length for this record type is %,d.%n" +
"If the file is not corrupt and not large, please open an issue on bugzilla to request %n" + "If the file is not corrupt and not large, please open an issue on bugzilla to request %n" +
"increasing the maximum allowable size for this record type.%n"+ "increasing the maximum allowable size for this record type.%n" +
"You can set a higher override value with IOUtils.setByteArrayMaxOverride()", length, maxLength)); "You can set a higher override value with IOUtils.setByteArrayMaxOverride()", length, maxLength));
} }
@ -651,7 +651,7 @@ public final class IOUtils {
throw new RecordFormatException(String.format(Locale.ROOT, "Tried to read data but the maximum length " + throw new RecordFormatException(String.format(Locale.ROOT, "Tried to read data but the maximum length " +
"for this record type is %,d.%n" + "for this record type is %,d.%n" +
"If the file is not corrupt and not large, please open an issue on bugzilla to request %n" + "If the file is not corrupt and not large, please open an issue on bugzilla to request %n" +
"increasing the maximum allowable size for this record type.%n"+ "increasing the maximum allowable size for this record type.%n" +
"You can set a higher override value with IOUtils.setByteArrayMaxOverride()", maxLength)); "You can set a higher override value with IOUtils.setByteArrayMaxOverride()", maxLength));
} }
} }

View File

@ -36,6 +36,7 @@ import org.apache.poi.hssf.usermodel.HSSFPatriarch;
import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.util.RecordFormatException;
import org.apache.poi.util.StringUtil; import org.apache.poi.util.StringUtil;
import org.apache.tools.ant.util.NullOutputStream; import org.apache.tools.ant.util.NullOutputStream;
@ -57,6 +58,7 @@ class TestBiffDrawingToXml extends BaseTestIteratingXLS {
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5436547081830400.xls", IllegalArgumentException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5436547081830400.xls", IllegalArgumentException.class);
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5889658057523200.xls", IndexOutOfBoundsException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5889658057523200.xls", IndexOutOfBoundsException.class);
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4977868385681408.xls", IllegalArgumentException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4977868385681408.xls", IllegalArgumentException.class);
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4734163573080064.xls", RecordFormatException.class);
return excludes; return excludes;
} }

View File

@ -48,6 +48,7 @@ class TestBiffViewer extends BaseTestIteratingXLS {
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6137883240824832.xls", IndexOutOfBoundsException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6137883240824832.xls", IndexOutOfBoundsException.class);
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6483562584932352.xls", IndexOutOfBoundsException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6483562584932352.xls", IndexOutOfBoundsException.class);
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5816431116615680.xls", RecordFormatException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5816431116615680.xls", RecordFormatException.class);
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4734163573080064.xls", IndexOutOfBoundsException.class);
return excludes; return excludes;
} }

View File

@ -45,6 +45,7 @@ class TestFormulaViewer extends BaseTestIteratingXLS {
excludes.put("43493.xls", RecordInputStream.LeftoverDataException.class); // HSSFWorkbook cannot open it as well excludes.put("43493.xls", RecordInputStream.LeftoverDataException.class); // HSSFWorkbook cannot open it as well
excludes.put("44958_1.xls", RecordInputStream.LeftoverDataException.class); excludes.put("44958_1.xls", RecordInputStream.LeftoverDataException.class);
excludes.put("protected_66115.xls", EncryptedDocumentException.class); excludes.put("protected_66115.xls", EncryptedDocumentException.class);
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4734163573080064.xls", IndexOutOfBoundsException.class);
return excludes; return excludes;
} }

View File

@ -51,6 +51,7 @@ class TestRecordLister extends BaseTestIteratingXLS {
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6137883240824832.xls", RecordFormatException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6137883240824832.xls", RecordFormatException.class);
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6483562584932352.xls", RecordFormatException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6483562584932352.xls", RecordFormatException.class);
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5816431116615680.xls", RecordFormatException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5816431116615680.xls", RecordFormatException.class);
excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4734163573080064.xls", IndexOutOfBoundsException.class);
return excludes; return excludes;
} }

Binary file not shown.