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