Skip to content

Commit 595bd45

Browse files
committed
gzwrite: drop unconsumed caller input after a stall
A partial non-blocking gzwrite reports only the bytes it consumed. The remaining bytes are still owned by the caller, so retaining next_in across the return can access released or reused memory from a later gzip operation.\n\nClear the external input reference before returning from a stalled direct write, and add a deterministic non-blocking regression test.
1 parent e3dc0a8 commit 595bd45

3 files changed

Lines changed: 104 additions & 1 deletion

File tree

gzwrite.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,15 @@ local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) {
242242
n -= state->strm.avail_in;
243243
state->x.pos += n;
244244
len -= n;
245-
if (ret == -1)
245+
if (ret == -1) {
246+
/*
247+
* The bytes not reported as consumed remain owned by the
248+
* caller. Do not keep their address after returning.
249+
*/
250+
state->strm.avail_in = 0;
251+
state->strm.next_in = Z_NULL;
246252
return state->again ? put - len : 0;
253+
}
247254
} while (len);
248255
}
249256

test/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ if(ZLIB_BUILD_STATIC)
6969
$<$<BOOL:${HAVE___ATTR__VIS_HIDDEN}>:HAVE_HIDDEN>)
7070
add_test(NAME zlib_examplestatic COMMAND zlib_examplestatic)
7171

72+
if(UNIX)
73+
add_executable(gznonblock gznonblock.c)
74+
target_link_libraries(gznonblock ZLIB::ZLIBSTATIC)
75+
add_test(NAME zlib_gznonblock COMMAND gznonblock)
76+
endif(UNIX)
77+
7278
add_executable(zlib_minigzipstatic minigzip.c)
7379
target_link_libraries(zlib_minigzipstatic ZLIB::ZLIBSTATIC)
7480
set_target_properties(zlib_minigzipstatic

test/gznonblock.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/* gznonblock.c -- test non-blocking gzip writes
2+
* Copyright (C) 2026 Mark Adler
3+
* For conditions of distribution and use, see copyright notice in zlib.h
4+
*/
5+
6+
#include "zlib.h"
7+
#include <errno.h>
8+
#include <fcntl.h>
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
#include <string.h>
12+
#include <unistd.h>
13+
14+
#define INPUT_SIZE (1024 * 1024)
15+
16+
int main(void) {
17+
int pipefd[2];
18+
unsigned char fill[4096];
19+
unsigned char *input;
20+
unsigned seed;
21+
size_t i;
22+
gzFile file;
23+
int put;
24+
25+
if (pipe(pipefd) == -1 ||
26+
fcntl(pipefd[1], F_SETFL,
27+
fcntl(pipefd[1], F_GETFL) | O_NONBLOCK) == -1) {
28+
perror("pipe");
29+
return 1;
30+
}
31+
32+
memset(fill, 0xa5, sizeof(fill));
33+
while (write(pipefd[1], fill, sizeof(fill)) > 0)
34+
;
35+
if (errno != EAGAIN && errno != EWOULDBLOCK) {
36+
perror("fill pipe");
37+
return 1;
38+
}
39+
40+
/*
41+
* Use incompressible input larger than the gzip buffers so that the full
42+
* pipe stalls gzwrite() before it consumes the caller's entire buffer.
43+
*/
44+
input = (unsigned char *)malloc(INPUT_SIZE);
45+
if (input == NULL) {
46+
fprintf(stderr, "out of memory\n");
47+
return 1;
48+
}
49+
seed = 0x12345678U;
50+
for (i = 0; i < INPUT_SIZE; i++) {
51+
seed ^= seed << 13;
52+
seed ^= seed >> 17;
53+
seed ^= seed << 5;
54+
input[i] = (unsigned char)seed;
55+
}
56+
57+
file = gzdopen(pipefd[1], "wbN");
58+
if (file == NULL) {
59+
fprintf(stderr, "gzdopen failed\n");
60+
return 1;
61+
}
62+
63+
put = gzwrite(file, input, INPUT_SIZE);
64+
free(input);
65+
if (put <= 0 || put >= INPUT_SIZE) {
66+
fprintf(stderr, "gzwrite did not make partial progress: %d\n", put);
67+
return 1;
68+
}
69+
70+
/*
71+
* gzwrite() reported the remaining input as unconsumed, so the caller was
72+
* allowed to release it. A following gzip operation must not access it.
73+
*/
74+
put = gzprintf(file, "%s", "X");
75+
if (put != 1) {
76+
fprintf(stderr, "gzprintf failed after partial gzwrite: %d\n", put);
77+
return 1;
78+
}
79+
80+
if (fcntl(pipefd[0], F_SETFL,
81+
fcntl(pipefd[0], F_GETFL) | O_NONBLOCK) == -1) {
82+
perror("read pipe");
83+
return 1;
84+
}
85+
while (read(pipefd[0], fill, sizeof(fill)) > 0)
86+
;
87+
(void)gzclose(file);
88+
close(pipefd[0]);
89+
return 0;
90+
}

0 commit comments

Comments
 (0)