Add more test cases for CPDF_ObjectStream.
Take the suggestions from https://pdfium-review.googlesource.com/85971
and test more corner cases.
Change-Id: I0843710d0d151609c6bdba711b0c58cb67186fdc
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/86011
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/parser/cpdf_object_stream_unittest.cpp b/core/fpdfapi/parser/cpdf_object_stream_unittest.cpp
index 4d55078..87b5cbf 100644
--- a/core/fpdfapi/parser/cpdf_object_stream_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_object_stream_unittest.cpp
@@ -12,6 +12,7 @@
#include "core/fpdfapi/parser/cpdf_string.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/cxx17_backports.h"
using testing::ElementsAre;
using testing::Pair;
@@ -175,6 +176,27 @@
EXPECT_FALSE(CPDF_ObjectStream::Create(stream.Get()));
}
+TEST(CPDF_ObjectStreamTest, StreamDictOffsetTooBig) {
+ auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+ dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+ dict->SetNewFor<CPDF_Number>("N", 3);
+ constexpr int kTooBigOffset = pdfium::size(kNormalStreamContent);
+ dict->SetNewFor<CPDF_Number>("First", kTooBigOffset);
+
+ auto stream = pdfium::MakeRetain<CPDF_Stream>(
+ ByteStringView(kNormalStreamContent).raw_span(), dict);
+ auto obj_stream = CPDF_ObjectStream::Create(stream.Get());
+ ASSERT_TRUE(obj_stream);
+
+ EXPECT_THAT(obj_stream->objects_offsets(),
+ ElementsAre(Pair(10, 0), Pair(11, 14), Pair(12, 21)));
+
+ CPDF_IndirectObjectHolder holder;
+ EXPECT_FALSE(obj_stream->ParseObject(&holder, 10));
+ EXPECT_FALSE(obj_stream->ParseObject(&holder, 11));
+ EXPECT_FALSE(obj_stream->ParseObject(&holder, 12));
+}
+
TEST(CPDF_ObjectStreamTest, StreamDictTooFewCount) {
auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
@@ -270,6 +292,46 @@
EXPECT_TRUE(obj11->IsDictionary());
}
+TEST(CPDF_ObjectStreamTest, StreamDictNegativeObjectOffset) {
+ auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+ dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+ dict->SetNewFor<CPDF_Number>("N", 3);
+ dict->SetNewFor<CPDF_Number>("First", 16);
+
+ const char kStreamContent[] = "10 0 11 -1 12 21<</Name /Foo>>[1 2 3]4";
+ auto stream = pdfium::MakeRetain<CPDF_Stream>(
+ ByteStringView(kStreamContent).raw_span(), dict);
+ auto obj_stream = CPDF_ObjectStream::Create(stream.Get());
+ ASSERT_TRUE(obj_stream);
+
+ // TODO(thestig): Should object 11 be rejected?
+ EXPECT_THAT(obj_stream->objects_offsets(),
+ ElementsAre(Pair(10, 0), Pair(11, 4294967295), Pair(12, 21)));
+
+ CPDF_IndirectObjectHolder holder;
+ EXPECT_FALSE(obj_stream->ParseObject(&holder, 11));
+}
+
+TEST(CPDF_ObjectStreamTest, StreamDictObjectOffsetTooBig) {
+ auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+ dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+ dict->SetNewFor<CPDF_Number>("N", 3);
+ dict->SetNewFor<CPDF_Number>("First", 17);
+
+ const char kStreamContent[] = "10 0 11 999 12 21<</Name /Foo>>[1 2 3]4";
+ auto stream = pdfium::MakeRetain<CPDF_Stream>(
+ ByteStringView(kStreamContent).raw_span(), dict);
+ auto obj_stream = CPDF_ObjectStream::Create(stream.Get());
+ ASSERT_TRUE(obj_stream);
+
+ // TODO(thestig): Should object 11 be rejected?
+ EXPECT_THAT(obj_stream->objects_offsets(),
+ ElementsAre(Pair(10, 0), Pair(11, 999), Pair(12, 21)));
+
+ CPDF_IndirectObjectHolder holder;
+ EXPECT_FALSE(obj_stream->ParseObject(&holder, 11));
+}
+
TEST(CPDF_ObjectStreamTest, StreamDictDuplicateObjNum) {
auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
@@ -299,3 +361,79 @@
EXPECT_EQ(0u, obj12->GetGenNum());
EXPECT_TRUE(obj12->IsNumber());
}
+
+TEST(CPDF_ObjectStreamTest, StreamDictUnorderedObjectNumbers) {
+ // ISO 32000-1:2008 spec. section 7.5.7, note 6 says there is no restriction
+ // on object number ordering.
+ auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+ dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+ dict->SetNewFor<CPDF_Number>("N", 3);
+ dict->SetNewFor<CPDF_Number>("First", 16);
+
+ const char kStreamContent[] = "11 0 12 14 10 21<</Name /Foo>>[1 2 3]4";
+ auto stream = pdfium::MakeRetain<CPDF_Stream>(
+ ByteStringView(kStreamContent).raw_span(), dict);
+ auto obj_stream = CPDF_ObjectStream::Create(stream.Get());
+ ASSERT_TRUE(obj_stream);
+
+ EXPECT_THAT(obj_stream->objects_offsets(),
+ ElementsAre(Pair(10, 21), Pair(11, 0), Pair(12, 14)));
+
+ CPDF_IndirectObjectHolder holder;
+ RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10);
+ ASSERT_TRUE(obj10);
+ EXPECT_EQ(10u, obj10->GetObjNum());
+ EXPECT_EQ(0u, obj10->GetGenNum());
+ EXPECT_TRUE(obj10->IsNumber());
+
+ RetainPtr<CPDF_Object> obj11 = obj_stream->ParseObject(&holder, 11);
+ ASSERT_TRUE(obj11);
+ EXPECT_EQ(11u, obj11->GetObjNum());
+ EXPECT_EQ(0u, obj11->GetGenNum());
+ EXPECT_TRUE(obj11->IsDictionary());
+
+ RetainPtr<CPDF_Object> obj12 = obj_stream->ParseObject(&holder, 12);
+ ASSERT_TRUE(obj12);
+ EXPECT_EQ(12u, obj12->GetObjNum());
+ EXPECT_EQ(0u, obj12->GetGenNum());
+ EXPECT_TRUE(obj12->IsArray());
+}
+
+TEST(CPDF_ObjectStreamTest, StreamDictUnorderedObjectOffsets) {
+ // ISO 32000-1:2008 spec. section 7.5.7, says offsets shall be in increasing
+ // order.
+ // TODO(thestig): Should CPDF_ObjectStream check for this and reject this
+ // object stream?
+ auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+ dict->SetNewFor<CPDF_Name>("Type", "ObjStm");
+ dict->SetNewFor<CPDF_Number>("N", 3);
+ dict->SetNewFor<CPDF_Number>("First", 16);
+
+ const char kStreamContent[] = "10 21 11 0 12 14<</Name /Foo>>[1 2 3]4";
+ auto stream = pdfium::MakeRetain<CPDF_Stream>(
+ ByteStringView(kStreamContent).raw_span(), dict);
+ auto obj_stream = CPDF_ObjectStream::Create(stream.Get());
+ ASSERT_TRUE(obj_stream);
+
+ EXPECT_THAT(obj_stream->objects_offsets(),
+ ElementsAre(Pair(10, 21), Pair(11, 0), Pair(12, 14)));
+
+ CPDF_IndirectObjectHolder holder;
+ RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10);
+ ASSERT_TRUE(obj10);
+ EXPECT_EQ(10u, obj10->GetObjNum());
+ EXPECT_EQ(0u, obj10->GetGenNum());
+ EXPECT_TRUE(obj10->IsNumber());
+
+ RetainPtr<CPDF_Object> obj11 = obj_stream->ParseObject(&holder, 11);
+ ASSERT_TRUE(obj11);
+ EXPECT_EQ(11u, obj11->GetObjNum());
+ EXPECT_EQ(0u, obj11->GetGenNum());
+ EXPECT_TRUE(obj11->IsDictionary());
+
+ RetainPtr<CPDF_Object> obj12 = obj_stream->ParseObject(&holder, 12);
+ ASSERT_TRUE(obj12);
+ EXPECT_EQ(12u, obj12->GetObjNum());
+ EXPECT_EQ(0u, obj12->GetGenNum());
+ EXPECT_TRUE(obj12->IsArray());
+}