[xfa] Fix image data positions in the GIF decoder
Fixes GIF decoder bugs related to image data positions. The following
issues are addressed:
1. The image data position should not add in the input buffer position.
ProgressiveDecoder::GifCurrentPosition() already accounts for the
input buffer position, so the defective code is double-counting it.
2. A GIF-specific code path in ProgressiveDecoder::ReadMoreData() tries
to replace the existing buffer contents with the bytes at the image
data position, but should also update the associated buffer pointers.
The fixed code moves the GIF-specific logic out of the common code
path as well.
Bug: pdfium:904, pdfium:1337
Change-Id: I9fdf844e49844c4850f56b9feaba90e92b1c3189
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/92410
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: K. Moon <kmoon@chromium.org>
diff --git a/core/fxcodec/gif/cfx_gifcontext.cpp b/core/fxcodec/gif/cfx_gifcontext.cpp
index 01babf6..6ce9570 100644
--- a/core/fxcodec/gif/cfx_gifcontext.cpp
+++ b/core/fxcodec/gif/cfx_gifcontext.cpp
@@ -27,10 +27,6 @@
CFX_GifContext::~CFX_GifContext() = default;
-uint32_t CFX_GifContext::CurrentPosition() const {
- return delegate_->GifCurrentPosition();
-}
-
void CFX_GifContext::ReadScanline(int32_t row_num, uint8_t* row_buf) {
delegate_->GifReadScanline(row_num, row_buf);
}
@@ -496,8 +492,7 @@
}
gif_image->code_exp = code_size;
- gif_image->data_pos = CurrentPosition();
- gif_image->data_pos += input_buffer_->GetPosition();
+ gif_image->data_pos = delegate_->GifCurrentPosition();
gif_image->image_GCE = nullptr;
if (graphic_control_extension_.get()) {
if (graphic_control_extension_->gce_flags.transparency) {
diff --git a/core/fxcodec/gif/cfx_gifcontext.h b/core/fxcodec/gif/cfx_gifcontext.h
index 6894da6..2130c8c 100644
--- a/core/fxcodec/gif/cfx_gifcontext.h
+++ b/core/fxcodec/gif/cfx_gifcontext.h
@@ -25,7 +25,6 @@
explicit CFX_GifContext(GifDecoder::Delegate* delegate);
~CFX_GifContext() override;
- uint32_t CurrentPosition() const;
void ReadScanline(int32_t row_num, uint8_t* row_buf);
bool GetRecordPosition(uint32_t cur_pos,
int32_t left,
diff --git a/core/fxcodec/progressive_decoder.cpp b/core/fxcodec/progressive_decoder.cpp
index 9612025..ad52068 100644
--- a/core/fxcodec/progressive_decoder.cpp
+++ b/core/fxcodec/progressive_decoder.cpp
@@ -371,11 +371,12 @@
int32_t trans_index,
bool interlace) {
m_offSet = rcd_pos;
- m_InvalidateGifBuffer = true;
FXCODEC_STATUS error_status = FXCODEC_STATUS::kError;
+ m_pCodecMemory->Seek(m_pCodecMemory->GetSize());
if (!GifReadMoreData(&error_status))
return false;
+ m_pCodecMemory->Seek(0);
CFX_GifPalette* pPalette = nullptr;
if (pal_num != 0 && pal_ptr) {
@@ -719,7 +720,7 @@
bool ProgressiveDecoder::BmpReadMoreData(
ProgressiveDecoderIface::Context* pContext,
FXCODEC_STATUS* err_status) {
- return ReadMoreData(BmpProgressiveDecoder::GetInstance(), pContext, false,
+ return ReadMoreData(BmpProgressiveDecoder::GetInstance(), pContext,
err_status);
}
@@ -761,12 +762,8 @@
#ifdef PDF_ENABLE_XFA_GIF
bool ProgressiveDecoder::GifReadMoreData(FXCODEC_STATUS* err_status) {
- if (!ReadMoreData(GifProgressiveDecoder::GetInstance(), m_pGifContext.get(),
- m_InvalidateGifBuffer, err_status)) {
- return false;
- }
- m_InvalidateGifBuffer = false;
- return true;
+ return ReadMoreData(GifProgressiveDecoder::GetInstance(), m_pGifContext.get(),
+ err_status);
}
bool ProgressiveDecoder::GifDetectImageTypeInBuffer() {
@@ -929,7 +926,7 @@
bool ProgressiveDecoder::JpegReadMoreData(FXCODEC_STATUS* err_status) {
return ReadMoreData(JpegProgressiveDecoder::GetInstance(),
- m_pJpegContext.get(), false, err_status);
+ m_pJpegContext.get(), err_status);
}
bool ProgressiveDecoder::JpegDetectImageTypeInBuffer(
@@ -1449,7 +1446,6 @@
bool ProgressiveDecoder::ReadMoreData(
ProgressiveDecoderIface* pModule,
ProgressiveDecoderIface::Context* pContext,
- bool invalidate_buffer,
FXCODEC_STATUS* err_status) {
// Check for EOF.
if (m_offSet >= static_cast<uint32_t>(m_pFile->GetSize()))
@@ -1460,13 +1456,10 @@
pdfium::base::checked_cast<uint32_t>(m_pFile->GetSize() - m_offSet);
// Figure out if the codec stopped processing midway through the buffer.
- size_t dwUnconsumed = 0;
- if (!invalidate_buffer) {
- FX_SAFE_SIZE_T avail_input = pModule->GetAvailInput(pContext);
- if (!avail_input.IsValid())
- return false;
- dwUnconsumed = avail_input.ValueOrDie();
- }
+ size_t dwUnconsumed;
+ FX_SAFE_SIZE_T avail_input = pModule->GetAvailInput(pContext);
+ if (!avail_input.AssignIfValid(&dwUnconsumed))
+ return false;
if (dwUnconsumed == m_pCodecMemory->GetSize()) {
// Codec couldn't make any progress against the bytes in the buffer.
diff --git a/core/fxcodec/progressive_decoder.h b/core/fxcodec/progressive_decoder.h
index f7bee03..72a5041 100644
--- a/core/fxcodec/progressive_decoder.h
+++ b/core/fxcodec/progressive_decoder.h
@@ -197,7 +197,6 @@
CFX_DIBAttribute* pAttribute);
bool ReadMoreData(ProgressiveDecoderIface* pModule,
ProgressiveDecoderIface::Context* pContext,
- bool invalidate_buffer,
FXCODEC_STATUS* err_status);
int GetDownScale();
@@ -265,7 +264,6 @@
int32_t m_GifPltNumber = 0;
int m_GifTransIndex = -1;
FX_RECT m_GifFrameRect;
- bool m_InvalidateGifBuffer = true;
#endif // PDF_ENABLE_XFA_GIF
#ifdef PDF_ENABLE_XFA_BMP
bool m_BmpIsTopBottom = false;
diff --git a/core/fxcodec/progressive_decoder_unittest.cpp b/core/fxcodec/progressive_decoder_unittest.cpp
index 7de25bf..4c1b2c4 100644
--- a/core/fxcodec/progressive_decoder_unittest.cpp
+++ b/core/fxcodec/progressive_decoder_unittest.cpp
@@ -4,9 +4,19 @@
#include "core/fxcodec/progressive_decoder.h"
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <tuple>
+
#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/fx_codec_def.h"
#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/retain_ptr.h"
#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/base/span.h"
@@ -16,7 +26,85 @@
namespace fxcodec {
+namespace {
+
+using ::testing::ElementsAre;
+
+FXCODEC_STATUS DecodeToBitmap(ProgressiveDecoder& decoder,
+ const fxcrt::RetainPtr<CFX_DIBitmap>& bitmap) {
+ FXCODEC_STATUS status = decoder.StartDecode(bitmap, 0, 0, bitmap->GetWidth(),
+ bitmap->GetHeight());
+ while (status == FXCODEC_STATUS::kDecodeToBeContinued)
+ status = decoder.ContinueDecode();
+ return status;
+}
+
+} // namespace
+
#ifdef PDF_ENABLE_XFA_GIF
+TEST(ProgressiveDecoder, Gif87a) {
+ static constexpr uint8_t kInput[] = {
+ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x01,
+ 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x2c, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b};
+
+ auto decoder = std::make_unique<ProgressiveDecoder>();
+
+ auto source = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(kInput);
+ CFX_DIBAttribute attr;
+ FXCODEC_STATUS status =
+ decoder->LoadImageInfo(source, FXCODEC_IMAGE_GIF, &attr, true);
+ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+ ASSERT_EQ(1, decoder->GetWidth());
+ ASSERT_EQ(1, decoder->GetHeight());
+
+ auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+ bitmap->Create(decoder->GetWidth(), decoder->GetHeight(),
+ FXDIB_Format::kArgb);
+
+ size_t frames;
+ std::tie(status, frames) = decoder->GetFrames();
+ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+ ASSERT_EQ(1u, frames);
+
+ status = DecodeToBitmap(*decoder, bitmap);
+ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+ EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0xff));
+}
+
+TEST(ProgressiveDecoder, Gif89a) {
+ static constexpr uint8_t kInput[] = {
+ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80,
+ 0x01, 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x21, 0xf9, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b};
+
+ auto decoder = std::make_unique<ProgressiveDecoder>();
+
+ auto source = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(kInput);
+ CFX_DIBAttribute attr;
+ FXCODEC_STATUS status =
+ decoder->LoadImageInfo(source, FXCODEC_IMAGE_GIF, &attr, true);
+ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+ ASSERT_EQ(1, decoder->GetWidth());
+ ASSERT_EQ(1, decoder->GetHeight());
+
+ auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+ bitmap->Create(decoder->GetWidth(), decoder->GetHeight(),
+ FXDIB_Format::kArgb);
+
+ size_t frames;
+ std::tie(status, frames) = decoder->GetFrames();
+ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+ ASSERT_EQ(1u, frames);
+
+ status = DecodeToBitmap(*decoder, bitmap);
+ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+ EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0xff));
+}
+
TEST(ProgressiveDecoder, BUG_895009) {
static constexpr uint8_t kInput[] = {
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x62, 0x00, 0x21, 0x1b, 0x27, 0x01,
@@ -369,33 +457,28 @@
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
- {
- auto decoder = std::make_unique<ProgressiveDecoder>();
+ auto decoder = std::make_unique<ProgressiveDecoder>();
- auto source = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(kInput);
- CFX_DIBAttribute attr;
- FXCODEC_STATUS status =
- decoder->LoadImageInfo(source, FXCODEC_IMAGE_GIF, &attr, true);
- ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+ auto source = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(kInput);
+ CFX_DIBAttribute attr;
+ FXCODEC_STATUS status =
+ decoder->LoadImageInfo(source, FXCODEC_IMAGE_GIF, &attr, true);
+ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
- ASSERT_EQ(98, decoder->GetWidth());
- ASSERT_EQ(6945, decoder->GetHeight());
+ ASSERT_EQ(98, decoder->GetWidth());
+ ASSERT_EQ(6945, decoder->GetHeight());
- auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
- bitmap->Create(decoder->GetWidth(), decoder->GetHeight(),
- FXDIB_Format::kArgb);
+ auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+ bitmap->Create(decoder->GetWidth(), decoder->GetHeight(),
+ FXDIB_Format::kArgb);
- size_t frames;
- std::tie(status, frames) = decoder->GetFrames();
- ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
- ASSERT_EQ(1u, frames);
+ size_t frames;
+ std::tie(status, frames) = decoder->GetFrames();
+ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+ ASSERT_EQ(1u, frames);
- status = decoder->StartDecode(bitmap, 0, 0, bitmap->GetWidth(),
- bitmap->GetHeight());
- while (status == FXCODEC_STATUS::kDecodeToBeContinued)
- status = decoder->ContinueDecode();
- EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
- }
+ status = DecodeToBitmap(*decoder, bitmap);
+ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
}
#endif // PDF_ENABLE_XFA_GIF