Add runtime check for streams in CPDF_Dictionary::SetForInternal()
Disallow objects of CPDF_Stream to be directly added to CPDF_Dictionary
at runtime using a CHECK(). Fix the CPDF_SyntaxParser caller that
violates this check to make the existing test suites pass.
Additionally, include a crafted PDF that also demonstrates this issue as
a pixel test. Since PDFium no longer parses the out-of-spec direct
stream, the crafted PDF renders as blank.
Bug: pdfium:165,pdfium:2119
Change-Id: Iad3c277535a9b49d5307ee1d66d54408830361d4
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/115611
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/parser/cpdf_dictionary.cpp b/core/fpdfapi/parser/cpdf_dictionary.cpp
index 81d20f6..1a5b873 100644
--- a/core/fpdfapi/parser/cpdf_dictionary.cpp
+++ b/core/fpdfapi/parser/cpdf_dictionary.cpp
@@ -276,7 +276,8 @@
m_Map.erase(key);
return nullptr;
}
- DCHECK(pObj->IsInline());
+ CHECK(pObj->IsInline());
+ CHECK(!pObj->IsStream());
CPDF_Object* pRet = pObj.Get();
m_Map[MaybeIntern(key)] = std::move(pObj);
return pRet;
diff --git a/core/fpdfapi/parser/cpdf_dictionary.h b/core/fpdfapi/parser/cpdf_dictionary.h
index dcb89f5..a18f2f3 100644
--- a/core/fpdfapi/parser/cpdf_dictionary.h
+++ b/core/fpdfapi/parser/cpdf_dictionary.h
@@ -106,7 +106,6 @@
// If `object` is null, then `key` is erased from the map. Otherwise, takes
// ownership of `object` and stores in in the map. Invalidates iterators for
// the element with the key `key`.
- // TODO(crbug.com/pdfium/2119): Ensure `object` is not a `CPDF_Stream`.
void SetFor(const ByteString& key, RetainPtr<CPDF_Object> object);
// A stream must be indirect and added as a `CPDF_Reference` instead.
void SetFor(const ByteString& key, RetainPtr<CPDF_Stream> stream) = delete;
diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.cpp b/core/fpdfapi/parser/cpdf_syntax_parser.cpp
index 3d0d357..defaa06 100644
--- a/core/fpdfapi/parser/cpdf_syntax_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_syntax_parser.cpp
@@ -587,7 +587,8 @@
}
// `key` has to be "/X" at the minimum.
- if (key.GetLength() > 1) {
+ // `pObj` cannot be a stream, per ISO 32000-1:2008 section 7.3.8.1.
+ if (key.GetLength() > 1 && !pObj->IsStream()) {
pDict->SetFor(key.Substr(1), std::move(pObj));
}
}
diff --git a/testing/resources/pixel/direct_content_stream.pdf b/testing/resources/pixel/direct_content_stream.pdf
new file mode 100644
index 0000000..8e2802a
--- /dev/null
+++ b/testing/resources/pixel/direct_content_stream.pdf
@@ -0,0 +1,51 @@
+%PDF-1.1
+1 0 obj
+<<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+
+2 0 obj
+<<
+ /Type /Pages
+ /Count 1
+ /Kids [ 3 0 R ]
+>>
+endobj
+
+3 0 obj
+<<
+ /Type /Page
+ /Contents << /Length 47>>
+ stream
+ BT
+ /F1 50 Tf
+ 40 500 Td
+ (direct content stream!!)Tj
+ ET
+ endstream
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Arial
+ >>
+ >>
+ >>
+>>
+endobj
+
+xref
+0 1
+0000000000 65535 f
+0000000010 00000 n
+
+trailer
+<<
+ /Root 1 0 R
+>>
+%%EOF
+
diff --git a/testing/resources/pixel/direct_content_stream_expected.pdf.0.png b/testing/resources/pixel/direct_content_stream_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/direct_content_stream_expected.pdf.0.png
Binary files differ