You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Valid PNG files could fail to decode, in every release from 1.0.0 to 1.10.0. The deflate decoder's fast loop could read a length/distance pair from bits it had not yet loaded when the pair followed a literal with a long code, which happens in files whose compressed data uses long codes for rare literals and for far, long matches (some optimizing encoders produce this; zlib and this library's own encoder did not in any test file). The stream then went out of step, and the read failed with "Invalid DEFLATE block type", "Stored block length check failed", "Invalid literal/length code", "Invalid scanline filter type", an Adler-32 mismatch, or an ArgumentOutOfRangeException from the data reader. The same decoder reads TIFF Deflate strips and tiles, PSD ZIP channels, SVGZ, WOFF tables and PNG iCCP/zTXt text, so those formats could fail the same way. The decoder now reloads its bits before such a pair.
Wrong pixels without an error were possible for unusual compressed data, though rare in practice. The damaged bytes were caught only by the Adler-32 checksum or by the PNG scanline-header check, never by the decoder itself. In tests on the old decoder, using data that did trigger the bug, a whole-image read of a PNG, TIFF or PSD never returned wrong pixels: all of them failed (the six reported PNGs, 72 generated PNGs with the pattern at three densities, 40 generated streams each as TIFF and as PSD). Reading only part of a PNG could: a crop, region or band that ends inside the damaged data and before the next scanline header could return wrong pixels in its last rows with no error (1 of 956 partial reads of the reported files, in 1 of the 6 files; 43 of 11,448 partial reads of the generated files, in 29 of 72). Ordinary compressors (zlib at every level and strategy, and this library's own encoder) did not produce the pattern in any of about 200 test files; the six reported files came from an encoder that emits long codes for rare literals and for far, long matches.
Complete reads of deflate data now check the checksum at the end of the stream, which an earlier release did only when the four trailer bytes happened to arrive with the last rows (about one PNG in ten thousand missed it). What is checked: a PNG read of every row, a TIFF Deflate strip or tile, a PSD ZIP channel, a WOFF table and a gzip-compressed SVG each verify the zlib Adler-32 (gzip: CRC-32 and size) and fail with a format error when a trailer that is present in full does not match. What is tolerated, as in earlier releases: a file that ends right after the last row or byte, or after 1 to 3 trailer bytes (gzip: fewer than the 8), is accepted and its pixels are returned unchanged; deflate data that ends before everything was produced is still an error. iCCP profiles and compressed XMP with a bad or missing trailer are dropped without failing the image. What is not checked: a read of only part of a PNG (a crop, a region, a streaming window) holds only part of the stream, so it cannot verify the checksum; only the chunk CRCs of the chunks it read cover it.
A decode started from an image loaded with Image.Load(Stream) or LoadAsync(Stream) could read overwritten bytes if the Image became unreachable while the decode was still running (for example Image.Load(stream).Save(...) or .ToArray<T>() on a temporary). The compressed bytes of such an image live in a pooled buffer that is handed back to the pool when the image is collected, and the stream the decoder reads from did not keep that buffer alive; another thread could rent and overwrite it mid-read. Formats with checksums or strict structure (PNG, GIF, WebP, TIFF compression) then failed with a format error such as "Invalid DEFLATE block type", "Too many length or distance codes" or an Adler-32 mismatch; for data without redundancy (raw or lightly compressed pixels, JPEG coefficients) wrong pixels without an error were possible in principle. Present since 1.0.0. The stream now holds the buffer's owner. Loading from a file path or from memory you pass in (ReadOnlyMemory<byte>) is not affected by this.