Fix FPDFPageObj_SetIsActive() crash
When FPDFPageObj_SetIsActive() deactivates the sole page object,
FPDFPage_GenerateContent() may delete the page object's content stream.
When the page object gets reactivated, its content stream will have
unexpectedly disappeared by the time the next FPDFPage_GenerateContent()
call gets made. This leads to a CHECK() failure crash in
CPDF_PageContentManager::UpdateStream().
Fix this by adjusting CPDF_PageContentGenerator to avoid the crash.
By adding CPDF_PageContentManager::HasStreamAtIndex(),
CPDF_PageContentGenerator can now call it to see if the content stream
is gone. If it gone, call AddStream() instead of UpdateStream().
With the crash fixes, resolve a TODO and add the rest of the test case
that exercises FPDFPageObj_SetIsActive().
Bug: 378120423
Change-Id: I7523a8dac6be881ee520747f81b0730f815d3144
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/125690
Reviewed-by: dan sinclair <dsinclair@google.com>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index 1639b5e..ac86ce2 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -282,7 +282,11 @@
continue;
}
- page_content_manager.UpdateStream(stream_index, buf);
+ if (page_content_manager.HasStreamAtIndex(stream_index)) {
+ page_content_manager.UpdateStream(stream_index, buf);
+ } else {
+ page_content_manager.AddStream(buf);
+ }
}
}
diff --git a/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp b/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp
index 676469e..c26decf 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp
@@ -70,6 +70,10 @@
ExecuteScheduledRemovals();
}
+bool CPDF_PageContentManager::HasStreamAtIndex(size_t stream_index) {
+ return !!GetStreamByIndex(stream_index);
+}
+
RetainPtr<CPDF_Stream> CPDF_PageContentManager::GetStreamByIndex(
size_t stream_index) {
RetainPtr<CPDF_Stream> contents_stream = GetContentsStream();
diff --git a/core/fpdfapi/edit/cpdf_pagecontentmanager.h b/core/fpdfapi/edit/cpdf_pagecontentmanager.h
index 5785fc3..fcb4162 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentmanager.h
+++ b/core/fpdfapi/edit/cpdf_pagecontentmanager.h
@@ -33,6 +33,8 @@
// is empty, then schedule the removal of the stream instead.
void UpdateStream(size_t stream_index, fxcrt::ostringstream* buf);
+ bool HasStreamAtIndex(size_t stream_index);
+
private:
// Gets the Content stream at a given index. If Contents is a single stream
// rather than an array, it is retrievable at index 0.
diff --git a/fpdfsdk/fpdf_editpage_embeddertest.cpp b/fpdfsdk/fpdf_editpage_embeddertest.cpp
index 25c9932..066c888 100644
--- a/fpdfsdk/fpdf_editpage_embeddertest.cpp
+++ b/fpdfsdk/fpdf_editpage_embeddertest.cpp
@@ -611,7 +611,25 @@
// Reactivate `page_obj` and render.
ASSERT_TRUE(FPDFPageObj_SetIsActive(page_obj, true));
- // TODO(crbug.com/378120423): The test should be able to call
- // FPDFPage_GenerateContent() without crashing. Then check the in-memory
- // representation and the saved outputs.
+ EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+ {
+ ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
+ CompareBitmap(bitmap.get(), page_width, page_height, kChecksum);
+ EXPECT_EQ(1, FPDFPage_CountObjects(page.get()));
+ }
+
+ {
+ // Save a copy, open the copy, and render it.
+ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+ ASSERT_TRUE(OpenSavedDocument());
+ FPDF_PAGE saved_page = LoadSavedPage(0);
+ ASSERT_TRUE(saved_page);
+
+ ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+ CompareBitmap(bitmap.get(), page_width, page_height, kChecksum);
+ EXPECT_EQ(1, FPDFPage_CountObjects(saved_page));
+
+ CloseSavedPage(saved_page);
+ CloseSavedDocument();
+ }
}