Drop APNG frames that don't fit entirely within the destination surface. (flutter/engine#56928)
As per the [spec](https://www.w3.org/TR/png/#fcTL-chunk): > The frame must be rendered within the region defined by x_offset, y_offset, width, and height. This region may not fall outside of the default image; thus x_offset plus width must not be greater than the [IHDR](https://www.w3.org/TR/png/#11IHDR) width; similarly y_offset plus height must not be greater than the [IHDR](https://www.w3.org/TR/png/#11IHDR) height.
This commit is contained in:
parent
7b1b6d13f2
commit
ba21393f49
BIN
engine/src/flutter/lib/ui/fixtures/out_of_bounds.apng
Normal file
BIN
engine/src/flutter/lib/ui/fixtures/out_of_bounds.apng
Normal file
Binary file not shown.
After Width: | Height: | Size: 126 B |
@ -197,7 +197,7 @@ TEST(ImageDecoderNoGLTest, ImpellerWideGamutIndexedPng) {
|
|||||||
#endif // IMPELLER_SUPPORTS_RENDERING
|
#endif // IMPELLER_SUPPORTS_RENDERING
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ImageDecoderNoGLTest, ImepllerUnmultipliedAlphaPng) {
|
TEST(ImageDecoderNoGLTest, ImpellerUnmultipliedAlphaPng) {
|
||||||
#if defined(OS_FUCHSIA)
|
#if defined(OS_FUCHSIA)
|
||||||
GTEST_SKIP() << "Fuchsia can't load the test fixtures.";
|
GTEST_SKIP() << "Fuchsia can't load the test fixtures.";
|
||||||
#endif
|
#endif
|
||||||
|
@ -110,6 +110,20 @@ bool APNGImageGenerator::GetPixels(const SkImageInfo& info,
|
|||||||
<< ") of APNG due to the frame missing data (frame_info).";
|
<< ") of APNG due to the frame missing data (frame_info).";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (frame.x_offset + frame_info.width() >
|
||||||
|
static_cast<unsigned int>(info.width()) ||
|
||||||
|
frame.y_offset + frame_info.height() >
|
||||||
|
static_cast<unsigned int>(info.height())) {
|
||||||
|
FML_DLOG(ERROR)
|
||||||
|
<< "Decoded image at index " << image_index
|
||||||
|
<< " (frame index: " << frame_index
|
||||||
|
<< ") rejected because the destination region (x: " << frame.x_offset
|
||||||
|
<< ", y: " << frame.y_offset << ", width: " << frame_info.width()
|
||||||
|
<< ", height: " << frame_info.height()
|
||||||
|
<< ") is not entirely within the destination surface (width: "
|
||||||
|
<< info.width() << ", height: " << info.height() << ").";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
/// 3. Composite the frame onto the canvas.
|
/// 3. Composite the frame onto the canvas.
|
||||||
@ -630,7 +644,19 @@ uint32_t APNGImageGenerator::ChunkHeader::ComputeChunkCrc32() {
|
|||||||
bool APNGImageGenerator::RenderDefaultImage(const SkImageInfo& info,
|
bool APNGImageGenerator::RenderDefaultImage(const SkImageInfo& info,
|
||||||
void* pixels,
|
void* pixels,
|
||||||
size_t row_bytes) {
|
size_t row_bytes) {
|
||||||
SkCodec::Result result = images_[0].codec->getPixels(info, pixels, row_bytes);
|
APNGImage& frame = images_[0];
|
||||||
|
SkImageInfo frame_info = frame.codec->getInfo();
|
||||||
|
if (frame_info.width() > info.width() ||
|
||||||
|
frame_info.height() > info.height()) {
|
||||||
|
FML_DLOG(ERROR)
|
||||||
|
<< "Default image rejected because the destination region (width: "
|
||||||
|
<< frame_info.width() << ", height: " << frame_info.height()
|
||||||
|
<< ") is not entirely within the destination surface (width: "
|
||||||
|
<< info.width() << ", height: " << info.height() << ").";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkCodec::Result result = frame.codec->getPixels(info, pixels, row_bytes);
|
||||||
if (result != SkCodec::kSuccess) {
|
if (result != SkCodec::kSuccess) {
|
||||||
FML_DLOG(ERROR) << "Failed to decode the APNG's default/fallback image. "
|
FML_DLOG(ERROR) << "Failed to decode the APNG's default/fallback image. "
|
||||||
"SkCodec::Result: "
|
"SkCodec::Result: "
|
||||||
|
@ -252,6 +252,26 @@ void main() {
|
|||||||
imageData = (await image.toByteData())!;
|
imageData = (await image.toByteData())!;
|
||||||
expect(imageData.getUint32(imageData.lengthInBytes - 4), 0x00000000);
|
expect(imageData.getUint32(imageData.lengthInBytes - 4), 0x00000000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Animated apng frame decode does not crash with invalid destination region',
|
||||||
|
() async {
|
||||||
|
final Uint8List data = File(
|
||||||
|
path.join('flutter', 'lib', 'ui', 'fixtures', 'out_of_bounds.apng'),
|
||||||
|
).readAsBytesSync();
|
||||||
|
|
||||||
|
final ui.Codec codec = await ui.instantiateImageCodec(data);
|
||||||
|
try {
|
||||||
|
await codec.getNextFrame();
|
||||||
|
fail('exception not thrown');
|
||||||
|
} on Exception catch (e) {
|
||||||
|
if (impellerEnabled) {
|
||||||
|
expect(e.toString(), contains('Could not decompress image.'));
|
||||||
|
} else {
|
||||||
|
expect(e.toString(), contains('Codec failed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a File handle to a file in the skia/resources directory.
|
/// Returns a File handle to a file in the skia/resources directory.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user