Skip to content

Conversation

@brianpopow
Copy link
Collaborator

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

This PR changes the PNG decoder to verify that the CRC of IDAT chunk is correct.

@codecov
Copy link

codecov bot commented Dec 16, 2021

Codecov Report

Merging #1898 (112585c) into master (5e060ba) will decrease coverage by 0%.
The diff coverage is 100%.

Impacted file tree graph

@@          Coverage Diff           @@
##           master   #1898   +/-   ##
======================================
- Coverage      87%     87%   -1%     
======================================
  Files         960     960           
  Lines       50972   50975    +3     
  Branches     6310    6311    +1     
======================================
+ Hits        44731   44733    +2     
  Misses       5202    5202           
- Partials     1039    1040    +1     
Flag Coverage Δ
unittests 87% <100%> (-1%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/ImageSharp/Formats/Png/PngDecoderCore.cs 90% <100%> (-1%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5e060ba...112585c. Read the comment docs.

Comment on lines +1226 to +1231
// Restore the stream position for IDAT chunks, because it will be decoded later and
// was only read to verifying the CRC is correct.
if (type == PngChunkType.Data)
{
this.currentStream.Position = pos;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to read the IDAT chunk twice? Upon rewinding the stream, this means creating & filling PngChunk.Data twice. Can't we avoid this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont see how we can avoid reading the stream twice. We need to read the stream completely once to be able to calculate the CRC. Then we are sure the byte stream is not corrupted and can start decoding it in ZlibInflateStream. Otherwise we would need to introduce PNG specific logic into ZlibInflateStream, which i dont think would be good.
Also the compressed stream can span over multiple IDAT chunks, which would make it even more complicated.

Note also the PngChunk.Data is not filled twice, just once. Before this PR the PngChunk.Data was left empty.
So we have now with this PR an additional allocation of the size of IDAT chunk.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does libpng check it? I know it’s optional but haven’t had the chance to dig through the code

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, libpng does check it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One way to possible avoid a large allocation here for the IDAT chunk just to calculate the CRC could be to read the IDAT data in smaller portions from the stream (lets say 8192 bytes) and calculate the CRC on those smaller chunk's of data.

Is it worth introducing such special handling for calculating the CRC of IDAT?

Copy link
Member

@antonfirsov antonfirsov Dec 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ZlibInflateStream operates over BufferedReadStream, so we can consider making CRC32 calculation as an optional part of BufferedReadStream buffer fills. Although it's definitely not a 2.0 optimization, I would open a separate issue to investigate this.

read the IDAT data in smaller portions from the stream (lets say 8192 bytes) and calculate the CRC on those smaller chunk's of data.

If my understanding is correct, PNG-s with single very large IDAT chunks are rare, so it's probably not worth it, I would better investigate the BufferedReadStream approach, since it also avoids stream rewinding.

brianpopow and others added 2 commits December 16, 2021 15:45
Co-authored-by: Anton Firszov <antonfir@gmail.com>
Copy link
Member

@antonfirsov antonfirsov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good for 2.0, but might worth to investigate on-the-fly CRC32 calculation in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants