Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Server hang using a malformed gzip file #4602

Closed
michaelrsweet opened this Issue Mar 1, 2015 · 4 comments

Comments

Projects
None yet
1 participant
Collaborator

michaelrsweet commented Mar 1, 2015

Version: 2.0-current
CUPS.org User: pdewacht

The CUPS server can get stuck in an infinite loop when a user queues a malformed gzip file. When this happens the CUPS server will be unable to service any further requests. (I'm running CUPS using systemd's socket activation, which might perhaps be relevant.)

To reproduce: lp < gziphang.dat

Note: it's important to feed the file via stdin, otherwise it's the client that gets stuck (client and server use the same code).

Found by running afl-fuzz against the 'testmime' program.

Collaborator

michaelrsweet commented Mar 30, 2015

CUPS.org User: mike

Update: have been investigating since Friday and have a tentative patch I am testing now...

Collaborator

michaelrsweet commented Mar 30, 2015

CUPS.org User: mike

OK, patch attached which fixes this issue as well as a bug in the gzip CRC checking code. Holding full disclosure until release...

Collaborator

michaelrsweet commented Jun 8, 2015

CUPS.org User: mike

Fixed in Subversion repository.

Collaborator

michaelrsweet commented Jun 8, 2015

"str4602.patch":

Index: cups/file.c

--- cups/file.c (revision 12567)
+++ cups/file.c (working copy)
@@ -8,7 +8,7 @@

  • our own file functions allows us to provide transparent support of
  • gzip'd print files, PPD files, etc.
    *
    • * Copyright 2007-2014 by Apple Inc.
    • * Copyright 2007-2015 by Apple Inc.
  • Copyright 1997-2007 by Easy Software Products, all rights reserved.
    *
  • These coded instructions, statements, and computer programs are the
    @@ -639,6 +639,8 @@
    • Range check input...
      */
  • DEBUG_printf(("4cupsFileGetChar(fp=%p)", fp));

if (!fp || (fp->mode != 'r' && fp->mode != 's'))
{
DEBUG_puts("5cupsFileGetChar: Bad arguments!");
@@ -649,8 +651,10 @@

  • If the input buffer is empty, try to read more data...
    */
  • DEBUG_printf(("5cupsFileGetChar: fp->eof=%d, fp->ptr=%p, fp->end=%p", fp->eof, fp->ptr, fp->end));

if (fp->ptr >= fp->end)

  • if (cups_fill(fp) < 0)
  • if (cups_fill(fp) <= 0)
    {
    DEBUG_puts("5cupsFileGetChar: Unable to fill buffer!");
    return (-1);
    @@ -1284,7 +1288,7 @@
    */

if (fp->ptr >= fp->end)

  • if (cups_fill(fp) < 0)
  • if (cups_fill(fp) <= 0)
    return (-1);

/*
@@ -1779,7 +1783,7 @@

  • Preload a buffer to determine whether the file is compressed...
    */

  • if (cups_fill(fp) < 0)

  • if (cups_fill(fp) <= 0)
    return (-1);
    }
    #endif /* HAVE_LIBZ */
    @@ -2195,6 +2199,8 @@
    DEBUG_printf(("9cups_fill: cups_read() returned " CUPS_LLFMT,
    CUPS_LLCAST bytes));

  •    fp->eof = 1;
    

    return (-1);
    }

@@ -2234,6 +2240,11 @@
* Can't read from file!
*/

  • DEBUG_puts("9cups_fill: Extra gzip header data missing, returning -1.");
    
  •      fp->eof = 1;
    
  • errno   = EIO;
    
    • return (-1);
      }

@@ -2246,6 +2257,11 @@
* Can't read from file!
*/

  • DEBUG_puts("9cups_fill: Extra gzip header data does not fit in initial buffer, returning -1.");
    
  •      fp->eof = 1;
    
  • errno   = EIO;
    
    • return (-1);
      }
      }
      @@ -2267,6 +2283,11 @@
    • Can't read from file!
      */
  • DEBUG_puts("9cups_fill: Original filename in gzip header data does not fit in initial buffer, returning -1.");
    
  •      fp->eof = 1;
    
  • errno   = EIO;
    
    • return (-1);
      }
      }
      @@ -2288,6 +2309,11 @@
    • Can't read from file!
      */
  • DEBUG_puts("9cups_fill: Comment in gzip header data does not fit in initial buffer, returning -1.");
    
  •      fp->eof = 1;
    
  • errno   = EIO;
    
    • return (-1);
      }
      }
      @@ -2306,6 +2332,11 @@
    • Can't read from file!
      */
  • DEBUG_puts("9cups_fill: Header CRC in gzip header data does not fit in initial buffer, returning -1.");
    
  •      fp->eof = 1;
    
  • errno   = EIO;
    
    • return (-1);
      }
      }
      @@ -2330,8 +2361,15 @@
      fp->stream.avail_out = 0;
      fp->crc = crc32(0L, Z_NULL, 0);
  •  if (inflateInit2(&(fp->stream), -15) != Z_OK)
    
  •  if ((status = inflateInit2(&(fp->stream), -15)) != Z_OK)
    
  •  {
    
  • DEBUG_printf(("9cups_fill: inflateInit2 returned %d, returning -1.", status));

  •    fp->eof = 1;
    
  •    errno   = EIO;
    

    return (-1);

  •  }
    

    fp->compressed = 1;
    }
    @@ -2343,8 +2381,12 @@
    */

    if (fp->eof)

  • return (-1);

  •  {
    
  •    DEBUG_puts("9cups_fill: EOF, returning 0.");
    
  • return (0);

  •  }
    
    • /*
    • Fill the decompression buffer as needed...
      */
      @@ -2352,8 +2394,14 @@
      if (fp->stream.avail_in == 0)
      {
      if ((bytes = cups_read(fp, (char *)fp->cbuf, sizeof(fp->cbuf))) <= 0)
  •      return (-1);
    
  • {

  • DEBUG_printf(("9cups_fill: cups_read error, returning %d.", (int)bytes));
    
  • fp->eof = 1;
    
  •      return (bytes);
    
  • }

fp->stream.next_in = fp->cbuf;
fp->stream.avail_in = (uInt)bytes;
}
@@ -2379,44 +2427,71 @@

unsigned char trailer[8]; /* Trailer bytes /
uLong tcrc; /
Trailer CRC */

  • ssize_t tbytes = 0; /* Number of bytes */

  • if (read(fp->fd, trailer, sizeof(trailer)) < (ssize_t)sizeof(trailer))
  • if (fp->stream.avail_in > 0)
    {
  • /*
  •      \* Can't get it, so mark end-of-file...
    
  • */
    
  • if (fp->stream.avail_in > sizeof(trailer))
    
  •   tbytes = (ssize_t)sizeof(trailer);
    
  • else
    
  •   tbytes = (ssize_t)fp->stream.avail_in;
    
  •      fp->eof = 1;
    
  • memcpy(trailer, fp->stream.next_in, tbytes);
    
  • fp->stream.next_in  += tbytes;
    
  • fp->stream.avail_in -= (size_t)tbytes;
    
    }
  • else
  •    if (tbytes < (ssize_t)sizeof(trailer))
    
    {
  • tcrc = ((((((uLong)trailer[3] << 8) | (uLong)trailer[2]) << 8) |
    

- (uLong)trailer[1]) << 8) | (uLong)trailer[0];

  • if (tcrc != fp->crc)
    
  • if (read(fp->fd, trailer + tbytes, sizeof(trailer) - (size_t)tbytes) < ((ssize_t)sizeof(trailer) - tbytes))
    

    {
    /*

  •        \* Bad CRC, mark end-of-file...
    
  •   * Can't get it, so mark end-of-file...
    */
    
  •        DEBUG_printf(("9cups_fill: tcrc=%08x != fp->crc=%08x",
    
  •                 (unsigned int)tcrc, (unsigned int)fp->crc));
    
  •   DEBUG_puts("9cups_fill: Unable to read gzip CRC trailer, returning -1.");
    
    fp->eof = 1;
    
  •   errno   = EIO;
    
    return (-1);
    

    }

  • }

  • tcrc = ((((((uLong)trailer[3] << 8) | (uLong)trailer[2]) << 8) |

  •   (uLong)trailer[1]) << 8) | (uLong)trailer[0];
    
  • if (tcrc != fp->crc)

  • {
    /*

  • \* Otherwise, reset the compressed flag so that we re-read the
    
  • \* file header...
    
  • * Bad CRC, mark end-of-file...
    

    */

  • fp->compressed = 0;
    
  • DEBUG_printf(("9cups_fill: tcrc=%08x != fp->crc=%08x, returning -1.", (unsigned int)tcrc, (unsigned int)fp->crc));
    
  • fp->eof = 1;
    
  • errno   = EIO;
    
  • return (-1);
    

    }
    +

  •   /*
    
  • * Otherwise, reset the compressed flag so that we re-read the

  • * file header...

  • */

  • fp->compressed = 0;
    }

  •  else if (status < Z_OK)
    
  •  {
    
  • DEBUG_printf(("9cups_fill: inflate returned %d, returning -1.", status));

  •    fp->eof = 1;
    
  •    errno   = EIO;
    
  • return (-1);

  •  }
    
    • bytes = (ssize_t)sizeof(fp->buf) - (ssize_t)fp->stream.avail_out;

      /*
      @@ -2427,7 +2502,10 @@
      fp->end = fp->buf + bytes;

      if (bytes)

  •  {
    
  •    DEBUG_printf(("9cups_fill: Returning %d.", (int)bytes));
    

    return (bytes);

  •  }
    

    }
    }
    #endif /* HAVE_LIBZ */
    @@ -2445,18 +2523,20 @@
    fp->eof = 1;
    fp->ptr = fp->buf;
    fp->end = fp->buf;

  • }

  • else

  • {

  • /*

  • * Return the bytes we read...

  • */

  • return (-1);

  • fp->eof = 0;

  • fp->ptr = fp->buf;

  • fp->end = fp->buf + bytes;
    }

  • /*

  • * Return the bytes we read...

  • */

  • DEBUG_printf(("9cups_fill: Not gzip, returning %d.", (int)bytes));

  • fp->eof = 0;

  • fp->ptr = fp->buf;

- fp->end = fp->buf + bytes;

return (bytes);
}

Index: cups/testfile.c

--- cups/testfile.c (revision 12567)
+++ cups/testfile.c (working copy)
@@ -3,7 +3,7 @@
*

  • File test program for CUPS.
    *
    • * Copyright 2007-2014 by Apple Inc.
    • * Copyright 2007-2015 by Apple Inc.
  • Copyright 1997-2007 by Easy Software Products.
    *
  • These coded instructions, statements, and computer programs are the
    @@ -205,14 +205,14 @@
    • Cat the filename on the command-line...
      */
  • char line[1024]; /* Line from file */
  • char line[8192]; /* Line from file */

if ((fp = cupsFileOpen(argv[1], "r")) == NULL)
{
perror(argv[1]);
status = 1;
}

  • else
  • else if (argc == 2)
    {
    status = 0;

@@ -224,6 +224,21 @@

   cupsFileClose(fp);
 }
  • else
  • {
  •  status = 0;
    
  •  ssize_t bytes;
    
  •  while ((bytes = cupsFileRead(fp, line, sizeof(line))) > 0)
    
  •    printf("%s: %d bytes\n", argv[1], (int)bytes);
    
  •  if (cupsFileEOF(fp))
    
  •    printf("%s: EOF\n", argv[1]);
    
  •  else
    
  •    perror(argv[1]);
    
  •  cupsFileClose(fp);
    
  • }
    }

return (status);
@@ -798,7 +817,8 @@

  • Remove the test file...
    */
  • unlink(compression ? "testfile.dat.gz" : "testfile.dat");
  • if (!status)
  • unlink(compression ? "testfile.dat.gz" : "testfile.dat");

/*

  • Return the test status...
    Index: scheduler/type.c

    --- scheduler/type.c (revision 12567)
    +++ scheduler/type.c (working copy)
    @@ -3,7 +3,7 @@
    *
  • MIME typing routines for CUPS.
    *
  • * Copyright 2007-2014 by Apple Inc.
    • Copyright 2007-2015 by Apple Inc.
    • Copyright 1997-2006 by Easy Software Products, all rights reserved.
      *
    • These coded instructions, statements, and computer programs are the
      @@ -613,9 +613,24 @@
      return (NULL);
      }
  • fb.offset = -1;
  • fb.length = 0;
  • /*
  • * Then preload the first MIME_MAX_BUFFER bytes of the file into the file
  • * buffer, returning an error if we can't read anything...
  • */
  • fb.offset = 0;
  • fb.length = (int)cupsFileRead(fb.fp, (char *)fb.buffer, MIME_MAX_BUFFER);
  • if (fb.length <= 0)
  • {
  • DEBUG_printf(("1mimeFileType: Unable to read from "%s": %s", pathname, strerror(errno)));
  • DEBUG_puts("1mimeFileType: Returning NULL.");
  • cupsFileClose(fb.fp);
  • return (NULL);
  • }

/*

  • Figure out the base filename (without directory portion)...
    */
    @@ -780,6 +795,8 @@
    fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
    sizeof(fb->buffer));
    fb->offset = rules->offset;
    +

  •   DEBUG_printf(("4mime_check_rules: MIME_MAGIC_ASCII fb->length=%d", fb->length));
    

    }

      /*
    

    @@ -822,6 +839,8 @@
    fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
    sizeof(fb->buffer));
    fb->offset = rules->offset;
    +

  •   DEBUG_printf(("4mime_check_rules: MIME_MAGIC_PRINTABLE fb->length=%d", fb->length));
    

    }

      /*
    

    @@ -870,6 +889,8 @@
    sizeof(fb->buffer));
    fb->offset = rules->offset;

  •   DEBUG_printf(("4mime_check_rules: MIME_MAGIC_REGEX fb->length=%d", fb->length));
    
    •    DEBUG_printf(("5mime_check_rules: loaded %d byte fb->buffer at %d, starts "
                  "with \"%c%c%c%c\".",
                  fb->length, fb->offset, fb->buffer[0], fb->buffer[1],
      

    @@ -914,6 +935,8 @@
    sizeof(fb->buffer));
    fb->offset = rules->offset;

  •   DEBUG_printf(("4mime_check_rules: MIME_MAGIC_STRING fb->length=%d", fb->length));
    
    •    DEBUG_printf(("5mime_check_rules: loaded %d byte fb->buffer at %d, starts "
                  "with \"%c%c%c%c\".",
                  fb->length, fb->offset, fb->buffer[0], fb->buffer[1],
      

    @@ -948,6 +971,8 @@
    fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
    sizeof(fb->buffer));
    fb->offset = rules->offset;
    +

  •   DEBUG_printf(("4mime_check_rules: MIME_MAGIC_ISTRING fb->length=%d", fb->length));
    

    }

      /*
    

    @@ -976,6 +1001,8 @@
    fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
    sizeof(fb->buffer));
    fb->offset = rules->offset;
    +

  •   DEBUG_printf(("4mime_check_rules: MIME_MAGIC_CHAR fb->length=%d", fb->length));
    

    }

    /*
    @@ -1006,6 +1033,8 @@
    fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
    sizeof(fb->buffer));
    fb->offset = rules->offset;
    +

  •   DEBUG_printf(("4mime_check_rules: MIME_MAGIC_SHORT fb->length=%d", fb->length));
    

    }

    /*
    @@ -1039,6 +1068,8 @@
    fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
    sizeof(fb->buffer));
    fb->offset = rules->offset;
    +

  •   DEBUG_printf(("4mime_check_rules: MIME_MAGIC_INT fb->length=%d", fb->length));
    

    }

    /*
    @@ -1080,6 +1111,8 @@
    fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
    sizeof(fb->buffer));
    fb->offset = rules->offset;
    +

  •   DEBUG_printf(("4mime_check_rules: MIME_MAGIC_CONTAINS fb->length=%d", fb->length));
    

    }

      /*
    

@michaelrsweet michaelrsweet added this to the Stable milestone Mar 17, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment