]> git.ipfire.org Git - thirdparty/zlib-ng.git/commitdiff
Fix bugs in creating a very large gzip header.
authorMark Adler <madler@alumni.caltech.edu>
Sun, 6 Nov 2016 05:55:34 +0000 (22:55 -0700)
committerHans Kristian Rosbach <hk-git@circlestorm.org>
Thu, 2 Feb 2017 09:57:25 +0000 (10:57 +0100)
deflate.c
deflate.h

index 4c0e55a48fd19a256f6dd49ccd03c2f544fee493..123cb42aae2618971c25ab5c0be29c58452c5a55 100644 (file)
--- a/deflate.c
+++ b/deflate.c
@@ -281,6 +281,9 @@ static int deflateStateCheck (z_stream *strm) {
         return 1;
     s = strm->state;
     if (s == NULL || s->strm != strm || (s->status != INIT_STATE &&
+#ifdef GZIP
+                                           s->status != GZIP_STATE &&
+#endif
                                            s->status != EXTRA_STATE &&
                                            s->status != NAME_STATE &&
                                            s->status != COMMENT_STATE &&
@@ -368,7 +371,12 @@ int ZEXPORT deflateResetKeep(z_stream *strm) {
     if (s->wrap < 0) {
         s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */
     }
-    s->status = s->wrap ? INIT_STATE : BUSY_STATE;
+    s->status =
+#ifdef GZIP
+        s->wrap == 2 ? GZIP_STATE :
+#endif
+        s->wrap ? INIT_STATE : BUSY_STATE;
+
 #ifdef GZIP
     strm->adler = s->wrap == 2 ? crc32(0L, NULL, 0) : adler32(0L, NULL, 0);
 #else
@@ -501,7 +509,6 @@ int ZEXPORT deflateTune(z_stream *strm, int good_length, int max_lazy, int nice_
 unsigned long ZEXPORT deflateBound(z_stream *strm, unsigned long sourceLen) {
     deflate_state *s;
     unsigned long complen, wraplen;
-    unsigned char *str;
 
     /* conservative upper bound for compressed data */
     complen = sourceLen + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;
@@ -519,9 +526,11 @@ unsigned long ZEXPORT deflateBound(z_stream *strm, unsigned long sourceLen) {
     case 1:                                 /* zlib wrapper */
         wraplen = 6 + (s->strstart ? 4 : 0);
         break;
+#ifdef GZIP
     case 2:                                 /* gzip wrapper */
         wraplen = 18;
         if (s->gzhead != NULL) {            /* user-supplied gzip header */
+            unsigned char *str;
             if (s->gzhead->extra != NULL) {
                 wraplen += 2 + s->gzhead->extra_len;
             }
@@ -541,6 +550,7 @@ unsigned long ZEXPORT deflateBound(z_stream *strm, unsigned long sourceLen) {
                 wraplen += 2;
         }
         break;
+#endif
     default:                                /* for compiler happiness */
         wraplen = 6;
     }
@@ -584,13 +594,22 @@ ZLIB_INTERNAL void flush_pending(z_stream *strm) {
     strm->next_out  += len;
     s->pending_out  += len;
     strm->total_out += len;
-    strm->avail_out  -= len;
-    s->pending -= len;
+    strm->avail_out -= len;
+    s->pending      -= len;
     if (s->pending == 0) {
         s->pending_out = s->pending_buf;
     }
 }
 
+/* ===========================================================================
+ * Update the header CRC with the bytes s->pending_buf[beg..s->pending - 1].
+ */
+#define HCRC_UPDATE(beg) \
+    do { \
+        if (s->gzhead->hcrc && s->pending > (beg)) \
+            strm->adler = crc32(strm->adler, s->pending_buf + (beg), s->pending - (beg)); \
+    } while (0)
+
 /* ========================================================================= */
 int ZEXPORT deflate(z_stream *strm, int flush) {
     int old_flush; /* value of flush param for previous deflate call */
@@ -608,137 +627,169 @@ int ZEXPORT deflate(z_stream *strm, int flush) {
     if (strm->avail_out == 0)
         ERR_RETURN(strm, Z_BUF_ERROR);
 
-    s->strm = strm; /* just in case */
     old_flush = s->last_flush;
     s->last_flush = flush;
 
+    /* Flush as much pending output as possible */
+    if (s->pending != 0) {
+        flush_pending(strm);
+        if (strm->avail_out == 0) {
+            /* Since avail_out is 0, deflate will be called again with
+             * more output space, but possibly with both pending and
+             * avail_in equal to zero. There won't be anything to do,
+             * but this is not an error situation so make sure we
+             * return OK instead of BUF_ERROR at next call of deflate:
+             */
+            s->last_flush = -1;
+            return Z_OK;
+        }
+
+    /* Make sure there is something to do and avoid duplicate consecutive
+     * flushes. For repeated and useless calls with Z_FINISH, we keep
+     * returning Z_STREAM_END instead of Z_BUF_ERROR.
+     */
+    } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) &&
+               flush != Z_FINISH) {
+        ERR_RETURN(strm, Z_BUF_ERROR);
+    }
+
+    /* User must not provide more input after the first FINISH: */
+    if (s->status == FINISH_STATE && strm->avail_in != 0) {
+        ERR_RETURN(strm, Z_BUF_ERROR);
+    }
+
     /* Write the header */
     if (s->status == INIT_STATE) {
-#ifdef GZIP
-        if (s->wrap == 2) {
-            crc_reset(s);
-            put_byte(s, 31);
-            put_byte(s, 139);
-            put_byte(s, 8);
-            if (s->gzhead == NULL) {
-                put_byte(s, 0);
-                put_byte(s, 0);
-                put_byte(s, 0);
-                put_byte(s, 0);
-                put_byte(s, 0);
-                put_byte(s, s->level == 9 ? 2 :
-                            (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
-                            4 : 0));
-                put_byte(s, OS_CODE);
-                s->status = BUSY_STATE;
-            } else {
-                put_byte(s, (s->gzhead->text ? 1 : 0) +
-                            (s->gzhead->hcrc ? 2 : 0) +
-                            (s->gzhead->extra == NULL ? 0 : 4) +
-                            (s->gzhead->name == NULL ? 0 : 8) +
-                            (s->gzhead->comment == NULL ? 0 : 16) );
-                put_byte(s, (unsigned char)(s->gzhead->time & 0xff));
-                put_byte(s, (unsigned char)((s->gzhead->time >> 8) & 0xff));
-                put_byte(s, (unsigned char)((s->gzhead->time >> 16) & 0xff));
-                put_byte(s, (unsigned char)((s->gzhead->time >> 24) & 0xff));
-                put_byte(s, s->level == 9 ? 2 :
-                            (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
-                             4 : 0));
-                put_byte(s, s->gzhead->os & 0xff);
-                if (s->gzhead->extra != NULL) {
-                    put_byte(s, s->gzhead->extra_len & 0xff);
-                    put_byte(s, (s->gzhead->extra_len >> 8) & 0xff);
-                }
-                if (s->gzhead->hcrc)
-                    strm->adler = crc32(strm->adler, s->pending_buf, s->pending);
-                s->gzindex = 0;
-                s->status = EXTRA_STATE;
-            }
-        } else
-#endif
-        {
-            unsigned int header = (Z_DEFLATED + ((s->w_bits-8) << 4)) << 8;
-            unsigned int level_flags;
-
-            if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
-                level_flags = 0;
-            else if (s->level < TRIGGER_LEVEL)
-                level_flags = 1;
-            else if (s->level == TRIGGER_LEVEL)
-                level_flags = 2;
-            else
-                level_flags = 3;
-            header |= (level_flags << 6);
-            if (s->strstart != 0)
-                header |= PRESET_DICT;
-            header += 31 - (header % 31);
+        /* zlib header */
+        unsigned int header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+        unsigned int level_flags;
+
+        if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
+            level_flags = 0;
+        else if (s->level < 6)
+            level_flags = 1;
+        else if (s->level == 6)
+            level_flags = 2;
+        else
+            level_flags = 3;
+        header |= (level_flags << 6);
+        if (s->strstart != 0) header |= PRESET_DICT;
+        header += 31 - (header % 31);
+
+        putShortMSB(s, header);
 
+        /* Save the adler32 of the preset dictionary: */
+        if (s->strstart != 0) {
+            putShortMSB(s, (uint16_t)(strm->adler >> 16));
+            putShortMSB(s, (uint16_t)(strm->adler));
+        }
+        strm->adler = adler32(0L, NULL, 0);
+        s->status = BUSY_STATE;
+
+        /* Compression must start with an empty pending buffer */
+        flush_pending(strm);
+        if (s->pending != 0) {
+            s->last_flush = -1;
+            return Z_OK;
+        }
+    }
+#ifdef GZIP
+    if (s->status == GZIP_STATE) {
+        /* gzip header */
+        strm->adler = crc32(0L, NULL, 0);
+        put_byte(s, 31);
+        put_byte(s, 139);
+        put_byte(s, 8);
+        if (s->gzhead == NULL) {
+            put_byte(s, 0);
+            put_byte(s, 0);
+            put_byte(s, 0);
+            put_byte(s, 0);
+            put_byte(s, 0);
+            put_byte(s, s->level == 9 ? 2 :
+                     (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0));
+            put_byte(s, OS_CODE);
             s->status = BUSY_STATE;
-            putShortMSB(s, header);
 
-            /* Save the adler32 of the preset dictionary: */
-            if (s->strstart != 0) {
-                putShortMSB(s, (uint16_t)(strm->adler >> 16));
-                putShortMSB(s, (uint16_t)strm->adler);
+            /* Compression must start with an empty pending buffer */
+            flush_pending(strm);
+            if (s->pending != 0) {
+                s->last_flush = -1;
+                return Z_OK;
+            }
+        }
+        else {
+            put_byte(s, (s->gzhead->text ? 1 : 0) +
+                     (s->gzhead->hcrc ? 2 : 0) +
+                     (s->gzhead->extra == NULL ? 0 : 4) +
+                     (s->gzhead->name == NULL ? 0 : 8) +
+                     (s->gzhead->comment == NULL ? 0 : 16)
+                     );
+            put_byte(s, (unsigned char)(s->gzhead->time & 0xff));
+            put_byte(s, (unsigned char)((s->gzhead->time >> 8) & 0xff));
+            put_byte(s, (unsigned char)((s->gzhead->time >> 16) & 0xff));
+            put_byte(s, (unsigned char)((s->gzhead->time >> 24) & 0xff));
+            put_byte(s, s->level == 9 ? 2 :
+                     (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0));
+            put_byte(s, s->gzhead->os & 0xff);
+            if (s->gzhead->extra != NULL) {
+                put_byte(s, s->gzhead->extra_len & 0xff);
+                put_byte(s, (s->gzhead->extra_len >> 8) & 0xff);
             }
-            strm->adler = adler32(0L, NULL, 0);
+            if (s->gzhead->hcrc)
+                strm->adler = crc32(strm->adler, s->pending_buf, s->pending);
+            s->gzindex = 0;
+            s->status = EXTRA_STATE;
         }
     }
-#ifdef GZIP
     if (s->status == EXTRA_STATE) {
-        if (s->gzhead->extra != NULL) {
-            uint32_t beg = s->pending;  /* start of bytes to update crc */
-
-            while (s->gzindex < (s->gzhead->extra_len & 0xffff)) {
-                if (s->pending == s->pending_buf_size) {
-                    if (s->gzhead->hcrc && s->pending > beg)
-                        strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg);
-                    flush_pending(strm);
-                    beg = s->pending;
-                    if (s->pending == s->pending_buf_size)
-                        break;
+        if (s->gzhead->extra != Z_NULL) {
+            uint32_t beg = s->pending;   /* start of bytes to update crc */
+            uint32_t left = (s->gzhead->extra_len & 0xffff) - s->gzindex;
+
+            while (s->pending + left > s->pending_buf_size) {
+                uint32_t copy = s->pending_buf_size - s->pending;
+                memcpy(s->pending_buf + s->pending, s->gzhead->extra + s->gzindex, copy);
+                s->pending = s->pending_buf_size;
+                HCRC_UPDATE(beg);
+                s->gzindex += copy;
+                flush_pending(strm);
+                if (s->pending != 0) {
+                    s->last_flush = -1;
+                    return Z_OK;
                 }
-                put_byte(s, s->gzhead->extra[s->gzindex]);
-                s->gzindex++;
-            }
-            if (s->gzhead->hcrc && s->pending > beg)
-                strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg);
-            if (s->gzindex == s->gzhead->extra_len) {
-                s->gzindex = 0;
-                s->status = NAME_STATE;
+                beg = 0;
+                left -= copy;
             }
-        } else {
-            s->status = NAME_STATE;
+            memcpy(s->pending_buf + s->pending, s->gzhead->extra + s->gzindex, left);
+            s->pending += left;
+            HCRC_UPDATE(beg);
+            s->gzindex = 0;
         }
+        s->status = NAME_STATE;
     }
     if (s->status == NAME_STATE) {
         if (s->gzhead->name != NULL) {
-            uint32_t beg = s->pending;  /* start of bytes to update crc */
+            uint32_t beg = s->pending;   /* start of bytes to update crc */
             int val;
 
             do {
                 if (s->pending == s->pending_buf_size) {
-                    if (s->gzhead->hcrc && s->pending > beg)
-                        strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg);
+                    HCRC_UPDATE(beg);
                     flush_pending(strm);
-                    beg = s->pending;
-                    if (s->pending == s->pending_buf_size) {
-                        val = 1;
-                        break;
+                    if (s->pending != 0) {
+                        s->last_flush = -1;
+                        return Z_OK;
                     }
+                    beg = 0;
                 }
                 val = s->gzhead->name[s->gzindex++];
                 put_byte(s, val);
             } while (val != 0);
-            if (s->gzhead->hcrc && s->pending > beg)
-                strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg);
-            if (val == 0) {
-                s->gzindex = 0;
-                s->status = COMMENT_STATE;
-            }
-        } else {
-            s->status = COMMENT_STATE;
+            HCRC_UPDATE(beg);
+            s->gzindex = 0;
         }
+        s->status = COMMENT_STATE;
     }
     if (s->status == COMMENT_STATE) {
         if (s->gzhead->comment != NULL) {
@@ -747,68 +798,44 @@ int ZEXPORT deflate(z_stream *strm, int flush) {
 
             do {
                 if (s->pending == s->pending_buf_size) {
-                    if (s->gzhead->hcrc && s->pending > beg)
-                        strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg);
+                    HCRC_UPDATE(beg);
                     flush_pending(strm);
-                    beg = s->pending;
-                    if (s->pending == s->pending_buf_size) {
-                        val = 1;
-                        break;
+                    if (s->pending != 0) {
+                        s->last_flush = -1;
+                        return Z_OK;
                     }
+                    beg = 0;
                 }
                 val = s->gzhead->comment[s->gzindex++];
                 put_byte(s, val);
             } while (val != 0);
-            if (s->gzhead->hcrc && s->pending > beg)
-                strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg);
-            if (val == 0)
-                s->status = HCRC_STATE;
-        } else {
-            s->status = HCRC_STATE;
+            HCRC_UPDATE(beg);
         }
+        s->status = HCRC_STATE;
     }
     if (s->status == HCRC_STATE) {
         if (s->gzhead->hcrc) {
-            if (s->pending + 2 > s->pending_buf_size)
+            if (s->pending + 2 > s->pending_buf_size) {
                 flush_pending(strm);
-            if (s->pending + 2 <= s->pending_buf_size) {
-                put_byte(s, (unsigned char)(strm->adler & 0xff));
-                put_byte(s, (unsigned char)((strm->adler >> 8) & 0xff));
-                strm->adler = crc32(0L, NULL, 0);
-                s->status = BUSY_STATE;
+                if (s->pending != 0) {
+                    s->last_flush = -1;
+                    return Z_OK;
+                }
             }
-        } else {
-            s->status = BUSY_STATE;
+            put_byte(s, (unsigned char)(strm->adler & 0xff));
+            put_byte(s, (unsigned char)((strm->adler >> 8) & 0xff));
+            strm->adler = crc32(0L, NULL, 0);
         }
-    }
-#endif
+        s->status = BUSY_STATE;
 
-    /* Flush as much pending output as possible */
-    if (s->pending != 0) {
+        /* Compression must start with an empty pending buffer */
         flush_pending(strm);
-        if (strm->avail_out == 0) {
-            /* Since avail_out is 0, deflate will be called again with
-             * more output space, but possibly with both pending and
-             * avail_in equal to zero. There won't be anything to do,
-             * but this is not an error situation so make sure we
-             * return OK instead of BUF_ERROR at next call of deflate:
-             */
+        if (s->pending != 0) {
             s->last_flush = -1;
             return Z_OK;
         }
-
-    /* Make sure there is something to do and avoid duplicate consecutive
-     * flushes. For repeated and useless calls with Z_FINISH, we keep
-     * returning Z_STREAM_END instead of Z_BUF_ERROR.
-     */
-    } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && flush != Z_FINISH) {
-        ERR_RETURN(strm, Z_BUF_ERROR);
-    }
-
-    /* User must not provide more input after the first FINISH: */
-    if (s->status == FINISH_STATE && strm->avail_in != 0) {
-        ERR_RETURN(strm, Z_BUF_ERROR);
     }
+#endif
 
     /* Start a new block or continue the current one.
      */
index 5d122d803d527294ba01c736b061400c01a3d0e9..8f96203b4917580164f1254aa95e9955dd98f05e 100644 (file)
--- a/deflate.h
+++ b/deflate.h
 #define END_BLOCK 256
 /* end of block literal code */
 
-#define INIT_STATE    42
-#define EXTRA_STATE   69
-#define NAME_STATE    73
-#define COMMENT_STATE 91
-#define HCRC_STATE   103
-#define BUSY_STATE   113
-#define FINISH_STATE 666
+#define INIT_STATE    42    /* zlib header -> BUSY_STATE */
+#ifdef GZIP
+#  define GZIP_STATE  57    /* gzip header -> BUSY_STATE | EXTRA_STATE */
+#endif
+#define EXTRA_STATE   69    /* gzip extra block -> NAME_STATE */
+#define NAME_STATE    73    /* gzip file name -> COMMENT_STATE */
+#define COMMENT_STATE 91    /* gzip comment -> HCRC_STATE */
+#define HCRC_STATE   103    /* gzip header CRC -> BUSY_STATE */
+#define BUSY_STATE   113    /* deflate -> FINISH_STATE */
+#define FINISH_STATE 666    /* stream complete */
 /* Stream status */
 
 
@@ -105,10 +108,10 @@ typedef struct internal_state {
     unsigned char *pending_buf;      /* output still pending */
     unsigned long pending_buf_size;  /* size of pending_buf */
     unsigned char *pending_out;      /* next pending byte to output to the stream */
-    unsigned int  pending;           /* nb of bytes in the pending buffer */
+    uint32_t      pending;           /* nb of bytes in the pending buffer */
     int           wrap;              /* bit 0 true for zlib, bit 1 true for gzip */
     gz_headerp    gzhead;            /* gzip header information to write */
-    unsigned int  gzindex;           /* where in extra, name, or comment */
+    uint32_t      gzindex;           /* where in extra, name, or comment */
     unsigned char method;            /* can only be DEFLATED */
     int           last_flush;        /* value of flush param for previous deflate call */