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,

Reply via email to