1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-10-19 02:29:40 +00:00

Fix a buffer overflow in the "none" decompression handler that

occurred with large read-ahead requests.  This only affected
formats that incorrectly make large requests (ZIP did this until
recently) or with block sizes over 32k.
This commit is contained in:
Tim Kientzle 2005-02-13 23:29:54 +00:00
parent 59892d33e5
commit bceab447de
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=141850
2 changed files with 76 additions and 68 deletions

View File

@ -7,7 +7,7 @@
LIB= archive
VERSION= 1.02.002
VERSION= 1.02.006
ARCHIVE_API_FEATURE= 2
ARCHIVE_API_VERSION= 1
SHLIB_MAJOR= ${ARCHIVE_API_VERSION}

View File

@ -144,63 +144,83 @@ archive_decompressor_none_read_ahead(struct archive *a, const void **buff,
if (state->fatal)
return (-1);
/*
* Don't make special efforts to handle requests larger than
* the copy buffer.
*/
if (min > state->buffer_size)
min = state->buffer_size;
/* Keep reading until we have accumulated enough data. */
while (state->avail + state->client_avail < min) {
if (state->next > state->buffer &&
state->next + min > state->buffer + state->buffer_size &&
state->avail > 0) {
memmove(state->buffer, state->next, state->avail);
state->next = state->buffer;
}
if (state->client_avail > 0) {
memcpy(state->next + state->avail, state->client_next,
state->client_avail);
state->client_next += state->client_avail;
state->avail += state->client_avail;
state->client_avail = 0;
}
/*
* It seems to me that const void ** and const char **
* should be compatible, but they aren't, hence the cast.
*/
bytes_read = (a->client_reader)(a, a->client_data,
(const void **)&state->client_buff);
if (bytes_read < 0) { /* Read error. */
state->client_total = state->client_avail = 0;
state->client_next = state->client_buff = NULL;
state->fatal = 1;
return (-1);
}
if (bytes_read == 0) { /* End-of-file. */
state->client_total = state->client_avail = 0;
state->client_next = state->client_buff = NULL;
state->end_of_file = 1;
break;
}
a->raw_position += bytes_read;
state->client_total = bytes_read;
state->client_avail = state->client_total;
state->client_next = state->client_buff;
}
/* Common case: If client buffer suffices, use that. */
if (state->avail == 0) {
/*
* Try to satisfy the request directly from the client
* buffer. We can do this if all of the data in the copy
* buffer was copied from the current client buffer. This
* also covers the case where the copy buffer is empty and
* the client buffer has all the data we need.
*/
if (state->client_total >= state->client_avail + state->avail
&& state->client_avail + state->avail >= min) {
state->client_avail += state->avail;
state->client_next -= state->avail;
state->avail = 0;
state->next = state->buffer;
*buff = state->client_next;
return (state->client_avail);
}
/* Add in bytes from client buffer as necessary to meet the minimum. */
if (min > state->avail + state->client_avail)
min = state->avail + state->client_avail;
if (state->avail < min) {
memcpy(state->next + state->avail, state->client_next,
min - state->avail);
state->client_next += min - state->avail;
state->client_avail -= min - state->avail;
state->avail = min;
/*
* If we can't use client buffer, we'll have to use copy buffer.
*/
/* Move data forward in copy buffer if necessary. */
if (state->next > state->buffer &&
state->next + min > state->buffer + state->buffer_size) {
if (state->avail > 0)
memmove(state->buffer, state->next, state->avail);
state->next = state->buffer;
}
/* Collect data in copy buffer to fulfill request. */
while (state->avail < min) {
/* Copy data from client buffer to our copy buffer. */
if (state->client_avail > 0) {
/* First estimate: copy to fill rest of buffer. */
size_t tocopy = (state->buffer + state->buffer_size)
- (state->next + state->avail);
/* Don't copy more than is available. */
if (tocopy > state->client_avail)
tocopy = state->client_avail;
memcpy(state->next + state->avail, state->client_next,
tocopy);
state->client_next += tocopy;
state->client_avail -= tocopy;
state->avail += tocopy;
} else {
/* There is no more client data: fetch more. */
/*
* It seems to me that const void ** and const
* char ** should be compatible, but they
* aren't, hence the cast.
*/
bytes_read = (a->client_reader)(a, a->client_data,
(const void **)&state->client_buff);
if (bytes_read < 0) { /* Read error. */
state->client_total = state->client_avail = 0;
state->client_next = state->client_buff = NULL;
state->fatal = 1;
return (-1);
}
if (bytes_read == 0) { /* End-of-file. */
state->client_total = state->client_avail = 0;
state->client_next = state->client_buff = NULL;
state->end_of_file = 1;
break;
}
a->raw_position += bytes_read;
state->client_total = bytes_read;
state->client_avail = state->client_total;
state->client_next = state->client_buff;
}
}
*buff = state->next;
@ -208,11 +228,9 @@ archive_decompressor_none_read_ahead(struct archive *a, const void **buff,
}
/*
* Mark the appropriate data as used. Note that the request here could
* be much smaller than the size of the previous read_ahead request, but
* typically it won't be. I make an attempt to go back to reading straight
* from the client buffer in case some end-of-block alignment mismatch forced
* me to combine writes above.
* Mark the appropriate data as used. Note that the request here will
* often be much smaller than the size of the previous read_ahead
* request.
*/
static ssize_t
archive_decompressor_none_read_consume(struct archive *a, size_t request)
@ -221,21 +239,11 @@ archive_decompressor_none_read_consume(struct archive *a, size_t request)
state = a->compression_data;
if (state->avail > 0) {
/* Read came from copy buffer. */
state->next += request;
state->avail -= request;
/*
* Rollback state->client_next if we can so that future
* reads come straight from the client buffer and we
* avoid copying more data into our buffer.
*/
if (state->avail <=
(size_t)(state->client_next - state->client_buff)) {
state->client_next -= state->avail;
state->client_avail += state->avail;
state->avail = 0;
state->next = state->buffer;
}
} else {
/* Read came from client buffer. */
state->client_next += request;
state->client_avail -= request;
}