Skip to content

Commit f78b30d

Browse files
committed
add gzprintf
1 parent 91417d9 commit f78b30d

File tree

8 files changed

+421
-113
lines changed

8 files changed

+421
-113
lines changed

.github/workflows/checks.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,8 @@ jobs:
392392
LD_LIBRARY_PATH: "target/${{matrix.target}}/release/deps"
393393
working-directory: libz-rs-sys-cdylib
394394
run: |
395-
cargo build --release --target ${{matrix.target}} --no-default-features --features=gz,c-allocator
395+
rustup toolchain install nightly
396+
cargo +nightly build --release --target ${{matrix.target}} --no-default-features --features=gz,gzprintf,c-allocator
396397
cc -o example example.c target/${{matrix.target}}/release/deps/libz_rs.so
397398
./example
398399
valgrind --track-origins=yes --error-exitcode=1 ./example

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libz-rs-sys-cdylib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ custom-prefix = ["libz-rs-sys/custom-prefix"] # use the LIBZ_RS_SYS_PREFIX to pr
2828
semver-prefix = ["libz-rs-sys/semver-prefix"] # prefix all symbols in a semver-compatible way
2929
capi = []
3030
gz = ["dep:libc"] # support for the `gz*` functions is experimental
31+
gzprintf = []
3132
__internal-test = []
3233

3334
[dependencies]

libz-rs-sys-cdylib/example.c

Lines changed: 129 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ typedef unsigned long z_uintmax_t;
2121
#include <stdarg.h>
2222
#include <stdlib.h>
2323
#include <inttypes.h>
24+
#include <errno.h>
2425

2526
#define TESTFILE "/tmp/example.gz"
2627

@@ -94,114 +95,114 @@ static void test_compress(unsigned char *compr, z_uintmax_t comprLen, unsigned c
9495
printf("uncompress(): %s\n", (char *)uncompr);
9596
}
9697

97-
// /* ===========================================================================
98-
// * Test read/write of .gz files
99-
// */
100-
// static void test_gzio(const char *fname, unsigned char *uncompr, z_size_t uncomprLen) {
101-
// #ifdef NO_GZCOMPRESS
102-
// fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n");
103-
// #else
104-
// int err;
105-
// size_t read;
106-
// size_t len = strlen(hello)+1;
107-
// gzFile file;
108-
// z_off64_t pos;
109-
// z_off64_t comprLen;
110-
//
111-
// /* Write gz file with test data */
112-
// file = PREFIX(gzopen)(fname, "wb");
113-
// if (file == NULL)
114-
// error("gzopen error\n");
115-
// /* Write hello, hello! using gzputs and gzprintf */
116-
// PREFIX(gzputc)(file, 'h');
117-
// if (PREFIX(gzputs)(file, "ello") != 4)
118-
// error("gzputs err: %s\n", PREFIX(gzerror)(file, &err));
119-
// if (PREFIX(gzprintf)(file, ", %s!", "hello") != 8)
120-
// error("gzprintf err: %s\n", PREFIX(gzerror)(file, &err));
121-
// /* Write string null-teriminator using gzseek */
122-
// if (PREFIX(gzseek)(file, 1L, SEEK_CUR) < 0)
123-
// error("gzseek error, gztell=%ld\n", (long)PREFIX(gztell)(file));
124-
// /* Write hello, hello! using gzfwrite using best compression level */
125-
// if (PREFIX(gzsetparams)(file, Z_BEST_COMPRESSION, Z_DEFAULT_STRATEGY) != Z_OK)
126-
// error("gzsetparams err: %s\n", PREFIX(gzerror)(file, &err));
127-
// if (PREFIX(gzfwrite)(hello, len, 1, file) == 0)
128-
// error("gzfwrite err: %s\n", PREFIX(gzerror)(file, &err));
129-
// /* Flush compressed bytes to file */
130-
// if (PREFIX(gzflush)(file, Z_SYNC_FLUSH) != Z_OK)
131-
// error("gzflush err: %s\n", PREFIX(gzerror)(file, &err));
132-
// comprLen = PREFIX(gzoffset)(file);
133-
// if (comprLen <= 0)
134-
// error("gzoffset err: %s\n", PREFIX(gzerror)(file, &err));
135-
// PREFIX(gzclose)(file);
136-
//
137-
// /* Open gz file we previously wrote */
138-
// file = PREFIX(gzopen)(fname, "rb");
139-
// if (file == NULL)
140-
// error("gzopen error\n");
141-
//
142-
// /* Read uncompressed data - hello, hello! string twice */
143-
// strcpy((char*)uncompr, "garbages");
144-
// if (PREFIX(gzread)(file, uncompr, (unsigned)uncomprLen) != (int)(len + len))
145-
// error("gzread err: %s\n", PREFIX(gzerror)(file, &err));
146-
// if (strcmp((char*)uncompr, hello))
147-
// error("bad gzread: %s\n", (char*)uncompr);
148-
// else
149-
// printf("gzread(): %s\n", (char*)uncompr);
150-
// /* Check position at the end of the gz file */
151-
// if (PREFIX(gzeof)(file) != 1)
152-
// error("gzeof err: not reporting end of stream\n");
153-
//
154-
// /* Seek backwards mid-string and check char reading with gzgetc and gzungetc */
155-
// pos = PREFIX(gzseek)(file, -22L, SEEK_CUR);
156-
// if (pos != 6 || PREFIX(gztell)(file) != pos)
157-
// error("gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)PREFIX(gztell)(file));
158-
// if (PREFIX(gzgetc)(file) != ' ')
159-
// error("gzgetc error\n");
160-
// if (PREFIX(gzungetc)(' ', file) != ' ')
161-
// error("gzungetc error\n");
162-
// /* Read first hello, hello! string with gzgets */
163-
// strcpy((char*)uncompr, "garbages");
164-
// PREFIX(gzgets)(file, (char*)uncompr, (int)uncomprLen);
165-
// if (strlen((char*)uncompr) != 7) /* " hello!" */
166-
// error("gzgets err after gzseek: %s\n", PREFIX(gzerror)(file, &err));
167-
// if (strcmp((char*)uncompr, hello + 6))
168-
// error("bad gzgets after gzseek\n");
169-
// else
170-
// printf("gzgets() after gzseek: %s\n", (char*)uncompr);
171-
// /* Seek to second hello, hello! string */
172-
// pos = PREFIX(gzseek)(file, 14L, SEEK_SET);
173-
// if (pos != 14 || PREFIX(gztell)(file) != pos)
174-
// error("gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)PREFIX(gztell)(file));
175-
// /* Check position not at end of file */
176-
// if (PREFIX(gzeof)(file) != 0)
177-
// error("gzeof err: reporting end of stream\n");
178-
// /* Read first hello, hello! string with gzfread */
179-
// strcpy((char*)uncompr, "garbages");
180-
// read = PREFIX(gzfread)(uncompr, uncomprLen, 1, file);
181-
// if (strcmp((const char *)uncompr, hello) != 0)
182-
// error("bad gzgets\n");
183-
// else
184-
// printf("gzgets(): %s\n", (char*)uncompr);
185-
// pos = PREFIX(gzoffset)(file);
186-
// if (pos < 0 || pos != (comprLen + 10))
187-
// error("gzoffset err: wrong offset at end\n");
188-
// /* Trigger an error and clear it with gzclearerr */
189-
// PREFIX(gzfread)(uncompr, (size_t)-1, (size_t)-1, file);
190-
// PREFIX(gzerror)(file, &err);
191-
// if (err == 0)
192-
// error("gzerror err: no error returned\n");
193-
// PREFIX(gzclearerr)(file);
194-
// PREFIX(gzerror)(file, &err);
195-
// if (err != 0)
196-
// error("gzclearerr err: not zero %d\n", err);
197-
//
198-
// PREFIX(gzclose)(file);
199-
//
200-
// if (PREFIX(gzclose)(NULL) != Z_STREAM_ERROR)
201-
// error("gzclose unexpected return when handle null\n");
202-
// Z_UNUSED(read);
203-
// #endif
204-
// }
98+
/* ===========================================================================
99+
* Test read/write of .gz files
100+
*/
101+
static void test_gzio(const char *fname, unsigned char *uncompr, z_size_t uncomprLen) {
102+
#ifdef NO_GZCOMPRESS
103+
fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n");
104+
#else
105+
int err;
106+
size_t read;
107+
size_t len = strlen(hello)+1;
108+
gzFile file;
109+
z_off64_t pos;
110+
z_off64_t comprLen;
111+
112+
/* Write gz file with test data */
113+
file = PREFIX(gzopen)(fname, "wb");
114+
if (file == NULL)
115+
error("gzopen error\n");
116+
/* Write hello, hello! using gzputs and gzprintf */
117+
PREFIX(gzputc)(file, 'h');
118+
if (PREFIX(gzputs)(file, "ello") != 4)
119+
error("gzputs err: %s\n", PREFIX(gzerror)(file, &err));
120+
if (PREFIX(gzprintf)(file, ", %s!", "hello") != 8)
121+
error("gzprintf err: %s\n", PREFIX(gzerror)(file, &err));
122+
/* Write string null-teriminator using gzseek */
123+
if (PREFIX(gzseek)(file, 1L, SEEK_CUR) < 0)
124+
error("gzseek error, gztell=%ld\n", (long)PREFIX(gztell)(file));
125+
/* Write hello, hello! using gzfwrite using best compression level */
126+
if (PREFIX(gzsetparams)(file, Z_BEST_COMPRESSION, Z_DEFAULT_STRATEGY) != Z_OK)
127+
error("gzsetparams err: %s\n", PREFIX(gzerror)(file, &err));
128+
if (PREFIX(gzfwrite)(hello, len, 1, file) == 0)
129+
error("gzfwrite err: %s\n", PREFIX(gzerror)(file, &err));
130+
/* Flush compressed bytes to file */
131+
if (PREFIX(gzflush)(file, Z_SYNC_FLUSH) != Z_OK)
132+
error("gzflush err: %s\n", PREFIX(gzerror)(file, &err));
133+
comprLen = PREFIX(gzoffset)(file);
134+
if (comprLen <= 0)
135+
error("gzoffset err: %s\n", PREFIX(gzerror)(file, &err));
136+
PREFIX(gzclose)(file);
137+
138+
/* Open gz file we previously wrote */
139+
file = PREFIX(gzopen)(fname, "rb");
140+
if (file == NULL)
141+
error("gzopen error\n");
142+
143+
/* Read uncompressed data - hello, hello! string twice */
144+
strcpy((char*)uncompr, "garbages");
145+
if (PREFIX(gzread)(file, uncompr, (unsigned)uncomprLen) != (int)(len + len))
146+
error("gzread err: %s\n", PREFIX(gzerror)(file, &err));
147+
if (strcmp((char*)uncompr, hello))
148+
error("bad gzread: %s\n", (char*)uncompr);
149+
else
150+
printf("gzread(): %s\n", (char*)uncompr);
151+
/* Check position at the end of the gz file */
152+
if (PREFIX(gzeof)(file) != 1)
153+
error("gzeof err: not reporting end of stream\n");
154+
155+
/* Seek backwards mid-string and check char reading with gzgetc and gzungetc */
156+
pos = PREFIX(gzseek)(file, -22L, SEEK_CUR);
157+
if (pos != 6 || PREFIX(gztell)(file) != pos)
158+
error("gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)PREFIX(gztell)(file));
159+
if (PREFIX(gzgetc)(file) != ' ')
160+
error("gzgetc error\n");
161+
if (PREFIX(gzungetc)(' ', file) != ' ')
162+
error("gzungetc error\n");
163+
/* Read first hello, hello! string with gzgets */
164+
strcpy((char*)uncompr, "garbages");
165+
PREFIX(gzgets)(file, (char*)uncompr, (int)uncomprLen);
166+
if (strlen((char*)uncompr) != 7) /* " hello!" */
167+
error("gzgets err after gzseek: %s\n", PREFIX(gzerror)(file, &err));
168+
if (strcmp((char*)uncompr, hello + 6))
169+
error("bad gzgets after gzseek\n");
170+
else
171+
printf("gzgets() after gzseek: %s\n", (char*)uncompr);
172+
/* Seek to second hello, hello! string */
173+
pos = PREFIX(gzseek)(file, 14L, SEEK_SET);
174+
if (pos != 14 || PREFIX(gztell)(file) != pos)
175+
error("gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)PREFIX(gztell)(file));
176+
/* Check position not at end of file */
177+
if (PREFIX(gzeof)(file) != 0)
178+
error("gzeof err: reporting end of stream\n");
179+
/* Read first hello, hello! string with gzfread */
180+
strcpy((char*)uncompr, "garbages");
181+
read = PREFIX(gzfread)(uncompr, uncomprLen, 1, file);
182+
if (strcmp((const char *)uncompr, hello) != 0)
183+
error("bad gzgets\n");
184+
else
185+
printf("gzgets(): %s\n", (char*)uncompr);
186+
pos = PREFIX(gzoffset)(file);
187+
if (pos < 0 || pos != (comprLen + 10))
188+
error("gzoffset err: wrong offset at end\n");
189+
/* Trigger an error and clear it with gzclearerr */
190+
PREFIX(gzfread)(uncompr, (size_t)-1, (size_t)-1, file);
191+
PREFIX(gzerror)(file, &err);
192+
if (err == 0)
193+
error("gzerror err: no error returned\n");
194+
PREFIX(gzclearerr)(file);
195+
PREFIX(gzerror)(file, &err);
196+
if (err != 0)
197+
error("gzclearerr err: not zero %d\n", err);
198+
199+
PREFIX(gzclose)(file);
200+
201+
if (PREFIX(gzclose)(NULL) != Z_STREAM_ERROR)
202+
error("gzclose unexpected return when handle null\n");
203+
Z_UNUSED(read);
204+
#endif
205+
}
205206

206207
/* ===========================================================================
207208
* Test deflate() with small buffers
@@ -957,6 +958,22 @@ static void test_deflate_tune(unsigned char *compr, size_t comprLen) {
957958
CHECK_ERR(err, "deflateEnd");
958959
}
959960

961+
// Tests for a CVE in stock zlib. We're not vulnerable because we exclusively use `vsnprintf`.
962+
void cve_2003_0107() {
963+
gzFile f;
964+
int ret;
965+
966+
f = gzopen("/dev/null", "w");
967+
if (f == NULL) {
968+
error("failed to open `/dev/null` for writing\n");
969+
}
970+
971+
ret = gzprintf(f, "%10240s", "");
972+
printf("gzprintf -> %d\n", ret);
973+
ret = gzclose(f);
974+
printf("gzclose -> %d [%d]\n", ret, errno);
975+
}
976+
960977
/* ===========================================================================
961978
* Usage: example [output.gz [input.gz]]
962979
*/
@@ -977,8 +994,8 @@ int main(int argc, char *argv[]) {
977994

978995
test_compress(compr, comprLen, uncompr, uncomprLen);
979996

980-
// test_gzio((argc > 1 ? argv[1] : TESTFILE),
981-
// uncompr, uncomprLen);
997+
test_gzio((argc > 1 ? argv[1] : TESTFILE),
998+
uncompr, uncomprLen);
982999

9831000
test_deflate(compr, comprLen);
9841001
test_inflate(compr, comprLen, uncompr, uncomprLen);
@@ -1008,6 +1025,8 @@ int main(int argc, char *argv[]) {
10081025
test_deflate_pending(compr, comprLen);
10091026
test_deflate_prime(compr, comprLen, uncompr, uncomprLen);
10101027

1028+
cve_2003_0107();
1029+
10111030
free(compr);
10121031
free(uncompr);
10131032

0 commit comments

Comments
 (0)