The zlib library is a venerable piece of C software. The copyright for the decompression routine goes back to 1995, with the last rewrite in 2002. It is used daily by millions.
To be perfect, the decompression routine should not invoke undefined behavior, even if passed a maliciously crafted buffer. I have the opportunity to detect a number of places where it is not obvious that the library does not invoke undefined behavior on some input. Would users of this site help me in confirming these issues? We would improve an already great piece of Open Source software, and there is always something to learn in reading code that has passed the test of time.
Assuming this is okay, for my first post, I would like to suggest looking at function inflate()
in version 1.2.7 of the library.
int ZEXPORT inflate(strm, flush)
z_streamp strm;
int flush;
{
struct inflate_state FAR *state;
state = (struct inflate_state FAR *)strm->state;
/*** strm->state was previously allocated with malloc(), apparently.
Function malloc(), when it succeeds,
returns an uninitialized block of memory. */
...
for (;;)
switch (state->mode) {
...
case MATCH:
if (left == 0) goto inf_leave;
copy = out - left;
if (state->offset > copy) { /* copy from window */
/*** It is unclear if state->offset always contains initialized contents
at this point, line 1125 in inflate.c.
*/
...
So my question is, is it possible to reach line 1125 in inflate.c without having initialized state->offset
?
I linked the library with the following main()
function to identify the possible issue, so I guess that an input vector, if one exists, could be based on this main()
, simply initializing array in[]
to values that cause the issue. This main()
is using the library correctly, I hope, otherwise the flagged issue may be meaningless.
#include "zlib.h"
#define CHUNK 100
z_stream strm;
unsigned char in[CHUNK];
unsigned char out[CHUNK];
int ret;
main(){
int i;
/* allocate inflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit(&strm);
if (ret != Z_OK)
return ret;
for (i=0; i<CHUNK; i++)
in[i] = any();
strm.next_in = in;
strm.avail_in = CHUNK;
strm.next_out = out;
strm.avail_out = CHUNK;
ret = inflate(&strm, Z_NO_FLUSH);
}
Of course, if the apparently dangerous access at line 1125 is in fact safe, then a great answer would be an explanation why.