Fix FPDF_FILEACCESS lifetime issue with FPDF_LoadCustomDocument(). The FPDF_FILEACCESS that gets passed to FPDF_LoadCustomDocument() does not need to outlive the returned FPDF_DOCUMENT. See bug for discussion. This is a partial revert of commit b2ae83e8, along with a test case for this scenario and a clarification in the public API comments. BUG=pdfium:1261 Change-Id: Ib1d1b759fe3af98aa4ecc1d8387a259ee4ffe07f Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/51570 Commit-Queue: Tom Sepez <tsepez@chromium.org> Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/fpdfsdk/cpdfsdk_customaccess.cpp b/fpdfsdk/cpdfsdk_customaccess.cpp index fcaede5..acdf93a 100644 --- a/fpdfsdk/cpdfsdk_customaccess.cpp +++ b/fpdfsdk/cpdfsdk_customaccess.cpp
@@ -7,14 +7,12 @@ #include "fpdfsdk/cpdfsdk_customaccess.h" CPDFSDK_CustomAccess::CPDFSDK_CustomAccess(FPDF_FILEACCESS* pFileAccess) - : m_pFileAccess(pFileAccess) { - ASSERT(m_pFileAccess); -} + : m_FileAccess(*pFileAccess) {} CPDFSDK_CustomAccess::~CPDFSDK_CustomAccess() = default; FX_FILESIZE CPDFSDK_CustomAccess::GetSize() { - return m_pFileAccess->m_FileLen; + return m_FileAccess.m_FileLen; } bool CPDFSDK_CustomAccess::ReadBlockAtOffset(void* buffer, @@ -26,6 +24,6 @@ FX_SAFE_FILESIZE new_pos = pdfium::base::checked_cast<FX_FILESIZE>(size); new_pos += offset; return new_pos.IsValid() && new_pos.ValueOrDie() <= GetSize() && - m_pFileAccess->m_GetBlock(m_pFileAccess->m_Param, offset, - static_cast<uint8_t*>(buffer), size); + m_FileAccess.m_GetBlock(m_FileAccess.m_Param, offset, + static_cast<uint8_t*>(buffer), size); }
diff --git a/fpdfsdk/cpdfsdk_customaccess.h b/fpdfsdk/cpdfsdk_customaccess.h index 341d4ab..76940ce 100644 --- a/fpdfsdk/cpdfsdk_customaccess.h +++ b/fpdfsdk/cpdfsdk_customaccess.h
@@ -25,7 +25,7 @@ explicit CPDFSDK_CustomAccess(FPDF_FILEACCESS* pFileAccess); ~CPDFSDK_CustomAccess() override; - UnownedPtr<FPDF_FILEACCESS> const m_pFileAccess; + FPDF_FILEACCESS m_FileAccess; }; #endif // FPDFSDK_CPDFSDK_CUSTOMACCESS_H_
diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp index cade1ca..3a314ba 100644 --- a/fpdfsdk/fpdf_view_embeddertest.cpp +++ b/fpdfsdk/fpdf_view_embeddertest.cpp
@@ -276,6 +276,37 @@ EXPECT_FALSE(FPDF_LoadCustomDocument(nullptr, "")); } +// See https://crbug.com/pdfium/1261 +TEST_F(FPDFViewEmbedderTest, LoadCustomDocumentWithShortLivedFileAccess) { + std::string file_contents_string; // Must outlive |doc|. + ScopedFPDFDocument doc; + { + // Read a PDF, and copy it into |file_contents_string|. + std::string pdf_path; + size_t pdf_length; + ASSERT_TRUE(PathService::GetTestFilePath("rectangles.pdf", &pdf_path)); + auto file_contents = GetFileContents(pdf_path.c_str(), &pdf_length); + ASSERT_TRUE(file_contents); + for (size_t i = 0; i < pdf_length; ++i) + file_contents_string.push_back(file_contents.get()[i]); + + // Define a FPDF_FILEACCESS object that will go out of scope, while the + // loaded document in |doc| remains valid. + FPDF_FILEACCESS file_access = {}; + file_access.m_FileLen = pdf_length; + file_access.m_GetBlock = GetBlockFromString; + file_access.m_Param = &file_contents_string; + doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr)); + ASSERT_TRUE(doc); + } + + // Now try to access |doc| and make sure it still works. + ScopedFPDFPage page(FPDF_LoadPage(doc.get(), 0)); + ASSERT_TRUE(page); + EXPECT_DOUBLE_EQ(200, FPDF_GetPageWidth(page.get())); + EXPECT_DOUBLE_EQ(300, FPDF_GetPageHeight(page.get())); +} + TEST_F(FPDFViewEmbedderTest, Page) { EXPECT_TRUE(OpenDocument("about_blank.pdf")); FPDF_PAGE page = LoadPage(0);
diff --git a/public/fpdfview.h b/public/fpdfview.h index acd7400..8892da5 100644 --- a/public/fpdfview.h +++ b/public/fpdfview.h
@@ -453,10 +453,11 @@ // Return value: // A handle to the loaded document, or NULL on failure. // Comments: -// The application must keep the file resources valid until the PDF -// document is closed. +// The application must keep the file resources |pFileAccess| points to +// valid until the returned FPDF_DOCUMENT is closed. |pFileAccess| +// itself does not need to outlive the FPDF_DOCUMENT. // -// The loaded document can be closed with FPDF_CloseDocument. +// The loaded document can be closed with FPDF_CloseDocument(). // // See the comments for FPDF_LoadDocument() regarding the encoding for // |password|.