summaryrefslogtreecommitdiff
path: root/contrib/cvs/src/zlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/zlib.c')
-rw-r--r--contrib/cvs/src/zlib.c180
1 files changed, 143 insertions, 37 deletions
diff --git a/contrib/cvs/src/zlib.c b/contrib/cvs/src/zlib.c
index 00b36822dd406..32c95e50d707a 100644
--- a/contrib/cvs/src/zlib.c
+++ b/contrib/cvs/src/zlib.c
@@ -431,6 +431,20 @@ compress_buffer_shutdown_output (buf)
/* Here is our librarified gzip implementation. It is very minimal
but attempts to be RFC1952 compliant. */
+/* GZIP ID byte values */
+#define GZIP_ID1 31
+#define GZIP_ID2 139
+
+/* Compression methods */
+#define GZIP_CDEFLATE 8
+
+/* Flags */
+#define GZIP_FTEXT 1
+#define GZIP_FHCRC 2
+#define GZIP_FEXTRA 4
+#define GZIP_FNAME 8
+#define GZIP_FCOMMENT 16
+
/* BUF should contain SIZE bytes of gzipped data (RFC1952/RFC1951).
We are to uncompress the data and write the result to the file
descriptor FD. If something goes wrong, give a nonfatal error message
@@ -450,28 +464,78 @@ gunzip_and_write (fd, fullname, buf, size)
unsigned char outbuf[32768];
unsigned long crc;
- if (buf[0] != 31 || buf[1] != 139)
+ if (size < 10)
+ {
+ error (0, 0, "gzipped data too small - lacks complete header");
+ return 1;
+ }
+ if (buf[0] != GZIP_ID1 || buf[1] != GZIP_ID2)
{
error (0, 0, "gzipped data does not start with gzip identification");
return 1;
}
- if (buf[2] != 8)
+ if (buf[2] != GZIP_CDEFLATE)
{
error (0, 0, "only the deflate compression method is supported");
return 1;
}
/* 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;
- if (buf[3] & 4)
+ if (buf[3] & GZIP_FEXTRA)
+ {
+ 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 (buf[3] & 8)
- pos += strlen ((char *) buf + pos) + 1;
- if (buf[3] & 16)
- pos += strlen ((char *) buf + pos) + 1;
- if (buf[3] & 2)
+ if (pos > size)
+ {
+ error (0, 0, "%s lacks proper gzip \"extra field\"", fullname);
+ return 1;
+ }
+
+ }
+ if (buf[3] & GZIP_FNAME)
+ {
+ unsigned char *p = memchr(buf + pos, '\0', size - pos);
+ if (p == NULL)
+ {
+ error (0, 0, "%s has bad gzip filename field", fullname);
+ return 1;
+ }
+ pos = p - buf + 1;
+ }
+ if (buf[3] & GZIP_FCOMMENT)
+ {
+ unsigned char *p = memchr(buf + pos, '\0', size - pos);
+ if (p == NULL)
+ {
+ error (0, 0, "%s has bad gzip comment field", fullname);
+ return 1;
+ }
+ pos = p - buf + 1;
+ }
+ if (buf[3] & GZIP_FHCRC)
+ {
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. */
+ if (pos >= size)
+ {
+ error (0, 0, "gzip data incomplete for %s (no data)", fullname);
+ return 1;
+ }
memset (&zstr, 0, sizeof zstr);
/* Passing a negative argument tells zlib not to look for a zlib
@@ -513,19 +577,29 @@ gunzip_and_write (fd, fullname, buf, size)
if (zstatus != Z_OK)
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. */
+ pos += zstr.total_in;
+ if (size - pos != 8)
+ {
+ error (0, 0, "gzip data incomplete for %s (no trailer)", fullname);
+ return 1;
+ }
+
+ if (crc != ((unsigned long)buf[pos]
+ + ((unsigned long)buf[pos + 1] << 8)
+ + ((unsigned long)buf[pos + 2] << 16)
+ + ((unsigned long)buf[pos + 3] << 24)))
{
error (0, 0, "CRC error uncompressing %s", fullname);
return 1;
}
- 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 != ((unsigned long)buf[pos + 4]
+ + ((unsigned long)buf[pos + 5] << 8)
+ + ((unsigned long)buf[pos + 6] << 16)
+ + ((unsigned long)buf[pos + 7] << 24)))
{
error (0, 0, "invalid length uncompressing %s", fullname);
return 1;
@@ -544,7 +618,7 @@ gunzip_and_write (fd, fullname, buf, size)
int
read_and_gzip (fd, fullname, buf, size, len, level)
int fd;
- char *fullname;
+ const char *fullname;
unsigned char **buf;
size_t *size;
size_t *len;
@@ -569,9 +643,9 @@ read_and_gzip (fd, fullname, buf, size, len, level)
}
*buf = newbuf;
}
- (*buf)[0] = 31;
- (*buf)[1] = 139;
- (*buf)[2] = 8;
+ (*buf)[0] = GZIP_ID1;
+ (*buf)[1] = GZIP_ID2;
+ (*buf)[2] = GZIP_CDEFLATE;
(*buf)[3] = 0;
(*buf)[4] = (*buf)[5] = (*buf)[6] = (*buf)[7] = 0;
/* Could set this based on level, but why bother? */
@@ -587,7 +661,10 @@ read_and_gzip (fd, fullname, buf, size, len, level)
compress_error (0, zstatus, &zstr, fullname);
return 1;
}
- zstr.avail_out = *size;
+
+ /* Adjust for 10-byte output header (filled in above) */
+ zstr.total_out = 10;
+ zstr.avail_out = *size - 10;
zstr.next_out = *buf + 10;
while (1)
@@ -609,8 +686,6 @@ read_and_gzip (fd, fullname, buf, size, len, level)
do
{
- size_t offset;
-
/* I don't see this documented anywhere, but deflate seems
to tend to dump core sometimes if we pass it Z_FINISH and
a small (e.g. 2147 byte) avail_out. So we insist on at
@@ -620,7 +695,8 @@ read_and_gzip (fd, fullname, buf, size, len, level)
{
unsigned char *newbuf;
- offset = zstr.next_out - *buf;
+ assert(zstr.avail_out + zstr.total_out == *size);
+ assert(zstr.next_out == *buf + zstr.total_out);
*size *= 2;
newbuf = xrealloc (*buf, *size);
if (newbuf == NULL)
@@ -629,8 +705,10 @@ read_and_gzip (fd, fullname, buf, size, len, level)
return 1;
}
*buf = newbuf;
- zstr.next_out = *buf + offset;
- zstr.avail_out = *size - offset;
+ zstr.next_out = *buf + zstr.total_out;
+ zstr.avail_out = *size - zstr.total_out;
+ assert(zstr.avail_out + zstr.total_out == *size);
+ assert(zstr.next_out == *buf + zstr.total_out);
}
zstatus = deflate (&zstr, finish ? Z_FINISH : 0);
@@ -641,17 +719,45 @@ read_and_gzip (fd, fullname, buf, size, len, level)
} while (zstr.avail_out == 0);
}
done:
- *(*buf + zstr.total_out + 10) = crc & 0xff;
- *(*buf + zstr.total_out + 11) = (crc >> 8) & 0xff;
- *(*buf + zstr.total_out + 12) = (crc >> 16) & 0xff;
- *(*buf + zstr.total_out + 13) = (crc >> 24) & 0xff;
-
- *(*buf + zstr.total_out + 14) = zstr.total_in & 0xff;
- *(*buf + zstr.total_out + 15) = (zstr.total_in >> 8) & 0xff;
- *(*buf + zstr.total_out + 16) = (zstr.total_in >> 16) & 0xff;
- *(*buf + zstr.total_out + 17) = (zstr.total_in >> 24) & 0xff;
+ /* 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. */
+ if (zstr.avail_out < 8)
+ {
+ unsigned char *newbuf;
- *len = zstr.total_out + 18;
+ assert(zstr.avail_out + zstr.total_out == *size);
+ assert(zstr.next_out == *buf + zstr.total_out);
+ *size += 8 - zstr.avail_out;
+ newbuf = realloc (*buf, *size);
+ if (newbuf == NULL)
+ {
+ error (0, 0, "out of memory");
+ return 1;
+ }
+ *buf = newbuf;
+ zstr.next_out = *buf + zstr.total_out;
+ zstr.avail_out = *size - zstr.total_out;
+ assert(zstr.avail_out + zstr.total_out == *size);
+ assert(zstr.next_out == *buf + zstr.total_out);
+ }
+ *zstr.next_out++ = (unsigned char)(crc & 0xff);
+ *zstr.next_out++ = (unsigned char)((crc >> 8) & 0xff);
+ *zstr.next_out++ = (unsigned char)((crc >> 16) & 0xff);
+ *zstr.next_out++ = (unsigned char)((crc >> 24) & 0xff);
+
+ *zstr.next_out++ = (unsigned char)(zstr.total_in & 0xff);
+ *zstr.next_out++ = (unsigned char)((zstr.total_in >> 8) & 0xff);
+ *zstr.next_out++ = (unsigned char)((zstr.total_in >> 16) & 0xff);
+ *zstr.next_out++ = (unsigned char)((zstr.total_in >> 24) & 0xff);
+
+ zstr.total_out += 8;
+ zstr.avail_out -= 8;
+ assert(zstr.avail_out + zstr.total_out == *size);
+ assert(zstr.next_out == *buf + zstr.total_out);
+
+ *len = zstr.total_out;
zstatus = deflateEnd (&zstr);
if (zstatus != Z_OK)