Skip to content

Commit 2c6c414

Browse files
committed
Add safety check for max allowed offset in xref table
DEVSIX-8052
1 parent 916acd9 commit 2c6c414

File tree

3 files changed

+91
-2
lines changed

3 files changed

+91
-2
lines changed

kernel/src/main/java/com/itextpdf/kernel/exceptions/KernelExceptionMessageConstant.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,5 +435,10 @@ public final class KernelExceptionMessageConstant {
435435
"Failed to open the document. Security handler {0} is not supported";
436436

437437
public static final String ARG_SHOULD_NOT_BE_NULL = "{0} should not be null.";
438-
private KernelExceptionMessageConstant(){}
438+
public static final String XREF_HAS_AN_ENTRY_WITH_TOO_BIG_OFFSET = "Pdf document is to large to "
439+
+ "use normal cross reference table. Use cross reference streams instead. To enable feature use com.itextpdf"
440+
+ ".kernel.pdf.WriterProperties#setFullCompressionMode(true). ";
441+
442+
private KernelExceptionMessageConstant() {
443+
}
439444
}

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfXrefTable.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ public class PdfXrefTable {
5050
private static final int INITIAL_CAPACITY = 32;
5151
private static final int MAX_GENERATION = 65535;
5252

53+
/**
54+
* The maximum offset in a cross-reference stream. This is a limitation of the PDF specification.
55+
* SPEC1.7: 7.5.4 Cross reference trailer
56+
* <p>
57+
*
58+
* It states that the offset should be a 10-digit byte, so the maximum value is 9999999999.
59+
* This is the max value that can be represented in 10 bytes.
60+
*/
61+
private static final long MAX_OFFSET_IN_CROSS_REFERENCE_STREAM = 9_999_999_999L;
5362
private static final byte[] freeXRefEntry = ByteUtils.getIsoBytes("f \n");
5463
private static final byte[] inUseXRefEntry = ByteUtils.getIsoBytes("n \n");
5564

@@ -368,7 +377,9 @@ protected void writeXrefTableAndTrailer(PdfDocument document, PdfObject fileId,
368377
writer.writeInteger(first).writeSpace().writeInteger(len).writeByte((byte) '\n');
369378
for (int i = first; i < first + len; i++) {
370379
PdfIndirectReference reference = xrefTable.get(i);
371-
380+
if (reference.getOffset() > MAX_OFFSET_IN_CROSS_REFERENCE_STREAM) {
381+
throw new PdfException(KernelExceptionMessageConstant.XREF_HAS_AN_ENTRY_WITH_TOO_BIG_OFFSET);
382+
}
372383
StringBuilder off = new StringBuilder("0000000000").append(reference.getOffset());
373384
StringBuilder gen = new StringBuilder("00000").append(reference.getGenNumber());
374385
writer.writeString(off.substring(off.length() - 10, off.length())).writeSpace().

kernel/src/test/java/com/itextpdf/kernel/pdf/PdfXrefTableUnitTest.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.kernel.pdf;
2424

25+
import com.itextpdf.io.source.ByteArrayOutputStream;
2526
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
2627
import com.itextpdf.kernel.exceptions.MemoryLimitsAwareException;
28+
import com.itextpdf.kernel.exceptions.PdfException;
29+
import com.itextpdf.test.AssertUtil;
30+
import com.itextpdf.test.ExceptionTestUtil;
2731
import com.itextpdf.test.ExtendedITextTest;
2832
import com.itextpdf.test.annotations.type.UnitTest;
2933

@@ -128,4 +132,73 @@ public void zeroCapacityInConstructorWithHandlerTest() {
128132

129133
Assert.assertEquals(20, xrefTable.getCapacity());
130134
}
135+
136+
@Test
137+
public void xRefMaxValueLong() {
138+
PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));
139+
document.xref.add(new PdfIndirectReferenceProxy(document, 11, Long.MAX_VALUE));
140+
141+
Exception e = Assert.assertThrows(PdfException.class, () -> {
142+
document.close();
143+
});
144+
Assert.assertEquals(KernelExceptionMessageConstant.XREF_HAS_AN_ENTRY_WITH_TOO_BIG_OFFSET, e.getMessage());
145+
}
146+
147+
148+
@Test
149+
public void maxCrossReferenceOffSetReached() {
150+
long justOver10gbLogical = 10_000_000_001L;
151+
PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));
152+
document.xref.add(new PdfIndirectReferenceProxy(document, 11, justOver10gbLogical));
153+
154+
Exception e = Assert.assertThrows(PdfException.class, () -> {
155+
document.close();
156+
});
157+
Assert.assertEquals(KernelExceptionMessageConstant.XREF_HAS_AN_ENTRY_WITH_TOO_BIG_OFFSET, e.getMessage());
158+
}
159+
160+
@Test
161+
public void maxCrossReference() {
162+
long justOver10gbLogical = 10_000_000_000L;
163+
PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));
164+
document.xref.add(new PdfIndirectReferenceProxy(document, 11, justOver10gbLogical));
165+
166+
Exception e = Assert.assertThrows(PdfException.class, () -> {
167+
document.close();
168+
});
169+
Assert.assertEquals(KernelExceptionMessageConstant.XREF_HAS_AN_ENTRY_WITH_TOO_BIG_OFFSET, e.getMessage());
170+
}
171+
172+
@Test
173+
public void justBelowXrefThreshold() {
174+
long maxAllowedOffset = 10_000_000_000L - 1L;
175+
PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));
176+
document.xref.add(new PdfIndirectReferenceProxy(document, 11, maxAllowedOffset));
177+
178+
AssertUtil.doesNotThrow(() -> document.close());
179+
}
180+
181+
@Test
182+
public void xRefIntMax() {
183+
PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));
184+
document.xref.add(new PdfIndirectReferenceProxy(document, 11, Integer.MAX_VALUE));
185+
AssertUtil.doesNotThrow(() -> document.close());
186+
}
187+
188+
189+
190+
191+
}
192+
class PdfIndirectReferenceProxy extends PdfIndirectReference {
193+
private final long offset;
194+
195+
public PdfIndirectReferenceProxy(PdfDocument document, int objNumber, long offset) {
196+
super(document, objNumber);
197+
this.offset = offset;
198+
}
199+
200+
@Override
201+
public long getOffset() {
202+
return offset;
203+
}
131204
}

0 commit comments

Comments
 (0)