[gmime] Fixed GMimeFilterGZip's unzip logic and made it more robust



commit 8340c3afc86e1270bd0837cce0f283d2e6185ec1
Author: Jeffrey Stedfast <jestedfa microsoft com>
Date:   Thu Dec 14 09:25:08 2017 -0500

    Fixed GMimeFilterGZip's unzip logic and made it more robust

 gmime/gmime-filter-gzip.c |  126 ++++++++++++++++++++++++---------------------
 tests/test-filters.c      |    2 +-
 2 files changed, 69 insertions(+), 59 deletions(-)
---
diff --git a/gmime/gmime-filter-gzip.c b/gmime/gmime-filter-gzip.c
index 39d810b..3b66c62 100644
--- a/gmime/gmime-filter-gzip.c
+++ b/gmime/gmime-filter-gzip.c
@@ -304,19 +304,27 @@ gunzip_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
 {
        GMimeFilterGZip *gzip = (GMimeFilterGZip *) filter;
        struct _GMimeFilterGZipPrivate *priv = gzip->priv;
+       const char *inend = in + len;
+       const char *inptr = in;
        guint16 need, val;
+       size_t left = len;
        int retval;
        
+       *outprespace = prespace;
+       *outlen = 0;
+       *out = in;
+       
        if (!priv->state.unzip.got_hdr) {
-               if (len < 10) {
-                       g_mime_filter_backup (filter, in, len);
+               if (left < 10) {
+                       /* not enough data to decode the header */
+                       g_mime_filter_backup (filter, inptr, left);
                        return;
                }
                
-               memcpy (priv->hdr.buf, in, 10);
+               memcpy (priv->hdr.buf, inptr, 10);
                priv->state.unzip.got_hdr = TRUE;
-               len -= 10;
-               in += 10;
+               inptr += 10;
+               left -= 10;
                
                priv->state.unzip.is_valid = (priv->hdr.v.id1 == 31 &&
                                              priv->hdr.v.id2 == 139 &&
@@ -328,81 +336,87 @@ gunzip_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
        
        if (priv->hdr.v.flg & GZIP_FLAG_FEXTRA) {
                if (!priv->state.unzip.got_xlen) {
-                       if (len < 2) {
-                               g_mime_filter_backup (filter, in, len);
+                       if (left < 2) {
+                               /* not enough data to get the xlen field */
+                               g_mime_filter_backup (filter, inptr, left);
                                return;
                        }
                        
-                       memcpy (&val, in, 2);
+                       memcpy (&val, inptr, 2);
                        priv->state.unzip.xlen = GUINT16_FROM_LE (val);
                        priv->state.unzip.got_xlen = TRUE;
-                       len -= 2;
-                       in += 2;
+                       inptr += 2;
+                       left -= 2;
                }
                
                if (priv->state.unzip.xlen_nread < priv->state.unzip.xlen) {
                        need = priv->state.unzip.xlen - priv->state.unzip.xlen_nread;
                        
-                       if (need < len) {
-                               priv->state.unzip.xlen_nread += need;
-                               len -= need;
-                               in += need;
-                       } else {
-                               priv->state.unzip.xlen_nread += len;
+                       if (need >= left) {
+                               priv->state.unzip.xlen_nread += left;
                                return;
                        }
+                       
+                       priv->state.unzip.xlen_nread += need;
+                       inptr += need;
+                       left -= need;
                }
        }
        
        if ((priv->hdr.v.flg & GZIP_FLAG_FNAME) && !priv->state.unzip.got_fname) {
-               while (*in && len > 0) {
-                       len--;
-                       in++;
-               }
+               const char *start = inptr;
                
-               if (*in == '\0' && len > 0) {
-                       priv->state.unzip.got_fname = TRUE;
-                       len--;
-                       in++;
-               } else {
+               while (inptr < inend && *inptr)
+                       inptr++;
+               
+               if (inptr == inend) {
+                       g_mime_filter_backup (filter, start, left);
                        return;
                }
+               
+               priv->state.unzip.got_fname = TRUE;
+               inptr++;
+               
+               left -= (inptr - start);
        }
        
        if ((priv->hdr.v.flg & GZIP_FLAG_FCOMMENT) && !priv->state.unzip.got_fcomment) {
-               while (*in && len > 0) {
-                       len--;
-                       in++;
-               }
+               const char *start = inptr;
                
-               if (*in == '\0' && len > 0) {
-                       priv->state.unzip.got_fcomment = TRUE;
-                       len--;
-                       in++;
-               } else {
+               while (inptr < inend && *inptr)
+                       inptr++;
+               
+               if (inptr == inend) {
+                       g_mime_filter_backup (filter, start, left);
                        return;
                }
+               
+               priv->state.unzip.got_fcomment = TRUE;
+               inptr++;
+               
+               left -= (inptr - start);
        }
        
        if ((priv->hdr.v.flg & GZIP_FLAG_FHCRC) && !priv->state.unzip.got_crc16) {
-               if (len < 2) {
-                       g_mime_filter_backup (filter, in, len);
+               if (left < 2) {
+                       g_mime_filter_backup (filter, inptr, left);
                        return;
                }
                
-               memcpy (&val, in, 2);
+               memcpy (&val, inptr, 2);
                priv->state.unzip.crc16 = GUINT16_FROM_LE (val);
-               len -= 2;
-               in += 2;
+               priv->state.unzip.got_crc16 = TRUE;
+               inptr += 2;
+               left -= 2;
        }
        
-       if (len == 0)
+       if (left == 0 && flush != Z_FULL_FLUSH)
                return;
        
-       g_mime_filter_set_size (filter, (len * 2) + 12, FALSE);
+       g_mime_filter_set_size (filter, (left * 2) + 12, FALSE);
        
-       priv->stream->next_in = (unsigned char *) in;
-       priv->stream->avail_in = len - 8;
+       priv->stream->next_in = (unsigned char *) inptr;
+       priv->stream->avail_in = left;
        
        priv->stream->next_out = (unsigned char *) filter->outbuf;
        priv->stream->avail_out = filter->outsize;
@@ -410,29 +424,25 @@ gunzip_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
        do {
                /* FIXME: handle error cases? */
                /* Note: Z_BUF_ERROR is not really an error unless there is input available */
-               if ((retval = inflate (priv->stream, flush)) != Z_OK &&
-                   !(retval == Z_BUF_ERROR && !priv->stream->avail_in))
+               retval = inflate (priv->stream, flush);
+               
+               if (retval != Z_OK && !(retval == Z_BUF_ERROR && !priv->stream->avail_in)) {
                        w(fprintf (stderr, "gunzip: %d: %s\n", retval, priv->stream->msg));
+                       break;
+               }
+               
+               if (priv->stream->avail_in == 0)
+                       break;
                
                if (flush == Z_FULL_FLUSH) {
-                       size_t olen;
-                       
-                       if (priv->stream->avail_in == 0) {
-                               /* FIXME: extract & compare calculated crc32 and isize values? */
-                               break;
-                       }
+                       size_t olen = filter->outsize - priv->stream->avail_out;
                        
-                       olen = filter->outsize - priv->stream->avail_out;
                        g_mime_filter_set_size (filter, olen + (priv->stream->avail_in * 2) + 12, TRUE);
                        priv->stream->next_out = (unsigned char *) filter->outbuf + olen;
                        priv->stream->avail_out = filter->outsize - olen;
                } else {
-                       priv->stream->avail_in += 8;
-                       
-                       if (priv->stream->avail_in > 0)
-                               g_mime_filter_backup (filter, (char *) priv->stream->next_in,
-                                                     priv->stream->avail_in);
-                       
+                       g_mime_filter_backup (filter, (char *) priv->stream->next_in,
+                                             priv->stream->avail_in);
                        break;
                }
        } while (1);
diff --git a/tests/test-filters.c b/tests/test-filters.c
index 3d27458..fbdf161 100644
--- a/tests/test-filters.c
+++ b/tests/test-filters.c
@@ -170,7 +170,7 @@ test_gunzip (const char *datadir, const char *filename)
        
        filter = g_mime_filter_gzip_new (GMIME_FILTER_GZIP_MODE_UNZIP, 9);
        
-       pump_data_through_filter (filter, path, stream, FALSE, FALSE);
+       pump_data_through_filter (filter, path, stream, FALSE, TRUE);
        g_mime_filter_reset (filter);
        g_object_unref (stream);
        g_object_unref (filter);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]