Keep all trailers in CPDF_Parser in m_Trailers

This CL removes m_pTrailer in favor of having them all in a vector, and
having an index that points to the position of the previous m_pTrailer
in the vector.

Bug: pdfium:787
Change-Id: Ieebbf4849f7ea78f8f74d188e3adb3446a53482e
Reviewed-on: https://pdfium-review.googlesource.com/7040
Reviewed-by: dsinclair <dsinclair@chromium.org>
Commit-Queue: Nicolás Peña <npm@chromium.org>
diff --git a/core/fpdfapi/parser/cpdf_parser.cpp b/core/fpdfapi/parser/cpdf_parser.cpp
index b2bde8d..841bf27 100644
--- a/core/fpdfapi/parser/cpdf_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_parser.cpp
@@ -52,14 +52,14 @@
 }  // namespace
 
 CPDF_Parser::CPDF_Parser()
-    : m_bHasParsed(false),
+    : m_pSyntax(pdfium::MakeUnique<CPDF_SyntaxParser>()),
+      m_bHasParsed(false),
       m_bXRefStream(false),
       m_bVersionUpdated(false),
       m_FileVersion(0),
       m_pEncryptDict(nullptr),
-      m_dwXrefStartObjNum(0) {
-  m_pSyntax = pdfium::MakeUnique<CPDF_SyntaxParser>();
-}
+      m_TrailerPos(CPDF_Parser::kInvalidPos),
+      m_dwXrefStartObjNum(0) {}
 
 CPDF_Parser::~CPDF_Parser() {
   ReleaseEncryptHandler();
@@ -219,12 +219,13 @@
   }
   return SUCCESS;
 }
+
 CPDF_Parser::Error CPDF_Parser::SetEncryptHandler() {
   ReleaseEncryptHandler();
-  if (!m_pTrailer)
+  if (!GetTrailer())
     return FORMAT_ERROR;
 
-  CPDF_Object* pEncryptObj = m_pTrailer->GetObjectFor("Encrypt");
+  CPDF_Object* pEncryptObj = GetTrailer()->GetObjectFor("Encrypt");
   if (pEncryptObj) {
     if (CPDF_Dictionary* pEncryptDict = pEncryptObj->AsDictionary()) {
       SetEncryptDictionary(pEncryptDict);
@@ -304,11 +305,13 @@
   if (!LoadCrossRefV4(xrefpos, 0, true))
     return false;
 
-  m_pTrailer = LoadTrailerV4();
-  if (!m_pTrailer)
+  std::unique_ptr<CPDF_Dictionary> trailer = LoadTrailerV4();
+  if (!trailer)
     return false;
 
-  int32_t xrefsize = GetDirectInteger(m_pTrailer.get(), "Size");
+  m_Trailers.push_back(std::move(trailer));
+  m_TrailerPos = m_Trailers.size() - 1;
+  int32_t xrefsize = GetDirectInteger(GetTrailer(), "Size");
   if (xrefsize > 0 && xrefsize <= kMaxXRefSize)
     ShrinkObjectMap(xrefsize);
 
@@ -317,12 +320,12 @@
   std::set<FX_FILESIZE> seen_xrefpos;
 
   CrossRefList.push_back(xrefpos);
-  XRefStreamList.push_back(GetDirectInteger(m_pTrailer.get(), "XRefStm"));
+  XRefStreamList.push_back(GetDirectInteger(GetTrailer(), "XRefStm"));
   seen_xrefpos.insert(xrefpos);
 
-  // When |m_pTrailer| doesn't have Prev entry or Prev entry value is not
+  // When the trailer doesn't have Prev entry or Prev entry value is not
   // numerical, GetDirectInteger() returns 0. Loading will end.
-  xrefpos = GetDirectInteger(m_pTrailer.get(), "Prev");
+  xrefpos = GetDirectInteger(GetTrailer(), "Prev");
   while (xrefpos) {
     // Check for circular references.
     if (pdfium::ContainsKey(seen_xrefpos, xrefpos))
@@ -360,14 +363,13 @@
   if (!LoadLinearizedCrossRefV4(xrefpos, dwObjCount))
     return false;
 
-  if (m_pTrailer)
-    m_Trailers.push_back(std::move(m_pTrailer));
-
-  m_pTrailer = LoadTrailerV4();
-  if (!m_pTrailer)
+  std::unique_ptr<CPDF_Dictionary> trailer = LoadTrailerV4();
+  if (!trailer)
     return false;
 
-  int32_t xrefsize = GetDirectInteger(m_pTrailer.get(), "Size");
+  m_Trailers.push_back(std::move(trailer));
+  m_TrailerPos = m_Trailers.size() - 1;
+  int32_t xrefsize = GetDirectInteger(GetTrailer(), "Size");
   if (xrefsize == 0)
     return false;
 
@@ -376,10 +378,10 @@
   std::set<FX_FILESIZE> seen_xrefpos;
 
   CrossRefList.push_back(xrefpos);
-  XRefStreamList.push_back(GetDirectInteger(m_pTrailer.get(), "XRefStm"));
+  XRefStreamList.push_back(GetDirectInteger(GetTrailer(), "XRefStm"));
   seen_xrefpos.insert(xrefpos);
 
-  xrefpos = GetDirectInteger(m_pTrailer.get(), "Prev");
+  xrefpos = GetDirectInteger(GetTrailer(), "Prev");
   while (xrefpos) {
     // Check for circular references.
     if (pdfium::ContainsKey(seen_xrefpos, xrefpos))
@@ -568,7 +570,8 @@
 bool CPDF_Parser::RebuildCrossRef() {
   m_ObjectInfo.clear();
   m_SortedOffset.clear();
-  m_pTrailer.reset();
+  m_Trailers.clear();
+  m_TrailerPos = CPDF_Parser::kInvalidPos;
 
   ParserState state = ParserState::kDefault;
   int32_t inside_index = 0;
@@ -729,7 +732,8 @@
                       CPDF_Object* pRoot = pDict->GetObjectFor("Root");
                       if (pRoot && pRoot->GetDict() &&
                           pRoot->GetDict()->GetObjectFor("Pages")) {
-                        m_pTrailer = ToDictionary(pDict->Clone());
+                        m_Trailers.push_back(ToDictionary(pDict->Clone()));
+                        m_TrailerPos = m_Trailers.size() - 1;
                       }
                     }
                   }
@@ -785,7 +789,7 @@
                   CPDF_Stream* pStream = pObj->AsStream();
                   if (CPDF_Dictionary* pTrailer =
                           pStream ? pStream->GetDict() : pObj->AsDictionary()) {
-                    if (m_pTrailer) {
+                    if (GetTrailer()) {
                       CPDF_Object* pRoot = pTrailer->GetObjectFor("Root");
                       CPDF_Reference* pRef = ToReference(pRoot);
                       if (!pRoot ||
@@ -799,19 +803,19 @@
                           uint32_t dwObjNum =
                               pElement ? pElement->GetObjNum() : 0;
                           if (dwObjNum) {
-                            m_pTrailer->SetNewFor<CPDF_Reference>(
+                            GetTrailer()->SetNewFor<CPDF_Reference>(
                                 key, m_pDocument.Get(), dwObjNum);
                           } else {
-                            m_pTrailer->SetFor(key, pElement->Clone());
+                            GetTrailer()->SetFor(key, pElement->Clone());
                           }
                         }
                       }
                     } else {
-                      if (pObj->IsStream()) {
-                        m_pTrailer = ToDictionary(pTrailer->Clone());
-                      } else {
-                        m_pTrailer = ToDictionary(std::move(pObj));
-                      }
+                      if (pObj->IsStream())
+                        m_Trailers.push_back(ToDictionary(pTrailer->Clone()));
+                      else
+                        m_Trailers.push_back(ToDictionary(std::move(pObj)));
+                      m_TrailerPos = m_Trailers.size() - 1;
 
                       FX_FILESIZE dwSavePos = m_pSyntax->GetPos();
                       CFX_ByteString strWord = m_pSyntax->GetKeyword();
@@ -915,7 +919,7 @@
     last_trailer = m_pSyntax->m_FileLen;
 
   m_SortedOffset.insert(last_trailer - m_pSyntax->m_HeaderOffset);
-  return m_pTrailer && !m_ObjectInfo.empty();
+  return GetTrailer() && !m_ObjectInfo.empty();
 }
 
 bool CPDF_Parser::LoadCrossRefV5(FX_FILESIZE* pos, bool bMainXRef) {
@@ -951,7 +955,8 @@
 
   std::unique_ptr<CPDF_Dictionary> pNewTrailer = ToDictionary(pDict->Clone());
   if (bMainXRef) {
-    m_pTrailer = std::move(pNewTrailer);
+    m_Trailers.push_back(std::move(pNewTrailer));
+    m_TrailerPos = m_Trailers.size() - 1;
     ShrinkObjectMap(size);
     for (auto& it : m_ObjectInfo)
       it.second.type = 0;
@@ -1060,10 +1065,10 @@
 }
 
 CPDF_Array* CPDF_Parser::GetIDArray() {
-  if (!m_pTrailer)
+  if (!GetTrailer())
     return nullptr;
 
-  CPDF_Object* pID = m_pTrailer->GetObjectFor("ID");
+  CPDF_Object* pID = GetTrailer()->GetObjectFor("ID");
   if (!pID)
     return nullptr;
 
@@ -1074,19 +1079,19 @@
   std::unique_ptr<CPDF_Object> pNewObj =
       ParseIndirectObject(nullptr, pRef->GetRefObjNum());
   pID = pNewObj.get();
-  m_pTrailer->SetFor("ID", std::move(pNewObj));
+  GetTrailer()->SetFor("ID", std::move(pNewObj));
   return ToArray(pID);
 }
 
 uint32_t CPDF_Parser::GetRootObjNum() {
   CPDF_Reference* pRef =
-      ToReference(m_pTrailer ? m_pTrailer->GetObjectFor("Root") : nullptr);
+      ToReference(GetTrailer() ? GetTrailer()->GetObjectFor("Root") : nullptr);
   return pRef ? pRef->GetRefObjNum() : 0;
 }
 
 uint32_t CPDF_Parser::GetInfoObjNum() {
   CPDF_Reference* pRef =
-      ToReference(m_pTrailer ? m_pTrailer->GetObjectFor("Info") : nullptr);
+      ToReference(GetTrailer() ? GetTrailer()->GetObjectFor("Info") : nullptr);
   if (pRef)
     return pRef->GetRefObjNum();
 
@@ -1488,11 +1493,13 @@
   }
 
   if (bLoadV4) {
-    m_pTrailer = LoadTrailerV4();
-    if (!m_pTrailer)
+    std::unique_ptr<CPDF_Dictionary> trailer = LoadTrailerV4();
+    if (!trailer)
       return SUCCESS;
 
-    int32_t xrefsize = GetDirectInteger(m_pTrailer.get(), "Size");
+    m_Trailers.push_back(std::move(trailer));
+    m_TrailerPos = m_Trailers.size() - 1;
+    int32_t xrefsize = GetDirectInteger(GetTrailer(), "Size");
     if (xrefsize > 0)
       ShrinkObjectMap(xrefsize);
   }
@@ -1559,8 +1566,6 @@
 CPDF_Parser::Error CPDF_Parser::LoadLinearizedMainXRefTable() {
   uint32_t dwSaveMetadataObjnum = m_pSyntax->m_MetadataObjnum;
   m_pSyntax->m_MetadataObjnum = 0;
-  if (m_pTrailer)
-    m_Trailers.push_back(std::move(m_pTrailer));
   m_pSyntax->SetPos(m_LastXRefOffset - m_pSyntax->m_HeaderOffset);
 
   uint8_t ch = 0;
diff --git a/core/fpdfapi/parser/cpdf_parser.h b/core/fpdfapi/parser/cpdf_parser.h
index 8f55ddb..efe1055 100644
--- a/core/fpdfapi/parser/cpdf_parser.h
+++ b/core/fpdfapi/parser/cpdf_parser.h
@@ -7,6 +7,7 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_PARSER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_PARSER_H_
 
+#include <limits>
 #include <map>
 #include <memory>
 #include <set>
@@ -41,6 +42,8 @@
   // are higher, but this may be large enough in practice.
   static const uint32_t kMaxObjectNumber = 1048576;
 
+  static const size_t kInvalidPos = std::numeric_limits<size_t>::max();
+
   CPDF_Parser();
   ~CPDF_Parser();
 
@@ -51,7 +54,10 @@
 
   void SetPassword(const char* password) { m_Password = password; }
   CFX_ByteString GetPassword() { return m_Password; }
-  CPDF_Dictionary* GetTrailer() const { return m_pTrailer.get(); }
+  CPDF_Dictionary* GetTrailer() const {
+    return m_TrailerPos == kInvalidPos ? nullptr
+                                       : m_Trailers[m_TrailerPos].get();
+  }
   FX_FILESIZE GetLastXRefOffset() const { return m_LastXRefOffset; }
 
   uint32_t GetPermissions() const;
@@ -161,8 +167,8 @@
   std::unique_ptr<CPDF_SecurityHandler> m_pSecurityHandler;
   CFX_ByteString m_Password;
   std::set<FX_FILESIZE> m_SortedOffset;
-  std::unique_ptr<CPDF_Dictionary> m_pTrailer;
   std::vector<std::unique_ptr<CPDF_Dictionary>> m_Trailers;
+  size_t m_TrailerPos;
   std::unique_ptr<CPDF_LinearizedHeader> m_pLinearized;
   uint32_t m_dwXrefStartObjNum;