Description
I found a crash-prone out-of-bounds write in gzungetc() (gzread.c) when internal output-buffer state is invalid in the state->x.have == 0 path.
/* if output buffer empty, put byte at end (allows more pushing) */
if (state->x.have == 0) {
state->x.have = 1;
state->x.next = state->out + (state->size << 1) - 1;
state->x.next[0] = (unsigned char)c;
state->x.pos--;
state->past = 0;
return c;
}
If state->size == 0, state->x.next becomes state->out - 1; if state->out == NULL, the computed pointer is invalid. The subsequent state->x.next[0] write can cause SEGV / ASan underflow.
This was originally discovered by fuzzing through public gz* entry points (original artifact lost), and I reproduced the fault deterministically using a minimal internal-state PoC with ASan/UBSan.
Reproduction
Build zlib with ASan/UBSan in the source directory:
CFLAGS="-O0 -g -fsanitize=address,undefined -fno-omit-frame-pointer" ./configure --static
make -j
Build the PoC (uses internal header/state, e.g. gzguts.h):
gcc -O0 -g -fsanitize=address,undefined -fno-omit-frame-pointer -I. poc.c ./libz.a -o poc
Run:
ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:symbolize=1 ./poc
Before fix: ASan reports an out-of-bounds write in gzungetc at the state->x.next[0] write site.
After fix: no crash; function returns -1 in the invalid-state path.
PoC source (poc.c)
#include <stdio.h>
#include <string.h>
#include "gzguts.h"
int main(void) {
gz_state state;
memset(&state, 0, sizeof(state));
state.mode = GZ_READ;
state.err = Z_OK;
state.how = COPY;
state.x.have = 0;
state.out = NULL;
state.size = 0;
int ret = gzungetc('A', (gzFile)&state);
printf("ret=%d\n", ret);
return 0;
}
Proposed fix
Add a defensive guard before pointer arithmetic in that branch and fail early:
if (state->x.have == 0) {
if (state->out == NULL || state->size == 0)
return -1;
state->x.have = 1;
state->x.next = state->out + (state->size << 1) - 1;
state->x.next[0] = (unsigned char)c;
state->x.pos--;
state->past = 0;
return c;
}
This is a minimal hardening change: it preserves normal behavior and prevents invalid-state crashes.
Description
I found a crash-prone out-of-bounds write in
gzungetc()(gzread.c) when internal output-buffer state is invalid in thestate->x.have == 0path.If
state->size == 0,state->x.nextbecomesstate->out - 1; ifstate->out == NULL, the computed pointer is invalid. The subsequentstate->x.next[0]write can cause SEGV / ASan underflow.This was originally discovered by fuzzing through public gz* entry points (original artifact lost), and I reproduced the fault deterministically using a minimal internal-state PoC with ASan/UBSan.
Reproduction
Build zlib with ASan/UBSan in the source directory:
CFLAGS="-O0 -g -fsanitize=address,undefined -fno-omit-frame-pointer" ./configure --static make -jBuild the PoC (uses internal header/state, e.g.
gzguts.h):Run:
Before fix: ASan reports an out-of-bounds write in
gzungetcat thestate->x.next[0]write site.After fix: no crash; function returns
-1in the invalid-state path.PoC source (poc.c)
Proposed fix
Add a defensive guard before pointer arithmetic in that branch and fail early:
This is a minimal hardening change: it preserves normal behavior and prevents invalid-state crashes.