Skip to content

Commit 3213363

Browse files
Egor MartsynkovskyUbuntu
authored andcommitted
Fix OOM on reading raw bytes stream
DEVSIX-6244
1 parent a9af64d commit 3213363

File tree

6 files changed

+64
-20
lines changed

6 files changed

+64
-20
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ public final class KernelExceptionMessageConstant {
271271
+ "do not contain ExtGState entry. Unable to process operator {0}.";
272272
public static final String SHADING_TYPE_NOT_FOUND = "Shading type not found.";
273273
public static final String STDCF_NOT_FOUND_ENCRYPTION = "/StdCF not found (encryption)";
274+
public static final String STREAM_SHALL_END_WITH_ENDSTREAM = "Stream shall end with endstream keyword.";
274275
public static final String STRUCT_PARENT_INDEX_NOT_FOUND_IN_TAGGED_OBJECT = "StructParent index not found in "
275276
+ "tagged object.";
276277
public static final String STRUCTURE_ELEMENT_IN_STRUCTURE_DESTINATION_SHALL_BE_AN_INDIRECT_OBJECT = "Structure "

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,9 @@ public byte[] readStreamBytes(PdfStream stream, boolean decode) throws IOExcepti
382382
*/
383383
public byte[] readStreamBytesRaw(PdfStream stream) throws IOException {
384384
PdfName type = stream.getAsName(PdfName.Type);
385-
if (!PdfName.XRefStm.equals(type) && !PdfName.ObjStm.equals(type))
385+
if (!PdfName.XRef.equals(type) && !PdfName.ObjStm.equals(type)) {
386386
checkPdfStreamLength(stream);
387+
}
387388
long offset = stream.getOffset();
388389
if (offset <= 0)
389390
return null;
@@ -393,7 +394,7 @@ public byte[] readStreamBytesRaw(PdfStream stream) throws IOException {
393394
RandomAccessFileOrArray file = tokens.getSafeFile();
394395
byte[] bytes = null;
395396
try {
396-
file.seek(stream.getOffset());
397+
file.seek(offset);
397398
bytes = new byte[length];
398399
file.readFully(bytes);
399400
boolean embeddedStream = pdfDocument.doesStreamBelongToEmbeddedFile(stream);
@@ -1461,21 +1462,24 @@ private void checkPdfStreamLength(PdfStream pdfStream) throws IOException {
14611462
line.reset();
14621463

14631464
// added boolean because of mailing list issue (17 Feb. 2014)
1464-
if (!tokens.readLineSegment(line, false))
1465+
if (!tokens.readLineSegment(line, false)) {
1466+
if (!StrictnessLevel.CONSERVATIVE.isStricter(this.strictnessLevel)) {
1467+
throw new PdfException(KernelExceptionMessageConstant.STREAM_SHALL_END_WITH_ENDSTREAM);
1468+
}
14651469
break;
1470+
}
14661471
if (line.startsWith(endstream)) {
1467-
streamLength = (int) (pos - start);
14681472
break;
14691473
} else if (line.startsWith(endobj)) {
14701474
tokens.seek(pos - 16);
14711475
String s = tokens.readString(16);
14721476
int index = s.indexOf(endstream1);
14731477
if (index >= 0)
14741478
pos = pos - 16 + index;
1475-
streamLength = (int) (pos - start);
14761479
break;
14771480
}
14781481
}
1482+
streamLength = (int) (pos - start);
14791483
tokens.seek(pos - 2);
14801484
if (tokens.read() == 13) {
14811485
streamLength--;

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

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ This file is part of the iText (R) project.
6060
@Category(IntegrationTest.class)
6161
public class PdfReaderDecodeTest extends ExtendedITextTest {
6262

63-
public static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/pdf/PdfReaderDecodeTest/";
63+
public static final String SOURCE_FOLDER = "./src/test/resources/com/itextpdf/kernel/pdf/PdfReaderDecodeTest/";
6464

6565
@Test
6666
public void noMemoryHandlerTest() throws IOException {
6767
try (PdfDocument pdfDocument = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));
68-
FileInputStream is = new FileInputStream(sourceFolder + "stream")) {
68+
FileInputStream is = new FileInputStream(SOURCE_FOLDER + "stream")) {
6969
byte[] b = new byte[51];
7070
is.read(b);
7171

@@ -98,7 +98,7 @@ public void noMemoryHandlerTest() throws IOException {
9898
})
9999
public void defaultMemoryHandlerTest() throws IOException {
100100
try (PdfDocument pdfDocument = new PdfDocument(
101-
new PdfReader(sourceFolder + "timing.pdf"),
101+
new PdfReader(SOURCE_FOLDER + "timing.pdf"),
102102
new PdfWriter(new ByteArrayOutputStream()))) {
103103
PdfStream stream = pdfDocument.getFirstPage().getContentStream(0);
104104
byte[] b = stream.getBytes(false);
@@ -129,7 +129,7 @@ public void customMemoryHandlerSingleTest() throws IOException {
129129
handler.setMaxSizeOfSingleDecompressedPdfStream(1000);
130130

131131
try (PdfDocument pdfDocument = new PdfDocument(
132-
new PdfReader(sourceFolder + "timing.pdf",
132+
new PdfReader(SOURCE_FOLDER + "timing.pdf",
133133
new ReaderProperties().setMemoryLimitsAwareHandler(handler)),
134134
new PdfWriter(new ByteArrayOutputStream()))) {
135135

@@ -166,7 +166,7 @@ public void oneFilterCustomMemoryHandlerSingleTest() throws IOException {
166166
handler.setMaxSizeOfSingleDecompressedPdfStream(20);
167167

168168
try (PdfDocument pdfDocument = new PdfDocument(
169-
new PdfReader(sourceFolder + "timing.pdf",
169+
new PdfReader(SOURCE_FOLDER + "timing.pdf",
170170
new ReaderProperties().setMemoryLimitsAwareHandler(handler)),
171171
new PdfWriter(new ByteArrayOutputStream()))) {
172172

@@ -176,10 +176,10 @@ public void oneFilterCustomMemoryHandlerSingleTest() throws IOException {
176176
PdfArray array = new PdfArray();
177177
stream.put(PdfName.Filter, array);
178178

179-
// Limit is reached, but the stream has no filters. Therefore we don't consider ot to be suspicious
179+
// Limit is reached, but the stream has no filters. Therefore, we don't consider it to be suspicious.
180180
Assert.assertEquals(51, PdfReader.decodeBytes(b, stream).length);
181181

182-
// Limit is reached, but the stream has only one filter. Therefore we don't consider ot to be suspicious
182+
// Limit is reached, but the stream has only one filter. Therefore, we don't consider it to be suspicious.
183183
array.add(PdfName.Fl);
184184
Assert.assertEquals(40, PdfReader.decodeBytes(b, stream).length);
185185
}
@@ -200,7 +200,7 @@ public boolean isMemoryLimitsAwarenessRequiredOnDecompression(PdfArray filters)
200200
handler.setMaxSizeOfSingleDecompressedPdfStream(20);
201201

202202
try (PdfDocument pdfDocument = new PdfDocument(
203-
new PdfReader(sourceFolder + "timing.pdf",
203+
new PdfReader(SOURCE_FOLDER + "timing.pdf",
204204
new ReaderProperties().setMemoryLimitsAwareHandler(handler)),
205205
new PdfWriter(new ByteArrayOutputStream()))) {
206206

@@ -211,7 +211,7 @@ public boolean isMemoryLimitsAwarenessRequiredOnDecompression(PdfArray filters)
211211
stream.put(PdfName.Filter, array);
212212
array.add(PdfName.Fl);
213213

214-
// Limit is reached, and the stream with one filter is considered to be suspicious
214+
// Limit is reached, and the stream with one filter is considered to be suspicious.
215215
Exception e = Assert.assertThrows(MemoryLimitsAwareException.class,
216216
() -> PdfReader.decodeBytes(b, stream)
217217
);
@@ -235,7 +235,7 @@ public boolean isMemoryLimitsAwarenessRequiredOnDecompression(PdfArray filters)
235235
handler.setMaxSizeOfSingleDecompressedPdfStream(20);
236236

237237
try (PdfDocument pdfDocument = new PdfDocument(
238-
new PdfReader(sourceFolder + "timing.pdf",
238+
new PdfReader(SOURCE_FOLDER + "timing.pdf",
239239
new ReaderProperties().setMemoryLimitsAwareHandler(handler)),
240240
new PdfWriter(new ByteArrayOutputStream()))) {
241241

@@ -247,8 +247,7 @@ public boolean isMemoryLimitsAwarenessRequiredOnDecompression(PdfArray filters)
247247
array.add(PdfName.Fl);
248248
array.add(PdfName.Fl);
249249

250-
// Limit is reached but the stream with several copies of the filter is not considered
251-
// to be suspicious
250+
// Limit is reached but the stream with several copies of the filter is not considered to be suspicious.
252251
PdfReader.decodeBytes(b, stream);
253252
}
254253
}
@@ -279,7 +278,7 @@ public void customMemoryHandlerSumTest() throws IOException {
279278
handler.setMaxSizeOfDecompressedPdfStreamsSum(100000);
280279

281280
try (PdfDocument pdfDocument = new PdfDocument(
282-
new PdfReader(sourceFolder + "timing.pdf",
281+
new PdfReader(SOURCE_FOLDER + "timing.pdf",
283282
new ReaderProperties().setMemoryLimitsAwareHandler(handler)),
284283
new PdfWriter(new ByteArrayOutputStream()))) {
285284

@@ -303,7 +302,7 @@ public void pageSumTest() throws IOException {
303302
handler.setMaxSizeOfDecompressedPdfStreamsSum(1500000);
304303

305304
try (PdfDocument pdfDocument = new PdfDocument(
306-
new PdfReader(sourceFolder + "timing.pdf",
305+
new PdfReader(SOURCE_FOLDER + "timing.pdf",
307306
new ReaderProperties().setMemoryLimitsAwareHandler(handler)),
308307
new PdfWriter(new ByteArrayOutputStream()))) {
309308

@@ -324,7 +323,7 @@ public void pageAsSingleStreamTest() throws IOException {
324323
handler.setMaxSizeOfSingleDecompressedPdfStream(1500000);
325324

326325
try (PdfDocument pdfDocument = new PdfDocument(
327-
new PdfReader(sourceFolder + "timing.pdf",
326+
new PdfReader(SOURCE_FOLDER + "timing.pdf",
328327
new ReaderProperties().setMemoryLimitsAwareHandler(handler)),
329328
new PdfWriter(new ByteArrayOutputStream()))) {
330329

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ This file is part of the iText (R) project.
5757
import com.itextpdf.kernel.exceptions.XrefCycledReferencesException;
5858
import com.itextpdf.kernel.pdf.PdfReader.StrictnessLevel;
5959
import com.itextpdf.kernel.utils.CompareTool;
60+
import com.itextpdf.kernel.xmp.XMPException;
6061
import com.itextpdf.test.AssertUtil;
6162
import com.itextpdf.test.ExtendedITextTest;
6263
import com.itextpdf.test.annotations.LogMessage;
@@ -2518,6 +2519,45 @@ public void readPdfInvalidPrevConservativeModeTest() throws IOException {
25182519
}
25192520
}
25202521

2522+
@Test
2523+
public void streamWithoutEndstreamKeywordTest() throws IOException, XMPException {
2524+
final String fileName = SOURCE_FOLDER + "NoEndstreamKeyword.pdf";
2525+
try (PdfReader reader = new PdfReader(fileName)) {
2526+
reader.setStrictnessLevel(StrictnessLevel.LENIENT);
2527+
try (PdfDocument document = new PdfDocument(reader)) {
2528+
final PdfCatalog catalog = new PdfCatalog((PdfDictionary) reader.trailer
2529+
.get(PdfName.Root, true));
2530+
final PdfStream xmpMetadataStream = catalog.getPdfObject().getAsStream(PdfName.Metadata);
2531+
final int xmpMetadataStreamLength = ((PdfNumber) xmpMetadataStream.get(PdfName.Length)).intValue();
2532+
2533+
// 27600 is actual invalid length of stream. In reader StrictnessLevel#LENIENT we expect, that this
2534+
// length will be fixed.
2535+
Assert.assertNotEquals(27600, xmpMetadataStreamLength);
2536+
2537+
// 3090 is expected length of the stream after fix.
2538+
Assert.assertEquals(3090, xmpMetadataStreamLength);
2539+
}
2540+
}
2541+
}
2542+
2543+
@Test
2544+
public void streamWithoutEndstreamKeywordConservativeModeTest() throws IOException, XMPException {
2545+
final String fileName = SOURCE_FOLDER + "NoEndstreamKeyword.pdf";
2546+
try (PdfReader reader = new PdfReader(fileName)) {
2547+
reader.setStrictnessLevel(StrictnessLevel.CONSERVATIVE);
2548+
2549+
Exception exception = Assert.assertThrows(PdfException.class, () -> new PdfDocument(reader));
2550+
Assert.assertEquals(KernelExceptionMessageConstant.STREAM_SHALL_END_WITH_ENDSTREAM, exception.getMessage());
2551+
2552+
PdfCatalog catalog = new PdfCatalog((PdfDictionary) reader.trailer.get(PdfName.Root, true));
2553+
PdfStream xmpMetadataStream = catalog.getPdfObject().getAsStream(PdfName.Metadata);
2554+
2555+
// 27600 is actual invalid length of stream. In reader StrictnessLevel#CONSERVATIVE we expect, that
2556+
// exception would be thrown and length wouldn't be fixed.
2557+
Assert.assertEquals(27600, ((PdfNumber) xmpMetadataStream.get(PdfName.Length)).intValue());
2558+
}
2559+
}
2560+
25212561
/**
25222562
* Returns the current memory use.
25232563
*

0 commit comments

Comments
 (0)