diff options
-rw-r--r-- | BUILD.gn | 1 | ||||
-rw-r--r-- | core/fxcodec/gif/cfx_gif.cpp | 3 | ||||
-rw-r--r-- | core/fxcodec/gif/cfx_gif.h | 9 | ||||
-rw-r--r-- | core/fxcodec/gif/cfx_gifcontext.cpp | 98 | ||||
-rw-r--r-- | core/fxcodec/gif/cfx_gifcontext.h | 6 | ||||
-rw-r--r-- | core/fxcodec/gif/cfx_gifcontext_unittest.cpp | 336 |
6 files changed, 409 insertions, 44 deletions
@@ -1950,6 +1950,7 @@ test("pdfium_unittests") { include_dirs = [] if (pdf_enable_xfa) { sources += [ + "core/fxcodec/gif/cfx_gifcontext_unittest.cpp", "core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp", "core/fxcrt/css/cfx_cssdeclaration_unittest.cpp", "core/fxcrt/css/cfx_cssstylesheet_unittest.cpp", diff --git a/core/fxcodec/gif/cfx_gif.cpp b/core/fxcodec/gif/cfx_gif.cpp index c3c5c85323..985a687906 100644 --- a/core/fxcodec/gif/cfx_gif.cpp +++ b/core/fxcodec/gif/cfx_gif.cpp @@ -6,6 +6,9 @@ #include "core/fxcodec/gif/cfx_gif.h" +const char* kGifSignature87 = "GIF87a"; +const char* kGifSignature89 = "GIF89a"; + static_assert(sizeof(CFX_CFX_GifImageInfo) == 9, "CFX_CFX_GifImageInfo should have a size of 9"); static_assert(sizeof(CFX_GifPalette) == 3, diff --git a/core/fxcodec/gif/cfx_gif.h b/core/fxcodec/gif/cfx_gif.h index e9eb5c4054..ab8278ecd2 100644 --- a/core/fxcodec/gif/cfx_gif.h +++ b/core/fxcodec/gif/cfx_gif.h @@ -12,7 +12,9 @@ class CFX_GifContext; -#define GIF_SIGNATURE "GIF" +extern const char* kGifSignature87; +extern const char* kGifSignature89; + #define GIF_SIG_EXTENSION 0x21 #define GIF_SIG_IMAGE 0x2C #define GIF_SIG_TRAILER 0x3B @@ -49,10 +51,7 @@ typedef struct { uint8_t local_pal : 1; } CFX_GifLocalFlags; -typedef struct { - char signature[3]; - char version[3]; -} CFX_GifHeader; +typedef struct { char signature[6]; } CFX_GifHeader; typedef struct { uint16_t width; diff --git a/core/fxcodec/gif/cfx_gifcontext.cpp b/core/fxcodec/gif/cfx_gifcontext.cpp index 09e8753ca0..28bcad9ceb 100644 --- a/core/fxcodec/gif/cfx_gifcontext.cpp +++ b/core/fxcodec/gif/cfx_gifcontext.cpp @@ -67,43 +67,10 @@ bool CFX_GifContext::GetRecordPosition(uint32_t cur_pos, } CFX_GifDecodeStatus CFX_GifContext::ReadHeader() { - uint32_t skip_size_org = skip_size_; - CFX_GifHeader* gif_header = nullptr; - if (!ReadData(reinterpret_cast<uint8_t**>(&gif_header), 6)) - return CFX_GifDecodeStatus::Unfinished; - - if (strncmp(gif_header->signature, GIF_SIGNATURE, 3) != 0 || - gif_header->version[0] != '8' || gif_header->version[2] != 'a') - return CFX_GifDecodeStatus::Error; - - CFX_GifLocalScreenDescriptor* gif_lsd = nullptr; - if (!ReadData(reinterpret_cast<uint8_t**>(&gif_lsd), 7)) { - skip_size_ = skip_size_org; - return CFX_GifDecodeStatus::Unfinished; - } - - if (gif_lsd->global_flags.global_pal) { - global_pal_exp_ = gif_lsd->global_flags.pal_bits; - uint32_t global_pal_size = unsigned(2 << global_pal_exp_) * 3u; - uint8_t* global_pal = nullptr; - if (!ReadData(&global_pal, global_pal_size)) { - skip_size_ = skip_size_org; - return CFX_GifDecodeStatus::Unfinished; - } - - global_sort_flag_ = gif_lsd->global_flags.sort_flag; - global_color_resolution_ = gif_lsd->global_flags.color_resolution; - global_palette_.resize(global_pal_size / 3); - memcpy(global_palette_.data(), global_pal, global_pal_size); - } - - width_ = static_cast<int>( - FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&gif_lsd->width))); - height_ = static_cast<int>( - FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&gif_lsd->height))); - bc_index_ = gif_lsd->bc_index; - pixel_aspect_ = gif_lsd->pixel_aspect; - return CFX_GifDecodeStatus::Success; + CFX_GifDecodeStatus status = ReadGifSignature(); + if (status != CFX_GifDecodeStatus::Success) + return status; + return ReadLogicalScreenDescriptor(); } CFX_GifDecodeStatus CFX_GifContext::GetFrame() { @@ -383,7 +350,15 @@ int32_t CFX_GifContext::GetFrameNum() const { } uint8_t* CFX_GifContext::ReadData(uint8_t** des_buf_pp, uint32_t data_size) { - if (avail_in_ < skip_size_ + data_size) + if (!next_in_) + return nullptr; + if (avail_in_ <= skip_size_) + return nullptr; + if (!des_buf_pp) + return nullptr; + if (data_size == 0) + return nullptr; + if (avail_in_ - skip_size_ < data_size) return nullptr; *des_buf_pp = next_in_ + skip_size_; @@ -391,6 +366,53 @@ uint8_t* CFX_GifContext::ReadData(uint8_t** des_buf_pp, uint32_t data_size) { return *des_buf_pp; } +CFX_GifDecodeStatus CFX_GifContext::ReadGifSignature() { + CFX_GifHeader* header = nullptr; + uint32_t skip_size_org = skip_size_; + if (!ReadData(reinterpret_cast<uint8_t**>(&header), 6)) { + skip_size_ = skip_size_org; + return CFX_GifDecodeStatus::Unfinished; + } + + if (strncmp(header->signature, kGifSignature87, 6) != 0 && + strncmp(header->signature, kGifSignature89, 6) != 0) + return CFX_GifDecodeStatus::Error; + + return CFX_GifDecodeStatus::Success; +} + +CFX_GifDecodeStatus CFX_GifContext::ReadLogicalScreenDescriptor() { + CFX_GifLocalScreenDescriptor* lsd = nullptr; + uint32_t skip_size_org = skip_size_; + if (!ReadData(reinterpret_cast<uint8_t**>(&lsd), 7)) { + skip_size_ = skip_size_org; + return CFX_GifDecodeStatus::Unfinished; + } + + if (lsd->global_flags.global_pal) { + uint32_t global_pal_size = unsigned(2 << lsd->global_flags.pal_bits) * 3u; + uint8_t* global_pal = nullptr; + if (!ReadData(&global_pal, global_pal_size)) { + skip_size_ = skip_size_org; + return CFX_GifDecodeStatus::Unfinished; + } + + global_pal_exp_ = lsd->global_flags.pal_bits; + global_sort_flag_ = lsd->global_flags.sort_flag; + global_color_resolution_ = lsd->global_flags.color_resolution; + global_palette_.resize(global_pal_size / 3); + memcpy(global_palette_.data(), global_pal, global_pal_size); + } + + width_ = static_cast<int>( + FXWORD_GET_MSBFIRST(reinterpret_cast<uint8_t*>(&lsd->width))); + height_ = static_cast<int>( + FXWORD_GET_MSBFIRST(reinterpret_cast<uint8_t*>(&lsd->height))); + bc_index_ = lsd->bc_index; + pixel_aspect_ = lsd->pixel_aspect; + return CFX_GifDecodeStatus::Success; +} + void CFX_GifContext::SaveDecodingStatus(int32_t status) { decode_status_ = status; next_in_ += skip_size_; diff --git a/core/fxcodec/gif/cfx_gifcontext.h b/core/fxcodec/gif/cfx_gifcontext.h index a1cffb2746..e0c5900f93 100644 --- a/core/fxcodec/gif/cfx_gifcontext.h +++ b/core/fxcodec/gif/cfx_gifcontext.h @@ -65,8 +65,12 @@ class CFX_GifContext : public CCodec_GifModule::Context { uint8_t global_color_resolution_; uint8_t img_pass_num_; - private: + protected: uint8_t* ReadData(uint8_t** des_buf_pp, uint32_t data_size); + CFX_GifDecodeStatus ReadGifSignature(); + CFX_GifDecodeStatus ReadLogicalScreenDescriptor(); + + private: void SaveDecodingStatus(int32_t status); CFX_GifDecodeStatus DecodeExtension(); CFX_GifDecodeStatus DecodeImageInfo(); diff --git a/core/fxcodec/gif/cfx_gifcontext_unittest.cpp b/core/fxcodec/gif/cfx_gifcontext_unittest.cpp new file mode 100644 index 0000000000..98b8ec5cd0 --- /dev/null +++ b/core/fxcodec/gif/cfx_gifcontext_unittest.cpp @@ -0,0 +1,336 @@ +// Copyright 2017 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/fxcodec/gif/cfx_gifcontext.h" + +#include "core/fxcrt/unowned_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +class CFX_GifContextForTest : public CFX_GifContext { + public: + CFX_GifContextForTest(CCodec_GifModule* gif_module, + CCodec_GifModule::Delegate* delegate) + : CFX_GifContext(gif_module, delegate) {} + ~CFX_GifContextForTest() override {} + + using CFX_GifContext::ReadData; + using CFX_GifContext::ReadGifSignature; + using CFX_GifContext::ReadLogicalScreenDescriptor; +}; + +TEST(CFX_GifContext, SetInputBuffer) { + CFX_GifContextForTest context(nullptr, nullptr); + + context.SetInputBuffer(nullptr, 0); + EXPECT_EQ(nullptr, context.next_in_); + EXPECT_EQ(0u, context.avail_in_); + EXPECT_EQ(0u, context.skip_size_); + + context.SetInputBuffer(nullptr, 100); + EXPECT_EQ(nullptr, context.next_in_); + EXPECT_EQ(100u, context.avail_in_); + EXPECT_EQ(0u, context.skip_size_); + + uint8_t buffer[] = {0x00, 0x01, 0x02}; + context.SetInputBuffer(buffer, 0); + EXPECT_EQ(buffer, context.next_in_); + EXPECT_EQ(0u, context.avail_in_); + EXPECT_EQ(0u, context.skip_size_); + + context.SetInputBuffer(buffer, 3); + EXPECT_EQ(buffer, context.next_in_); + EXPECT_EQ(3u, context.avail_in_); + EXPECT_EQ(0u, context.skip_size_); + + context.SetInputBuffer(buffer, 100); + EXPECT_EQ(buffer, context.next_in_); + EXPECT_EQ(100u, context.avail_in_); + EXPECT_EQ(0u, context.skip_size_); +} + +TEST(CFX_GifContext, ReadData) { + CFX_GifContextForTest context(nullptr, nullptr); + + context.SetInputBuffer(nullptr, 0); + EXPECT_EQ(nullptr, context.ReadData(nullptr, 0)); + EXPECT_EQ(nullptr, context.ReadData(nullptr, 10)); + + uint8_t* dest_buffer = nullptr; + EXPECT_EQ(nullptr, + context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 0)); + EXPECT_EQ(nullptr, + context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 10)); + + uint8_t src_buffer[] = {0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09}; + context.SetInputBuffer(src_buffer, 0); + EXPECT_EQ(nullptr, + context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 10)); + EXPECT_EQ(reinterpret_cast<uint8_t*>(src_buffer), context.next_in_); + EXPECT_EQ(0u, context.avail_in_); + EXPECT_EQ(0u, context.skip_size_); + + dest_buffer = nullptr; + context.SetInputBuffer(src_buffer, 10); + EXPECT_EQ(src_buffer, + context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 10)); + EXPECT_EQ(reinterpret_cast<uint8_t*>(src_buffer), dest_buffer); + EXPECT_EQ(src_buffer, context.next_in_); + EXPECT_EQ(10u, context.avail_in_); + EXPECT_EQ(10u, context.skip_size_); + + dest_buffer = nullptr; + context.SetInputBuffer(src_buffer, 10); + EXPECT_EQ(src_buffer, + context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 5)); + EXPECT_EQ(reinterpret_cast<uint8_t*>(src_buffer), dest_buffer); + EXPECT_EQ(src_buffer, context.next_in_); + EXPECT_EQ(10u, context.avail_in_); + EXPECT_EQ(5u, context.skip_size_); + + dest_buffer = nullptr; + EXPECT_EQ(src_buffer + 5, + context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 5)); + EXPECT_EQ(reinterpret_cast<uint8_t*>(src_buffer + 5), dest_buffer); + EXPECT_EQ(src_buffer, context.next_in_); + EXPECT_EQ(10u, context.avail_in_); + EXPECT_EQ(10u, context.skip_size_); + + dest_buffer = nullptr; + EXPECT_EQ(nullptr, + context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 5)); + EXPECT_EQ(nullptr, dest_buffer); + EXPECT_EQ(src_buffer, context.next_in_); + EXPECT_EQ(10u, context.avail_in_); + EXPECT_EQ(10u, context.skip_size_); +} + +TEST(CFX_GifContext, ReadGifSignature) { + CFX_GifContextForTest context(nullptr, nullptr); + + { + uint8_t data[1]; + context.SetInputBuffer(data, 0); + EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadGifSignature()); + EXPECT_EQ(0u, context.skip_size_); + } + // Make sure testing the entire signature + { + uint8_t data[] = {'G', 'I', 'F'}; + context.SetInputBuffer(data, sizeof(data)); + EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadGifSignature()); + EXPECT_EQ(0u, context.skip_size_); + } + { + uint8_t data[] = {'N', 'O', 'T', 'G', 'I', 'F'}; + context.SetInputBuffer(data, sizeof(data)); + EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature()); + EXPECT_EQ(6u, context.skip_size_); + } + // Make sure not matching GIF8*a + { + uint8_t data[] = {'G', 'I', 'F', '8', '0', 'a'}; + context.SetInputBuffer(data, sizeof(data)); + EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature()); + EXPECT_EQ(6u, context.skip_size_); + } + // Make sure not matching GIF**a + { + uint8_t data[] = {'G', 'I', 'F', '9', '2', 'a'}; + context.SetInputBuffer(data, sizeof(data)); + EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature()); + EXPECT_EQ(6u, context.skip_size_); + } + // One valid signature + { + uint8_t data[] = {'G', 'I', 'F', '8', '7', 'a'}; + context.SetInputBuffer(data, sizeof(data)); + EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadGifSignature()); + EXPECT_EQ(6u, context.skip_size_); + } + // The other valid signature + { + uint8_t data[] = {'G', 'I', 'F', '8', '9', 'a'}; + context.SetInputBuffer(data, sizeof(data)); + EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadGifSignature()); + EXPECT_EQ(6u, context.skip_size_); + } +} + +TEST(CFX_GifContext, ReadLocalScreenDescriptor) { + CFX_GifContextForTest context(nullptr, nullptr); + { + uint8_t data[1]; + context.SetInputBuffer(data, 0); + EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, + context.ReadLogicalScreenDescriptor()); + } + // LSD with all the values zero'd + { + CFX_GifLocalScreenDescriptor lsd; + memset(&lsd, 0, sizeof(CFX_GifLocalScreenDescriptor)); + context.SetInputBuffer(reinterpret_cast<uint8_t*>(&lsd), + sizeof(CFX_GifLocalScreenDescriptor)); + + EXPECT_EQ(CFX_GifDecodeStatus::Success, + context.ReadLogicalScreenDescriptor()); + + EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor), context.skip_size_); + EXPECT_EQ(0, context.width_); + EXPECT_EQ(0, context.height_); + EXPECT_EQ(0u, context.bc_index_); + EXPECT_EQ(0u, context.pixel_aspect_); + } + // LSD with no global palette + { + CFX_GifLocalScreenDescriptor lsd{0x0A00, 0x000F, {0, 0, 0, 0}, 1, 2}; + context.SetInputBuffer(reinterpret_cast<uint8_t*>(&lsd), + sizeof(CFX_GifLocalScreenDescriptor)); + + EXPECT_EQ(CFX_GifDecodeStatus::Success, + context.ReadLogicalScreenDescriptor()); + + EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor), context.skip_size_); + EXPECT_EQ(0x000A, context.width_); + EXPECT_EQ(0x0F00, context.height_); + EXPECT_EQ(1u, context.bc_index_); + EXPECT_EQ(2u, context.pixel_aspect_); + } + // LSD with global palette bit set, but no global palette + { + CFX_GifLocalScreenDescriptor lsd{0x0A00, 0x000F, {0, 0, 0, 1}, 1, 2}; + context.SetInputBuffer(reinterpret_cast<uint8_t*>(&lsd), + sizeof(CFX_GifLocalScreenDescriptor)); + + EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, + context.ReadLogicalScreenDescriptor()); + + EXPECT_EQ(0u, context.skip_size_); + } + // LSD with global palette + { + CFX_GifLocalScreenDescriptor lsd = {0x0A00, 0x000F, {1, 1, 2, 1}, 1, 2}; + CFX_GifPalette palette[4] = {{0, 0, 0}, {1, 1, 1}, {1, 0, 0}, {0, 0, 1}}; + struct { + CFX_GifLocalScreenDescriptor lsd; + CFX_GifPalette palette[4]; + } data; + memcpy(&data.lsd, &lsd, sizeof(data.lsd)); + memcpy(&data.palette, &palette, sizeof(data.lsd)); + context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data)); + + EXPECT_EQ(CFX_GifDecodeStatus::Success, + context.ReadLogicalScreenDescriptor()); + + EXPECT_EQ(sizeof(data), context.skip_size_); + EXPECT_EQ(0x000A, context.width_); + EXPECT_EQ(0x0F00, context.height_); + EXPECT_EQ(1u, context.bc_index_); + EXPECT_EQ(2u, context.pixel_aspect_); + EXPECT_EQ(1u, context.global_pal_exp_); + EXPECT_EQ(1, context.global_sort_flag_); + EXPECT_EQ(2, context.global_color_resolution_); + EXPECT_TRUE(0 == memcmp(data.palette, context.global_palette_.data(), + sizeof(data.palette))); + } +} + +TEST(CFX_GifContext, ReadHeader) { + CFX_GifContextForTest context(nullptr, nullptr); + // Bad signature + { + uint8_t signature[] = {'N', 'O', 'T', 'G', 'I', 'F'}; + CFX_GifLocalScreenDescriptor lsd{0x0A00, 0x000F, {0, 0, 0, 0}, 1, 2}; + struct { + uint8_t signature[6]; + CFX_GifLocalScreenDescriptor lsd; + } data; + memcpy(&data.signature, signature, sizeof(data.signature)); + memcpy(&data.lsd, &lsd, sizeof(data.lsd)); + context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data)); + + EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadHeader()); + + EXPECT_EQ(sizeof(signature), context.skip_size_); + } + // Short after signature + { + uint8_t signature[] = {'G', 'I', 'F', '8', '7', 'a'}; + context.SetInputBuffer(reinterpret_cast<uint8_t*>(&signature), + sizeof(signature)); + + EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadHeader()); + + EXPECT_EQ(sizeof(signature), context.skip_size_); + } + // Success without global palette + { + uint8_t signature[] = {'G', 'I', 'F', '8', '7', 'a'}; + CFX_GifLocalScreenDescriptor lsd{0x0A00, 0x000F, {0, 0, 0, 0}, 1, 2}; + struct { + uint8_t signature[6]; + CFX_GifLocalScreenDescriptor lsd; + } data; + memcpy(&data.signature, signature, sizeof(data.signature)); + memcpy(&data.lsd, &lsd, sizeof(data.lsd)); + + context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data)); + + EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadHeader()); + + EXPECT_EQ(sizeof(data), context.skip_size_); + EXPECT_EQ(0x000A, context.width_); + EXPECT_EQ(0x0F00, context.height_); + EXPECT_EQ(1u, context.bc_index_); + EXPECT_EQ(2u, context.pixel_aspect_); + } + // Missing Global Palette + { + uint8_t signature[] = {'G', 'I', 'F', '8', '7', 'a'}; + CFX_GifLocalScreenDescriptor lsd{0x0A00, 0x000F, {0, 0, 0, 1}, 1, 2}; + + struct { + uint8_t signature[6]; + CFX_GifLocalScreenDescriptor lsd; + } data; + memcpy(&data.signature, signature, sizeof(data.signature)); + memcpy(&data.lsd, &lsd, sizeof(data.lsd)); + + context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data)); + + EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadHeader()); + + EXPECT_EQ(sizeof(signature), context.skip_size_); + } + // Success with global palette + { + uint8_t signature[] = {'G', 'I', 'F', '8', '7', 'a'}; + CFX_GifLocalScreenDescriptor lsd = {0x0A00, 0x000F, {1, 1, 2, 1}, 1, 2}; + CFX_GifPalette palette[4] = {{0, 0, 0}, {1, 1, 1}, {1, 0, 0}, {0, 0, 1}}; + + struct { + uint8_t signature[6]; + CFX_GifLocalScreenDescriptor lsd; + CFX_GifPalette palette[4]; + } data; + memcpy(&data.signature, signature, sizeof(data.signature)); + memcpy(&data.lsd, &lsd, sizeof(data.lsd)); + memcpy(&data.palette, &palette, sizeof(data.lsd)); + + context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data)); + + EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadHeader()); + + EXPECT_EQ(sizeof(data), context.skip_size_); + EXPECT_EQ(0x000A, context.width_); + EXPECT_EQ(0x0F00, context.height_); + EXPECT_EQ(1u, context.bc_index_); + EXPECT_EQ(2u, context.pixel_aspect_); + EXPECT_EQ(1u, context.global_pal_exp_); + EXPECT_EQ(1, context.global_sort_flag_); + EXPECT_EQ(2, context.global_color_resolution_); + EXPECT_TRUE(0 == memcmp(data.palette, context.global_palette_.data(), + sizeof(data.palette))); + } +} |