isoreader:gzip: Avoid shallow copying z_stream objects

This prevents the internal state of the objects from becoming
inconsistent, which causes inflate() to fail with recent zlib versions
(1.2.9 and later).
This commit is contained in:
Jonathan Li 2017-02-14 18:05:38 +00:00
parent c218ef3970
commit 1ff6eec1e3
2 changed files with 43 additions and 43 deletions

View File

@ -429,8 +429,13 @@ int GzippedFileReader::_ReadSync(void* pBuffer, PX_off_t offset, uint bytesToRea
// move the state to the appropriate span because it will be faster than using the index // move the state to the appropriate span because it will be faster than using the index
int targetix = (extractOffset + res) / span; int targetix = (extractOffset + res) / span;
m_zstates[targetix].Kill(); m_zstates[targetix].Kill();
m_zstates[targetix] = m_zstates[spanix]; // We have elements for the entire file, and another one. // We have elements for the entire file, and another one.
m_zstates[spanix].state.isValid = 0; // Not killing because we need the state. m_zstates[targetix].state.in_offset = m_zstates[spanix].state.in_offset;
m_zstates[targetix].state.isValid = m_zstates[spanix].state.isValid;
m_zstates[targetix].state.out_offset = m_zstates[spanix].state.out_offset;
inflateCopy(&m_zstates[targetix].state.strm, &m_zstates[spanix].state.strm);
m_zstates[spanix].Kill();
} }
if (size <= GZFILE_READ_CHUNK_SIZE) if (size <= GZFILE_READ_CHUNK_SIZE)

View File

@ -344,33 +344,29 @@ static inline PX_off_t getInOffset(zstate *state) {
was generated. extract() may also return Z_ERRNO if there is an error on was generated. extract() may also return Z_ERRNO if there is an error on
reading or seeking the input file. */ reading or seeking the input file. */
local int extract(FILE *in, struct access *index, PX_off_t offset, local int extract(FILE *in, struct access *index, PX_off_t offset,
unsigned char *buf, int len, zstate *state = 0) unsigned char *buf, int len, zstate *state)
{ {
int ret, skip; int ret, skip;
z_stream strm;
struct point *here; struct point *here;
unsigned char input[CHUNK]; unsigned char input[CHUNK];
unsigned char discard[WINSIZE]; unsigned char discard[WINSIZE];
int isEnd = 0; int isEnd = 0;
/* proceed only if something reasonable to do */ /* proceed only if something reasonable to do */
if (len < 0) if (len < 0 || state == nullptr)
return 0; return 0;
if (state) { if (state->isValid && offset != state->out_offset) {
if (state->isValid && offset != state->out_offset) { // state doesn't match offset, free allocations before strm is overwritten
// state doesn't match offset, free allocations before strm is overwritten inflateEnd(&state->strm);
(void)inflateEnd(&state->strm); state->isValid = 0;
state->isValid = 0;
}
state->out_offset = offset;
} }
state->out_offset = offset;
if (state && state->isValid) { if (state->isValid) {
strm = state->strm;
state->isValid = 0; // we took control over strm. revalidate when/if we give it back state->isValid = 0; // we took control over strm. revalidate when/if we give it back
PX_fseeko(in, state->in_offset, SEEK_SET); PX_fseeko(in, state->in_offset, SEEK_SET);
strm.avail_in = 0; state->strm.avail_in = 0;
offset = 0; offset = 0;
skip = 1; skip = 1;
} else { } else {
@ -381,12 +377,12 @@ local int extract(FILE *in, struct access *index, PX_off_t offset,
here++; here++;
/* initialize file and inflate state to start there */ /* initialize file and inflate state to start there */
strm.zalloc = Z_NULL; state->strm.zalloc = Z_NULL;
strm.zfree = Z_NULL; state->strm.zfree = Z_NULL;
strm.opaque = Z_NULL; state->strm.opaque = Z_NULL;
strm.avail_in = 0; state->strm.avail_in = 0;
strm.next_in = Z_NULL; state->strm.next_in = Z_NULL;
ret = inflateInit2(&strm, -15); /* raw inflate */ ret = inflateInit2(&state->strm, -15); /* raw inflate */
if (ret != Z_OK) if (ret != Z_OK)
return ret; return ret;
ret = PX_fseeko(in, here->in - (here->bits ? 1 : 0), SEEK_SET); ret = PX_fseeko(in, here->in - (here->bits ? 1 : 0), SEEK_SET);
@ -398,59 +394,59 @@ local int extract(FILE *in, struct access *index, PX_off_t offset,
ret = ferror(in) ? Z_ERRNO : Z_DATA_ERROR; ret = ferror(in) ? Z_ERRNO : Z_DATA_ERROR;
goto extract_ret; goto extract_ret;
} }
(void)inflatePrime(&strm, here->bits, ret >> (8 - here->bits)); inflatePrime(&state->strm, here->bits, ret >> (8 - here->bits));
} }
(void)inflateSetDictionary(&strm, here->window, WINSIZE); inflateSetDictionary(&state->strm, here->window, WINSIZE);
/* skip uncompressed bytes until offset reached, then satisfy request */ /* skip uncompressed bytes until offset reached, then satisfy request */
offset -= here->out; offset -= here->out;
strm.avail_in = 0; state->strm.avail_in = 0;
skip = 1; /* while skipping to offset */ skip = 1; /* while skipping to offset */
} }
do { do {
/* define where to put uncompressed data, and how much */ /* define where to put uncompressed data, and how much */
if (offset == 0 && skip) { /* at offset now */ if (offset == 0 && skip) { /* at offset now */
strm.avail_out = len; state->strm.avail_out = len;
strm.next_out = buf; state->strm.next_out = buf;
skip = 0; /* only do this once */ skip = 0; /* only do this once */
} }
if (offset > WINSIZE) { /* skip WINSIZE bytes */ if (offset > WINSIZE) { /* skip WINSIZE bytes */
strm.avail_out = WINSIZE; state->strm.avail_out = WINSIZE;
strm.next_out = discard; state->strm.next_out = discard;
offset -= WINSIZE; offset -= WINSIZE;
} }
else if (offset != 0) { /* last skip */ else if (offset != 0) { /* last skip */
strm.avail_out = (unsigned)offset; state->strm.avail_out = (unsigned)offset;
strm.next_out = discard; state->strm.next_out = discard;
offset = 0; offset = 0;
} }
/* uncompress until avail_out filled, or end of stream */ /* uncompress until avail_out filled, or end of stream */
do { do {
if (strm.avail_in == 0) { if (state->strm.avail_in == 0) {
state && (state->in_offset = PX_ftello(in)); state->in_offset = PX_ftello(in);
strm.avail_in = fread(input, 1, CHUNK, in); state->strm.avail_in = fread(input, 1, CHUNK, in);
if (ferror(in)) { if (ferror(in)) {
ret = Z_ERRNO; ret = Z_ERRNO;
goto extract_ret; goto extract_ret;
} }
if (strm.avail_in == 0) { if (state->strm.avail_in == 0) {
ret = Z_DATA_ERROR; ret = Z_DATA_ERROR;
goto extract_ret; goto extract_ret;
} }
strm.next_in = input; state->strm.next_in = input;
} }
uint prev_in = strm.avail_in; uint prev_in = state->strm.avail_in;
ret = inflate(&strm, Z_NO_FLUSH); /* normal inflate */ ret = inflate(&state->strm, Z_NO_FLUSH); /* normal inflate */
state && (state->in_offset += (prev_in - strm.avail_in)); state->in_offset += (prev_in - state->strm.avail_in);
if (ret == Z_NEED_DICT) if (ret == Z_NEED_DICT)
ret = Z_DATA_ERROR; ret = Z_DATA_ERROR;
if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR) if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
goto extract_ret; goto extract_ret;
if (ret == Z_STREAM_END) if (ret == Z_STREAM_END)
break; break;
} while (strm.avail_out != 0); } while (state->strm.avail_out != 0);
/* if reach end of stream, then don't keep trying to get more */ /* if reach end of stream, then don't keep trying to get more */
if (ret == Z_STREAM_END) if (ret == Z_STREAM_END)
@ -461,16 +457,15 @@ local int extract(FILE *in, struct access *index, PX_off_t offset,
isEnd = ret == Z_STREAM_END; isEnd = ret == Z_STREAM_END;
/* compute number of uncompressed bytes read after offset */ /* compute number of uncompressed bytes read after offset */
ret = skip ? 0 : len - strm.avail_out; ret = skip ? 0 : len - state->strm.avail_out;
/* clean up and return bytes read or error */ /* clean up and return bytes read or error */
extract_ret: extract_ret:
if (state && ret == len && !isEnd) { if (ret == len && !isEnd) {
state->out_offset += len; state->out_offset += len;
state->strm = strm;
state->isValid = 1; state->isValid = 1;
} else } else
(void)inflateEnd(&strm); inflateEnd(&state->strm);
return ret; return ret;
} }