Implement lazy allocation of data for complex escher properties

This may reduce memory usage if the content is never used
or not populated for some reason.

This should fix https://issues.oss-fuzz.com/issues/42528528
This commit is contained in:
Dominik Stadler 2026-01-09 16:05:12 +01:00
parent cc8b6d7591
commit 017ff5f8f8
7 changed files with 50 additions and 10 deletions

View File

@ -61,7 +61,8 @@ public class TestWordToConverterSuite {
"clusterfuzz-testcase-minimized-POIHWPFFuzzer-4947285593948160.doc",
"clusterfuzz-testcase-minimized-POIHWPFFuzzer-5440721166139392.doc",
"clusterfuzz-testcase-minimized-POIHWPFFuzzer-5050208641482752.doc",
"clusterfuzz-testcase-minimized-POIHWPFFuzzer-6610789829836800.doc"
"clusterfuzz-testcase-minimized-POIHWPFFuzzer-6610789829836800.doc",
"clusterfuzz-testcase-minimized-POIHWPFFuzzer-5832867957309440.doc"
);
public static Stream<Arguments> files() {

View File

@ -54,7 +54,8 @@ public class TestWordToTextConverter {
"clusterfuzz-testcase-minimized-POIHWPFFuzzer-4947285593948160.doc",
"clusterfuzz-testcase-minimized-POIHWPFFuzzer-5440721166139392.doc",
"clusterfuzz-testcase-minimized-POIHWPFFuzzer-5050208641482752.doc",
"clusterfuzz-testcase-minimized-POIHWPFFuzzer-6610789829836800.doc"
"clusterfuzz-testcase-minimized-POIHWPFFuzzer-6610789829836800.doc",
"clusterfuzz-testcase-minimized-POIHWPFFuzzer-5832867957309440.doc"
);
/**

View File

@ -35,6 +35,7 @@ public class EscherComplexProperty extends EscherProperty {
private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000_000;
private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH;
private int complexSize;
private byte[] complexData;
/**
@ -61,7 +62,15 @@ public class EscherComplexProperty extends EscherProperty {
*/
public EscherComplexProperty(short id, int complexSize) {
super((short)(id | IS_COMPLEX));
complexData = IOUtils.safelyAllocate(complexSize, MAX_RECORD_LENGTH);
// lazy-allocate the data
this.complexSize = complexSize;
}
private void ensureComplexData() {
if (this.complexData == null) {
complexData = IOUtils.safelyAllocate(complexSize, MAX_RECORD_LENGTH);
}
}
/**
@ -94,7 +103,7 @@ public class EscherComplexProperty extends EscherProperty {
@Override
public int serializeSimplePart(byte[] data, int pos) {
LittleEndian.putShort(data, pos, getId());
LittleEndian.putInt(data, pos + 2, complexData.length);
LittleEndian.putInt(data, pos + 2, complexSize);
return 6;
}
@ -107,8 +116,13 @@ public class EscherComplexProperty extends EscherProperty {
*/
@Override
public int serializeComplexPart(byte[] data, int pos) {
System.arraycopy(complexData, 0, data, pos, complexData.length);
return complexData.length;
if (complexData == null) {
// initialize empty array if complexData was never allocated
Arrays.fill(data, pos, pos + complexSize, (byte)0);
} else {
System.arraycopy(complexData, 0, data, pos, complexData.length);
}
return complexSize;
}
/**
@ -117,6 +131,7 @@ public class EscherComplexProperty extends EscherProperty {
* @return the complex bytes
*/
public byte[] getComplexData() {
ensureComplexData();
return complexData;
}
@ -128,25 +143,31 @@ public class EscherComplexProperty extends EscherProperty {
if (complexData == null) {
return 0;
} else {
ensureComplexData();
int copySize = Math.max(0, Math.min(this.complexData.length, complexData.length - offset));
System.arraycopy(complexData, offset, this.complexData, 0, copySize);
return copySize;
}
}
protected void resizeComplexData(int newSize) {
resizeComplexData(newSize, Integer.MAX_VALUE);
}
protected void resizeComplexData(int newSize, int copyLen) {
if (newSize == complexData.length) {
if (newSize == complexSize) {
return;
}
// no need to copy if data was not initialized yet
if (complexData == null) {
return;
}
byte[] newArray = IOUtils.safelyAllocate(newSize, MAX_RECORD_LENGTH);
System.arraycopy(complexData, 0, newArray, 0, Math.min(Math.min(complexData.length, copyLen),newSize));
complexData = newArray;
complexSize = newSize;
}
/**
@ -166,6 +187,18 @@ public class EscherComplexProperty extends EscherProperty {
EscherComplexProperty escherComplexProperty = (EscherComplexProperty) o;
// not equal if size differs
if (complexSize != escherComplexProperty.complexSize) {
return false;
}
if (complexData == null) {
// if coomplexData is not initialized, it is equal only if
// complexData is also uninitialized or equals an empty array
return escherComplexProperty.complexData == null ||
Arrays.equals(escherComplexProperty.complexData, new byte[escherComplexProperty.complexSize]);
}
return Arrays.equals(complexData, escherComplexProperty.complexData);
}
@ -176,16 +209,18 @@ public class EscherComplexProperty extends EscherProperty {
*/
@Override
public int getPropertySize() {
return 6 + complexData.length;
return 6 + complexSize;
}
@Override
public int hashCode() {
ensureComplexData();
return Arrays.deepHashCode(new Object[]{complexData, getId()});
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
ensureComplexData();
return GenericRecordUtil.getGenericProperties(
"base", super::getGenericProperties,
"data", this::getComplexData

View File

@ -2551,3 +2551,5 @@ org/openxmlformats/schemas/drawingml/x2006/main/impl/CTAudioFileImpl
org/openxmlformats/schemas/drawingml/x2006/main/CTAudioFile
org/openxmlformats/schemas/drawingml/x2006/chart/impl/STHoleSizePercentImpl
org/openxmlformats/schemas/drawingml/x2006/chart/impl/STHoleSizeUByteImpl
org/openxmlformats/schemas/drawingml/x2006/main/CTSupplementalFont
org/openxmlformats/schemas/drawingml/x2006/main/impl/CTSupplementalFontImpl

View File

@ -1186,3 +1186,4 @@ ctindex5371type
stholesizepercenta3d2type
stholesizeubyte577atype
chartspace67aadoctype
ctsupplementalfonta06etype

Binary file not shown.