Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
image_generator_apng_unittests.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <cstdint>
8#include <cstring>
9#include <vector>
10
13#include "third_party/skia/include/core/SkData.h"
14
15namespace flutter {
16namespace testing {
17
18namespace {
19
20// Writes a big-endian uint32_t to a buffer.
21void WriteBE32(std::vector<uint8_t>& buf, uint32_t val) {
22 buf.push_back((val >> 24) & 0xFF);
23 buf.push_back((val >> 16) & 0xFF);
24 buf.push_back((val >> 8) & 0xFF);
25 buf.push_back(val & 0xFF);
26}
27
28// Writes a big-endian uint16_t to a buffer.
29void WriteBE16(std::vector<uint8_t>& buf, uint16_t val) {
30 buf.push_back((val >> 8) & 0xFF);
31 buf.push_back(val & 0xFF);
32}
33
34// Appends a PNG chunk (length + type + data + CRC) to the buffer.
35void AppendChunk(std::vector<uint8_t>& buf,
36 const char type[4],
37 const std::vector<uint8_t>& data) {
38 FML_CHECK(data.size() <= std::numeric_limits<uint32_t>::max());
39 WriteBE32(buf, static_cast<uint32_t>(data.size()));
40 size_t type_start = buf.size();
41 buf.insert(buf.end(), type, type + 4);
42 buf.insert(buf.end(), data.begin(), data.end());
43 uint32_t crc = APNGImageGenerator::ComputeCrc32(buf.data() + type_start,
44 4 + data.size());
45 WriteBE32(buf, crc);
46}
47
48// Appends a chunk with a declared data_length that may differ from the actual
49// data bytes written. Used to test handling of malformed chunks.
50void AppendChunkWithFakeLength(std::vector<uint8_t>& buf,
51 const char type[4],
52 uint32_t declared_length,
53 const std::vector<uint8_t>& actual_data) {
54 WriteBE32(buf, declared_length);
55 buf.insert(buf.end(), type, type + 4);
56 buf.insert(buf.end(), actual_data.begin(), actual_data.end());
57 WriteBE32(buf, 0); // CRC placeholder
58}
59
60// Builds a minimal valid APNG with a malicious fdAT chunk whose
61// data_length is less than 4, which would trigger an integer underflow
62// in DemuxNextImage() without the bounds check fix.
63std::vector<uint8_t> BuildMaliciousApng(uint32_t fdat_data_length) {
64 std::vector<uint8_t> apng(APNGImageGenerator::kPngSignature.begin(),
66
67 // IHDR: 1x1 RGBA, 8-bit
68 {
69 std::vector<uint8_t> ihdr;
70 WriteBE32(ihdr, 1); // width
71 WriteBE32(ihdr, 1); // height
72 ihdr.push_back(8); // bit depth
73 ihdr.push_back(6); // color type (RGBA)
74 ihdr.push_back(0); // compression
75 ihdr.push_back(0); // filter
76 ihdr.push_back(0); // interlace
77 AppendChunk(apng, "IHDR", ihdr);
78 }
79
80 // acTL: 1 frame, loop forever
81 {
82 std::vector<uint8_t> actl;
83 WriteBE32(actl, 1); // num_frames
84 WriteBE32(actl, 0); // num_plays (0 = infinite)
85 AppendChunk(apng, "acTL", actl);
86 }
87
88 // fcTL for frame 0
89 {
90 std::vector<uint8_t> fctl;
91 WriteBE32(fctl, 0); // sequence_number
92 WriteBE32(fctl, 1); // width
93 WriteBE32(fctl, 1); // height
94 WriteBE32(fctl, 0); // x_offset
95 WriteBE32(fctl, 0); // y_offset
96 WriteBE16(fctl, 1); // delay_num
97 WriteBE16(fctl, 10); // delay_den
98 fctl.push_back(0); // dispose_op
99 fctl.push_back(0); // blend_op
100 AppendChunk(apng, "fcTL", fctl);
101 }
102
103 // Malicious fdAT for frame 0: data_length < 4
104 // An fdAT chunk must have at least 4 bytes (sequence number).
105 // With data_length < 4, the subtraction in DemuxNextImage() underflows.
106 AppendChunk(apng, "fdAT", std::vector<uint8_t>(fdat_data_length, 0));
107
108 // IEND
109 AppendChunk(apng, "IEND", {});
110
111 return apng;
112}
113
114} // namespace
115
116// Verify that the APNG decoder can handle fdAT chunks whose length is shorter
117// than the required 4-byte sequence number.
118TEST(APNGImageGeneratorTest, FdATWithShortDataLengthDoesNotCrash) {
119 ImageGeneratorRegistry registry;
120
121 auto make_generator = [](uint32_t fdat_length) -> auto {
122 auto apng_bytes = BuildMaliciousApng(fdat_length);
123 auto data = SkData::MakeWithCopy(apng_bytes.data(), apng_bytes.size());
125 };
126
127 // The decoder should reject fdAT chunks that are less than 4 bytes long.
128 EXPECT_EQ(make_generator(0), nullptr);
129 EXPECT_EQ(make_generator(2), nullptr);
130
131 // Creating the generator should succeed if the fdAT has sufficient length.
132 EXPECT_NE(make_generator(4), nullptr);
133}
134
135TEST(APNGImageGeneratorTest, FdATWithOverflowDataLengthIsRejected) {
136 std::vector<uint8_t> apng(APNGImageGenerator::kPngSignature.begin(),
138
139 // IHDR
140 {
141 std::vector<uint8_t> ihdr;
142 WriteBE32(ihdr, 1);
143 WriteBE32(ihdr, 1);
144 ihdr.push_back(8);
145 ihdr.push_back(6);
146 ihdr.push_back(0);
147 ihdr.push_back(0);
148 ihdr.push_back(0);
149 AppendChunk(apng, "IHDR", ihdr);
150 }
151 // acTL
152 {
153 std::vector<uint8_t> actl;
154 WriteBE32(actl, 1);
155 WriteBE32(actl, 0);
156 AppendChunk(apng, "acTL", actl);
157 }
158 // fcTL
159 {
160 std::vector<uint8_t> fctl;
161 WriteBE32(fctl, 0);
162 WriteBE32(fctl, 1);
163 WriteBE32(fctl, 1);
164 WriteBE32(fctl, 0);
165 WriteBE32(fctl, 0);
166 WriteBE16(fctl, 1);
167 WriteBE16(fctl, 10);
168 fctl.push_back(0);
169 fctl.push_back(0);
170 AppendChunk(apng, "fcTL", fctl);
171 }
172 // fdAT with declared data_length=0xFFFFFFFF but only 8 actual bytes
173 AppendChunkWithFakeLength(apng, "fdAT", 0xFFFFFFFF, {0, 0, 0, 1, 0, 0, 0, 0});
174 // IEND
175 AppendChunk(apng, "IEND", {});
176
177 auto data = SkData::MakeWithCopy(apng.data(), apng.size());
178 auto generator = APNGImageGenerator::MakeFromData(data);
179 // The generator should reject the malformed APNG without crashing.
180 EXPECT_EQ(generator, nullptr);
181}
182
183} // namespace testing
184} // namespace flutter
static uint32_t ComputeCrc32(const uint8_t *data, size_t length)
Computes the CRC of the data in a PNG chunk.
static constexpr std::array< uint8_t, 8 > kPngSignature
Signature at the start of a PNG file.
static std::unique_ptr< ImageGenerator > MakeFromData(sk_sp< SkData > data)
Keeps a priority-ordered registry of image generator builders to be used when decoding images....
#define FML_CHECK(condition)
Definition logging.h:104
TEST(NativeAssetsManagerTest, NoAvailableAssets)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
impeller::ShaderType type
const size_t end