Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,8 @@ jobs:
LD_LIBRARY_PATH: "target/${{matrix.target}}/release/deps"
working-directory: libz-rs-sys-cdylib
run: |
cargo build --release --target ${{matrix.target}} --no-default-features --features=gz,c-allocator
rustup toolchain install nightly
cargo +nightly build --release --target ${{matrix.target}} --no-default-features --features=gz,gzprintf,c-allocator
cc -o example example.c target/${{matrix.target}}/release/deps/libz_rs.so
./example
valgrind --track-origins=yes --error-exitcode=1 ./example
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions libz-rs-sys-cdylib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ custom-prefix = ["libz-rs-sys/custom-prefix"] # use the LIBZ_RS_SYS_PREFIX to pr
semver-prefix = ["libz-rs-sys/semver-prefix"] # prefix all symbols in a semver-compatible way
capi = []
gz = ["dep:libc"] # support for the `gz*` functions is experimental
gzprintf = []
__internal-test = []

[dependencies]
Expand Down
239 changes: 129 additions & 110 deletions libz-rs-sys-cdylib/example.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ typedef unsigned long z_uintmax_t;
#include <stdarg.h>
#include <stdlib.h>
#include <inttypes.h>
#include <errno.h>

#define TESTFILE "/tmp/example.gz"

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

// /* ===========================================================================
// * Test read/write of .gz files
// */
// static void test_gzio(const char *fname, unsigned char *uncompr, z_size_t uncomprLen) {
// #ifdef NO_GZCOMPRESS
// fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n");
// #else
// int err;
// size_t read;
// size_t len = strlen(hello)+1;
// gzFile file;
// z_off64_t pos;
// z_off64_t comprLen;
//
// /* Write gz file with test data */
// file = PREFIX(gzopen)(fname, "wb");
// if (file == NULL)
// error("gzopen error\n");
// /* Write hello, hello! using gzputs and gzprintf */
// PREFIX(gzputc)(file, 'h');
// if (PREFIX(gzputs)(file, "ello") != 4)
// error("gzputs err: %s\n", PREFIX(gzerror)(file, &err));
// if (PREFIX(gzprintf)(file, ", %s!", "hello") != 8)
// error("gzprintf err: %s\n", PREFIX(gzerror)(file, &err));
// /* Write string null-teriminator using gzseek */
// if (PREFIX(gzseek)(file, 1L, SEEK_CUR) < 0)
// error("gzseek error, gztell=%ld\n", (long)PREFIX(gztell)(file));
// /* Write hello, hello! using gzfwrite using best compression level */
// if (PREFIX(gzsetparams)(file, Z_BEST_COMPRESSION, Z_DEFAULT_STRATEGY) != Z_OK)
// error("gzsetparams err: %s\n", PREFIX(gzerror)(file, &err));
// if (PREFIX(gzfwrite)(hello, len, 1, file) == 0)
// error("gzfwrite err: %s\n", PREFIX(gzerror)(file, &err));
// /* Flush compressed bytes to file */
// if (PREFIX(gzflush)(file, Z_SYNC_FLUSH) != Z_OK)
// error("gzflush err: %s\n", PREFIX(gzerror)(file, &err));
// comprLen = PREFIX(gzoffset)(file);
// if (comprLen <= 0)
// error("gzoffset err: %s\n", PREFIX(gzerror)(file, &err));
// PREFIX(gzclose)(file);
//
// /* Open gz file we previously wrote */
// file = PREFIX(gzopen)(fname, "rb");
// if (file == NULL)
// error("gzopen error\n");
//
// /* Read uncompressed data - hello, hello! string twice */
// strcpy((char*)uncompr, "garbages");
// if (PREFIX(gzread)(file, uncompr, (unsigned)uncomprLen) != (int)(len + len))
// error("gzread err: %s\n", PREFIX(gzerror)(file, &err));
// if (strcmp((char*)uncompr, hello))
// error("bad gzread: %s\n", (char*)uncompr);
// else
// printf("gzread(): %s\n", (char*)uncompr);
// /* Check position at the end of the gz file */
// if (PREFIX(gzeof)(file) != 1)
// error("gzeof err: not reporting end of stream\n");
//
// /* Seek backwards mid-string and check char reading with gzgetc and gzungetc */
// pos = PREFIX(gzseek)(file, -22L, SEEK_CUR);
// if (pos != 6 || PREFIX(gztell)(file) != pos)
// error("gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)PREFIX(gztell)(file));
// if (PREFIX(gzgetc)(file) != ' ')
// error("gzgetc error\n");
// if (PREFIX(gzungetc)(' ', file) != ' ')
// error("gzungetc error\n");
// /* Read first hello, hello! string with gzgets */
// strcpy((char*)uncompr, "garbages");
// PREFIX(gzgets)(file, (char*)uncompr, (int)uncomprLen);
// if (strlen((char*)uncompr) != 7) /* " hello!" */
// error("gzgets err after gzseek: %s\n", PREFIX(gzerror)(file, &err));
// if (strcmp((char*)uncompr, hello + 6))
// error("bad gzgets after gzseek\n");
// else
// printf("gzgets() after gzseek: %s\n", (char*)uncompr);
// /* Seek to second hello, hello! string */
// pos = PREFIX(gzseek)(file, 14L, SEEK_SET);
// if (pos != 14 || PREFIX(gztell)(file) != pos)
// error("gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)PREFIX(gztell)(file));
// /* Check position not at end of file */
// if (PREFIX(gzeof)(file) != 0)
// error("gzeof err: reporting end of stream\n");
// /* Read first hello, hello! string with gzfread */
// strcpy((char*)uncompr, "garbages");
// read = PREFIX(gzfread)(uncompr, uncomprLen, 1, file);
// if (strcmp((const char *)uncompr, hello) != 0)
// error("bad gzgets\n");
// else
// printf("gzgets(): %s\n", (char*)uncompr);
// pos = PREFIX(gzoffset)(file);
// if (pos < 0 || pos != (comprLen + 10))
// error("gzoffset err: wrong offset at end\n");
// /* Trigger an error and clear it with gzclearerr */
// PREFIX(gzfread)(uncompr, (size_t)-1, (size_t)-1, file);
// PREFIX(gzerror)(file, &err);
// if (err == 0)
// error("gzerror err: no error returned\n");
// PREFIX(gzclearerr)(file);
// PREFIX(gzerror)(file, &err);
// if (err != 0)
// error("gzclearerr err: not zero %d\n", err);
//
// PREFIX(gzclose)(file);
//
// if (PREFIX(gzclose)(NULL) != Z_STREAM_ERROR)
// error("gzclose unexpected return when handle null\n");
// Z_UNUSED(read);
// #endif
// }
/* ===========================================================================
* Test read/write of .gz files
*/
static void test_gzio(const char *fname, unsigned char *uncompr, z_size_t uncomprLen) {
#ifdef NO_GZCOMPRESS
fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n");
#else
int err;
size_t read;
size_t len = strlen(hello)+1;
gzFile file;
z_off64_t pos;
z_off64_t comprLen;

/* Write gz file with test data */
file = PREFIX(gzopen)(fname, "wb");
if (file == NULL)
error("gzopen error\n");
/* Write hello, hello! using gzputs and gzprintf */
PREFIX(gzputc)(file, 'h');
if (PREFIX(gzputs)(file, "ello") != 4)
error("gzputs err: %s\n", PREFIX(gzerror)(file, &err));
if (PREFIX(gzprintf)(file, ", %s!", "hello") != 8)
error("gzprintf err: %s\n", PREFIX(gzerror)(file, &err));
/* Write string null-teriminator using gzseek */
if (PREFIX(gzseek)(file, 1L, SEEK_CUR) < 0)
error("gzseek error, gztell=%ld\n", (long)PREFIX(gztell)(file));
/* Write hello, hello! using gzfwrite using best compression level */
if (PREFIX(gzsetparams)(file, Z_BEST_COMPRESSION, Z_DEFAULT_STRATEGY) != Z_OK)
error("gzsetparams err: %s\n", PREFIX(gzerror)(file, &err));
if (PREFIX(gzfwrite)(hello, len, 1, file) == 0)
error("gzfwrite err: %s\n", PREFIX(gzerror)(file, &err));
/* Flush compressed bytes to file */
if (PREFIX(gzflush)(file, Z_SYNC_FLUSH) != Z_OK)
error("gzflush err: %s\n", PREFIX(gzerror)(file, &err));
comprLen = PREFIX(gzoffset)(file);
if (comprLen <= 0)
error("gzoffset err: %s\n", PREFIX(gzerror)(file, &err));
PREFIX(gzclose)(file);

/* Open gz file we previously wrote */
file = PREFIX(gzopen)(fname, "rb");
if (file == NULL)
error("gzopen error\n");

/* Read uncompressed data - hello, hello! string twice */
strcpy((char*)uncompr, "garbages");
if (PREFIX(gzread)(file, uncompr, (unsigned)uncomprLen) != (int)(len + len))
error("gzread err: %s\n", PREFIX(gzerror)(file, &err));
if (strcmp((char*)uncompr, hello))
error("bad gzread: %s\n", (char*)uncompr);
else
printf("gzread(): %s\n", (char*)uncompr);
/* Check position at the end of the gz file */
if (PREFIX(gzeof)(file) != 1)
error("gzeof err: not reporting end of stream\n");

/* Seek backwards mid-string and check char reading with gzgetc and gzungetc */
pos = PREFIX(gzseek)(file, -22L, SEEK_CUR);
if (pos != 6 || PREFIX(gztell)(file) != pos)
error("gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)PREFIX(gztell)(file));
if (PREFIX(gzgetc)(file) != ' ')
error("gzgetc error\n");
if (PREFIX(gzungetc)(' ', file) != ' ')
error("gzungetc error\n");
/* Read first hello, hello! string with gzgets */
strcpy((char*)uncompr, "garbages");
PREFIX(gzgets)(file, (char*)uncompr, (int)uncomprLen);
if (strlen((char*)uncompr) != 7) /* " hello!" */
error("gzgets err after gzseek: %s\n", PREFIX(gzerror)(file, &err));
if (strcmp((char*)uncompr, hello + 6))
error("bad gzgets after gzseek\n");
else
printf("gzgets() after gzseek: %s\n", (char*)uncompr);
/* Seek to second hello, hello! string */
pos = PREFIX(gzseek)(file, 14L, SEEK_SET);
if (pos != 14 || PREFIX(gztell)(file) != pos)
error("gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)PREFIX(gztell)(file));
/* Check position not at end of file */
if (PREFIX(gzeof)(file) != 0)
error("gzeof err: reporting end of stream\n");
/* Read first hello, hello! string with gzfread */
strcpy((char*)uncompr, "garbages");
read = PREFIX(gzfread)(uncompr, uncomprLen, 1, file);
if (strcmp((const char *)uncompr, hello) != 0)
error("bad gzgets\n");
else
printf("gzgets(): %s\n", (char*)uncompr);
pos = PREFIX(gzoffset)(file);
if (pos < 0 || pos != (comprLen + 10))
error("gzoffset err: wrong offset at end\n");
/* Trigger an error and clear it with gzclearerr */
PREFIX(gzfread)(uncompr, (size_t)-1, (size_t)-1, file);
PREFIX(gzerror)(file, &err);
if (err == 0)
error("gzerror err: no error returned\n");
PREFIX(gzclearerr)(file);
PREFIX(gzerror)(file, &err);
if (err != 0)
error("gzclearerr err: not zero %d\n", err);

PREFIX(gzclose)(file);

if (PREFIX(gzclose)(NULL) != Z_STREAM_ERROR)
error("gzclose unexpected return when handle null\n");
Z_UNUSED(read);
#endif
}

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

// Tests for a CVE in stock zlib. We're not vulnerable because we exclusively use `vsnprintf`.
void cve_2003_0107() {
gzFile f;
int ret;

f = gzopen("/dev/null", "w");
if (f == NULL) {
error("failed to open `/dev/null` for writing\n");
}

ret = gzprintf(f, "%10240s", "");
printf("gzprintf -> %d\n", ret);
ret = gzclose(f);
printf("gzclose -> %d [%d]\n", ret, errno);
}

/* ===========================================================================
* Usage: example [output.gz [input.gz]]
*/
Expand All @@ -977,8 +994,8 @@ int main(int argc, char *argv[]) {

test_compress(compr, comprLen, uncompr, uncomprLen);

// test_gzio((argc > 1 ? argv[1] : TESTFILE),
// uncompr, uncomprLen);
test_gzio((argc > 1 ? argv[1] : TESTFILE),
uncompr, uncomprLen);

test_deflate(compr, comprLen);
test_inflate(compr, comprLen, uncompr, uncomprLen);
Expand Down Expand Up @@ -1008,6 +1025,8 @@ int main(int argc, char *argv[]) {
test_deflate_pending(compr, comprLen);
test_deflate_prime(compr, comprLen, uncompr, uncomprLen);

cve_2003_0107();

free(compr);
free(uncompr);

Expand Down
Loading