--- src/ChangeLog.orig 2003-12-29 16:18:03.000000000 -0500 +++ src/ChangeLog 2003-12-29 16:17:29.000000000 -0500 @@ -1,2 +1,6 @@ +2003-12-29 Jeff Downs + + * zlib.c: Fix heap corruption issues in zlib library interface. + 2002-04-17 Derek Price --- src/zlib.c.orig 2003-12-11 09:07:19.000000000 -0500 +++ src/zlib.c 2003-12-29 15:10:00.000000000 -0500 @@ -55,4 +55,6 @@ static int compress_buffer_shutdown_output PROTO((struct buffer *)); +static size_t cvs_strnlen PROTO((const char *, const size_t)); + /* Report an error from one of the zlib functions. */ @@ -451,4 +453,9 @@ unsigned long crc; + if (size < 10) + { + error (0, 0, "gzipped data too small - lacks complete header"); + return 1; + } if (buf[0] != 31 || buf[1] != 139) { @@ -463,14 +470,62 @@ /* Skip over the fixed header, and then skip any of the variable-length - fields. */ + fields. As we skip each field, we keep pos <= size. The checks + on positions and lengths are really checks for malformed or + incomplete gzip data */ pos = 10; + /* FEXTRA */ if (buf[3] & 4) + { + if (pos + 2 >= size) + { + error (0, 0, "%s lacks proper gzip XLEN field", fullname); + return 1; + } pos += buf[pos] + (buf[pos + 1] << 8) + 2; + if (pos > size) + { + error (0, 0, "%s lacks proper gzip \"extra field\"", fullname); + return 1; + } + + } + /* FNAME */ if (buf[3] & 8) - pos += strlen ((char *) buf + pos) + 1; + { + size_t skip = cvs_strnlen((char *) buf + pos, size - pos); + if ((pos += skip + 1) > size) + { + error (0, 0, "%s has bad gzip filename field", fullname); + return 1; + } + } + /* FCOMMENT */ if (buf[3] & 16) - pos += strlen ((char *) buf + pos) + 1; + { + size_t skip = cvs_strnlen((char *) buf + pos, size - pos); + if ((pos += skip + 1) > size) + { + error (0, 0, "%s has bad gzip comment field", fullname); + return 1; + } + } + /* FHCRC */ if (buf[3] & 2) + { pos += 2; + if (pos > size) + { + error (0, 0, "%s has bad gzip CRC16 field", fullname); + return 1; + } + } + + /* There could be no data to decompress - check and short circuit + Don't have to check for > size since above checks preclude it */ + if (pos == size) + { + error (0, 0, "gzip data incomplete for %s (no data)", fullname); + return 1; + } memset (&zstr, 0, sizeof zstr); @@ -514,8 +569,18 @@ compress_error (0, zstatus, &zstr, fullname); - if (crc != (buf[zstr.total_in + 10] - + (buf[zstr.total_in + 11] << 8) - + (buf[zstr.total_in + 12] << 16) - + (buf[zstr.total_in + 13] << 24))) + /* Check that there is still 8 trailer bytes remaining + (CRC32 and ISIZE). Check total decomp. data, plus header len (pos) + against input buffer total size. */ + if (size - (zstr.total_in + pos) != 8) + { + error (0, 0, "gzip data incomplete for %s (no trailer)", fullname); + return 1; + } + + pos += zstr.total_in; + if (crc != (buf[pos] + + (buf[pos + 1] << 8) + + (buf[pos + 2] << 16) + + (buf[pos + 3] << 24))) { error (0, 0, "CRC error uncompressing %s", fullname); @@ -523,8 +588,8 @@ } - if (zstr.total_out != (buf[zstr.total_in + 14] - + (buf[zstr.total_in + 15] << 8) - + (buf[zstr.total_in + 16] << 16) - + (buf[zstr.total_in + 17] << 24))) + if (zstr.total_out != (buf[pos + 4] + + (buf[pos + 5] << 8) + + (buf[pos + 6] << 16) + + (buf[pos + 7] << 24))) { error (0, 0, "invalid length uncompressing %s", fullname); @@ -535,4 +600,24 @@ } +/* A strlen-like function, but with a maximum bounds in which to search + for the terminating '\0' character. Computes the number of characters in + the string pointed to by s, not including the terminating '\0' character. + If n bytes are searched for the terminating '\0' character and it is not + found, n is returned. Otherwise the length, as decribed above, is + returned. The length will be greater than or equal to 0 and less than + n. Same idea as GNU extension function 'strnlen'.*/ +static size_t +cvs_strnlen (s, n) + const char *s; + const size_t n; +{ + size_t i; + + for (i = 0; i < n && s[i] != '\0'; ++i) + ; + + return i; +} + /* Read all of FD and put the gzipped data (RFC1952/RFC1951) into *BUF, replacing previous contents of *BUF. *BUF is malloc'd and *SIZE is @@ -588,5 +673,7 @@ return 1; } - zstr.avail_out = *size; + + /* Adjust for 10-byte output header (filled in above) */ + zstr.avail_out = *size - 10; zstr.next_out = *buf + 10; @@ -642,4 +729,27 @@ } done: + /* + * Need to add the CRC information (8 bytes) + * to the end of the gzip'd output. + * Ensure there is enough space in the output buffer + * to do so. + */ + { + size_t used = zstr.total_out + 10; + /* remainder is always >= 0 */ + size_t remainder = *size - used; + if (remainder < 8) { + unsigned char *newbuf; + + *size += 8 - remainder; + newbuf = realloc (*buf, *size); + if (newbuf == NULL) + { + error (0, 0, "out of memory"); + return 1; + } + *buf = newbuf; + } + } *(*buf + zstr.total_out + 10) = crc & 0xff; *(*buf + zstr.total_out + 11) = (crc >> 8) & 0xff;