Package: pngcheck Version: 2.3.0-7 Severity: wishlist File: /usr/bin/pngcheck
pngcheck does not detect some cases of bad "window bits" size in the png file. For example, https://www.gutenberg.org/files/16713/16713-h/images/q169b2.png (attached below, in case rectified soon) pnginfo -d q169b2.png => libpng error: IDAT: invalid distance too far back pngcheck q169b2.png => succeeds whereas I hoped pngcheck would fail. This is the IDAT "window bits" CINFO too small for what the data actually needs. pngcheck inflates into a big output buffer and zlib by default makes use of that, thereby tolerating the problem. It'd be good if pngcheck used a copy of zlib compiled with its INFLATE_STRICT option, so as to detect the problem. Dunno if configury for a suitable copy of zlib would be easy or hard. I tried a byte-by-byte wrapper which gets the same effect with normal zlib, but is much less efficient of course.
--- pngcheck.c.orig 2007-07-08 16:23:31.000000000 +1000 +++ pngcheck.c 2018-10-23 17:36:13.000000000 +1100 @@ -1011,6 +1011,26 @@ } +/* strict_inflate() same as inflate() but enforcing window size in the + manner of zlib compiled with its INFLATE_STRICT option. Normally + inflate() tolerates window size too small if the output buffer is big + enough that it contains the needed data. Going byte by byte here ensures + the output buffer isn't and thus window size too small is detected. + ENHANCE-ME: Better to use the zlib INFLATE_STRICT option. + */ +int strict_inflate(z_stream *z, int flush) +{ + /* "extra" is the part of avail_out not presented to inflate() */ + int err; + do { + uInt extra = (z->avail_out ? z->avail_out - 1 : 0); + z->avail_out -= extra; + err = inflate(z, flush); + z->avail_out += extra; + } while (err == Z_OK && z->avail_in > 0 && z->avail_out > 0); + return err; +} + int pngcheck(FILE *fp, char *fname, int searching, FILE *fpOut) { @@ -1807,7 +1827,7 @@ while (err != Z_STREAM_END && zstrm.avail_in > 0) { /* know zstrm.avail_out > 0: get some image/filter data */ - err = inflate(&zstrm, Z_SYNC_FLUSH); + err = strict_inflate(&zstrm, Z_SYNC_FLUSH); if (err != Z_OK && err != Z_STREAM_END) { printf("%s zlib: inflate error = %d (%s)\n", verbose > 1? "\n " : (verbose == 1? " ":fname), err,