Check inputs for CPDFSDK_CustomAccess::ReadBlockAtOffset().
To be consistent with other IFX_SeekableReadStream::ReadBlockAtOffset()
implementation. Do the same checks in similar code that reads from
FPDF_FILEACCESS.
With these checks, CPDF_Parser::ParseAndAppendCrossRefSubsectionData()
will consistently fail for xref tables with 0 entries. Check for this
case and make the parser succeed. This deals with the xref table quirk
more gracefully and avoids rebuilds. Update test expectations as a
result of this new behavior.
BUG=pdfium:1197
Change-Id: Id9900161c3453c37b51d501cb8746618388214a9
Reviewed-on: https://pdfium-review.googlesource.com/c/47551
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/parser/cpdf_parser.cpp b/core/fpdfapi/parser/cpdf_parser.cpp
index ed4eeb1..64fdd6c 100644
--- a/core/fpdfapi/parser/cpdf_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_parser.cpp
@@ -418,6 +418,9 @@
uint32_t start_objnum,
uint32_t count,
std::vector<CrossRefObjData>* out_objects) {
+ if (!count)
+ return true;
+
// Each entry shall be exactly 20 byte.
// A sample entry looks like:
// "0000000000 00007 f\r\n"
diff --git a/fpdfsdk/cpdfsdk_customaccess.cpp b/fpdfsdk/cpdfsdk_customaccess.cpp
index b679858..fcaede5 100644
--- a/fpdfsdk/cpdfsdk_customaccess.cpp
+++ b/fpdfsdk/cpdfsdk_customaccess.cpp
@@ -20,16 +20,12 @@
bool CPDFSDK_CustomAccess::ReadBlockAtOffset(void* buffer,
FX_FILESIZE offset,
size_t size) {
- if (offset < 0)
+ if (!buffer || offset < 0 || !size)
return false;
FX_SAFE_FILESIZE new_pos = pdfium::base::checked_cast<FX_FILESIZE>(size);
new_pos += offset;
- if (!new_pos.IsValid())
- return false;
- if (new_pos.ValueOrDie() > static_cast<FX_FILESIZE>(m_pFileAccess->m_FileLen))
- return false;
-
- return !!m_pFileAccess->m_GetBlock(m_pFileAccess->m_Param, offset,
- static_cast<uint8_t*>(buffer), size);
+ return new_pos.IsValid() && new_pos.ValueOrDie() <= GetSize() &&
+ m_pFileAccess->m_GetBlock(m_pFileAccess->m_Param, offset,
+ static_cast<uint8_t*>(buffer), size);
}
diff --git a/fpdfsdk/fpdf_dataavail.cpp b/fpdfsdk/fpdf_dataavail.cpp
index 2b567dd..990a968 100644
--- a/fpdfsdk/fpdf_dataavail.cpp
+++ b/fpdfsdk/fpdf_dataavail.cpp
@@ -76,8 +76,14 @@
bool ReadBlockAtOffset(void* buffer,
FX_FILESIZE offset,
size_t size) override {
- return !!m_pFileAccess->m_GetBlock(m_pFileAccess->m_Param, offset,
- static_cast<uint8_t*>(buffer), size);
+ if (!buffer || offset < 0 || !size)
+ return false;
+
+ 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);
}
private:
diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp
index 9de00c5..5b6be41 100644
--- a/fpdfsdk/fpdf_view_embeddertest.cpp
+++ b/fpdfsdk/fpdf_view_embeddertest.cpp
@@ -686,18 +686,16 @@
}
// Related to https://crbug.com/pdfium/1197
-// TODO(thestig): FPDF_DocumentHasValidCrossReferenceTable() should return true
-// in this test.
TEST_F(FPDFViewEmbedderTest, LoadDocumentWithEmptyXRefConsistently) {
ASSERT_TRUE(OpenDocument("empty_xref.pdf"));
- EXPECT_FALSE(FPDF_DocumentHasValidCrossReferenceTable(document()));
+ EXPECT_TRUE(FPDF_DocumentHasValidCrossReferenceTable(document()));
std::string file_path;
ASSERT_TRUE(PathService::GetTestFilePath("empty_xref.pdf", &file_path));
{
ScopedFPDFDocument doc(FPDF_LoadDocument(file_path.c_str(), ""));
ASSERT_TRUE(doc);
- EXPECT_FALSE(FPDF_DocumentHasValidCrossReferenceTable(doc.get()));
+ EXPECT_TRUE(FPDF_DocumentHasValidCrossReferenceTable(doc.get()));
}
{
size_t file_length = 0;
@@ -707,6 +705,6 @@
ScopedFPDFDocument doc(
FPDF_LoadMemDocument(file_contents.get(), file_length, ""));
ASSERT_TRUE(doc);
- EXPECT_FALSE(FPDF_DocumentHasValidCrossReferenceTable(doc.get()));
+ EXPECT_TRUE(FPDF_DocumentHasValidCrossReferenceTable(doc.get()));
}
}
diff --git a/public/fpdfview.h b/public/fpdfview.h
index 05e94f9..0ba50e2 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -333,6 +333,7 @@
// A function pointer for getting a block of data from a specific position.
// Position is specified by byte offset from the beginning of the file.
+ // The pointer to the buffer is never NULL and the size is never 0.
// The position and size will never go out of range of the file length.
// It may be possible for FPDFSDK to call this function multiple times for
// the same position.
diff --git a/testing/embedder_test.cpp b/testing/embedder_test.cpp
index a94b7ef..e038aeb 100644
--- a/testing/embedder_test.cpp
+++ b/testing/embedder_test.cpp
@@ -568,12 +568,15 @@
unsigned char* buf,
unsigned long size) {
std::string* new_file = static_cast<std::string*>(param);
- if (!new_file || pos + size < pos)
+ if (!new_file || pos + size < pos) {
+ NOTREACHED();
return 0;
+ }
- unsigned long file_size = new_file->size();
- if (pos + size > file_size)
+ if (pos + size > new_file->size()) {
+ NOTREACHED();
return 0;
+ }
memcpy(buf, new_file->data() + pos, size);
return 1;
diff --git a/testing/fake_file_access.cpp b/testing/fake_file_access.cpp
index 9e0e6db..290a10d 100644
--- a/testing/fake_file_access.cpp
+++ b/testing/fake_file_access.cpp
@@ -99,8 +99,12 @@
int FakeFileAccess::GetBlock(unsigned long position,
unsigned char* pBuf,
unsigned long size) {
+ if (!pBuf || !size)
+ return false;
+
if (!IsDataAvail(static_cast<size_t>(position), static_cast<size_t>(size)))
return false;
+
return file_access_->m_GetBlock(file_access_->m_Param, position, pBuf, size);
}
diff --git a/testing/test_support.cpp b/testing/test_support.cpp
index 15309ad..03d243d 100644
--- a/testing/test_support.cpp
+++ b/testing/test_support.cpp
@@ -217,8 +217,10 @@
unsigned char* pBuf,
unsigned long size) {
TestLoader* pLoader = static_cast<TestLoader*>(param);
- if (pos + size < pos || pos + size > pLoader->m_Len)
+ if (pos + size < pos || pos + size > pLoader->m_Len) {
+ NOTREACHED();
return 0;
+ }
memcpy(pBuf, pLoader->m_pBuf + pos, size);
return 1;