From a4cd265e7c4704a1522b00c57e3e894afa4d55d4 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Wed, 13 Nov 2019 13:36:00 +0100 Subject: [PATCH] unittests: Cover the decompression API with tests --- src/tests/unittests/Makefile.am | 12 +- src/tests/unittests/Makefile.mak | 3 +- src/tests/unittests/compression.c | 309 ++++++++++++++++++++++++++++++ 3 files changed, 322 insertions(+), 2 deletions(-) create mode 100644 src/tests/unittests/compression.c diff --git a/src/tests/unittests/Makefile.am b/src/tests/unittests/Makefile.am index 25b35fff..05a4a39d 100644 --- a/src/tests/unittests/Makefile.am +++ b/src/tests/unittests/Makefile.am @@ -7,6 +7,7 @@ clean-local: code-coverage-clean distclean-local: code-coverage-dist-clean noinst_PROGRAMS = asn1 +TESTS = asn1 noinst_HEADERS = torture.h @@ -17,7 +18,16 @@ asn1_CFLAGS = $(CMOCKA_CFLAGS) $(CODE_COVERAGE_CFLAGS) asn1_LDADD = $(top_builddir)/src/libopensc/libopensc.la \ $(CMOCKA_LIBS) $(CODE_COVERAGE_LIBS) $(OPTIONAL_OPENSSL_LIBS) +if ENABLE_ZLIB +noinst_PROGRAMS += compression +TESTS += compression + +compression_SOURCES = compression.c +compression_CFLAGS = $(CMOCKA_CFLAGS) $(CODE_COVERAGE_CFLAGS) +compression_LDADD = $(top_builddir)/src/libopensc/libopensc.la \ + $(CMOCKA_LIBS) $(CODE_COVERAGE_LIBS) $(OPTIONAL_ZLIB_LIBS) +endif + -TESTS = asn1 endif diff --git a/src/tests/unittests/Makefile.mak b/src/tests/unittests/Makefile.mak index 37783a02..41762fdb 100644 --- a/src/tests/unittests/Makefile.mak +++ b/src/tests/unittests/Makefile.mak @@ -1,8 +1,9 @@ TOPDIR = ..\..\.. -TARGETS = asn1 +TARGETS = asn1 compression OBJECTS = asn1.obj \ + compression.obj $(TOPDIR)\win32\versioninfo.res all: $(TARGETS) diff --git a/src/tests/unittests/compression.c b/src/tests/unittests/compression.c new file mode 100644 index 00000000..d1b7a4d7 --- /dev/null +++ b/src/tests/unittests/compression.c @@ -0,0 +1,309 @@ +/* + * compression.c: Unit tests for compression API + * + * Copyright (C) 2019 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "torture.h" +#include "libopensc/log.c" +#include "libopensc/compression.c" + +/* The data from fuzzer has valid header (0x1f, 0x8b), but anything + * after that is just garbage. The first call to inflate() + * returns Z_STREAM_END, calculated number of processed bytes 0, while + * keeping the allocated buffers. + */ +u8 invalid_data[] = { + 0x1f, 0x8b, 0x08, 0x10, 0x08, 0x78, 0x10, 0x1f, + 0x8b, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, + 0x10, 0x08, 0x78, 0x10, 0x1f, 0x8b, 0x08, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x10, 0x08, 0x78, + 0x10, 0x1f, 0x8b, 0x08, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x08, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x10, 0x08, 0x78, + 0x10, 0x1f, 0x8b}; + +/* Generated using + * $ echo "test" > /tmp/test + * $ gzip -c /tmp/test > /tmp/test.gz + * $ hexdump -C /tmp/test.gz + */ +u8 valid_data[] = { + 0x1f, 0x8b, 0x08, 0x08, 0x5d, 0xd8, 0xcb, 0x5d, + 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x00, 0x2b, + 0x49, 0x2d, 0x2e, 0xe1, 0x02, 0x00, 0xc6, 0x35, + 0xb9, 0x3b, 0x05, 0x00, 0x00, 0x00}; + +/* Generated as in the previous test case with some added mess on the end + */ +u8 invalid_suffix_data[] = { + 0x1f, 0x8b, 0x08, 0x08, 0x5d, 0xd8, 0xcb, 0x5d, + 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x00, 0x2b, + 0x49, 0x2d, 0x2e, 0xe1, 0x02, 0x00, 0xc6, 0x35, + 0xb9, 0x3b, 0x05, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff}; + +static void torture_compression_decompress_alloc_empty(void **state) +{ + u8 *buf = NULL; + u8 *data = NULL; + size_t buflen = 0; + size_t datalen = 0; + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); + assert_null(buf); +} + +static void torture_compression_decompress_alloc_gzip_empty(void **state) +{ + u8 *buf = NULL; + u8 *data = NULL; + size_t buflen = 0; + size_t datalen = 0; + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, data, datalen, COMPRESSION_GZIP); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); + assert_null(buf); +} + +static void torture_compression_decompress_alloc_zlib_empty(void **state) +{ + u8 *buf = NULL; + u8 *data = NULL; + size_t buflen = 0; + size_t datalen = 0; + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, data, datalen, COMPRESSION_ZLIB); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); + assert_null(buf); +} + +static void torture_compression_decompress_alloc_header(void **state) +{ + u8 *buf = NULL; + u8 data[] = {0x1f, 0x8b}; + size_t buflen = 0; + size_t datalen = sizeof(data); + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); + assert_null(buf); +} + +static void torture_compression_decompress_alloc_header_invalid(void **state) +{ + u8 *buf = NULL; + u8 data[] = {0x1e, 0x8a}; + size_t buflen = 0; + size_t datalen = sizeof(data); + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); + assert_null(buf); +} + +static void torture_compression_decompress_alloc_invalid(void **state) +{ + u8 *buf = NULL; + size_t buflen = 0; + size_t datalen = sizeof(invalid_data); + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, invalid_data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); + assert_null(buf); +} + +static void torture_compression_decompress_alloc_valid(void **state) +{ + u8 *buf = NULL; + size_t buflen = 0; + size_t datalen = sizeof(valid_data); + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, valid_data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(buflen, 5); + assert_memory_equal(buf, "test\x0a", 5); +} + +static void torture_compression_decompress_alloc_invalid_suffix(void **state) +{ + u8 *buf = NULL; + size_t buflen = 0; + size_t datalen = sizeof(invalid_suffix_data); + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, invalid_suffix_data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); /* TODO Is this fine? */ + assert_int_equal(buflen, 5); + assert_memory_equal(buf, "test\x0a", 5); +} + + + +/* Decompress without allocation */ +static void torture_compression_decompress_empty(void **state) +{ + u8 buf[1024]; + u8 *data = NULL; + size_t buflen = sizeof(buf); + size_t datalen = 0; + int rv; + + rv = sc_decompress(buf, &buflen, data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, sizeof(buf)); /* Was not touched */ +} + +static void torture_compression_decompress_gzip_empty(void **state) +{ + u8 buf[1024]; + u8 *data = NULL; + size_t buflen = sizeof(buf); + size_t datalen = 0; + int rv; + + rv = sc_decompress(buf, &buflen, data, datalen, COMPRESSION_GZIP); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, sizeof(buf)); +} + +static void torture_compression_decompress_zlib_empty(void **state) +{ + u8 buf[1024]; + u8 *data = NULL; + size_t buflen = sizeof(buf); + size_t datalen = 0; + int rv; + + rv = sc_decompress(buf, &buflen, data, datalen, COMPRESSION_ZLIB); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); +} + +static void torture_compression_decompress_header(void **state) +{ + u8 buf[1024]; + u8 data[] = {0x1f, 0x8b}; + size_t buflen = sizeof(buf); + size_t datalen = sizeof(data); + int rv; + + rv = sc_decompress(buf, &buflen, data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); +} + +static void torture_compression_decompress_header_invalid(void **state) +{ + u8 buf[1024]; + u8 data[] = {0x1e, 0x8a}; + size_t buflen = sizeof(buf); + size_t datalen = sizeof(data); + int rv; + + rv = sc_decompress(buf, &buflen, data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); +} + +static void torture_compression_decompress_invalid(void **state) +{ + u8 buf[1024]; + size_t buflen = sizeof(buf); + size_t datalen = sizeof(invalid_data); + int rv; + + rv = sc_decompress(buf, &buflen, invalid_data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); +} + +static void torture_compression_decompress_valid(void **state) +{ + u8 buf[1024]; + size_t buflen = sizeof(buf); + size_t datalen = sizeof(valid_data); + int rv; + + rv = sc_decompress(buf, &buflen, valid_data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(buflen, 5); + assert_memory_equal(buf, "test\x0a", 5); +} + +static void torture_compression_decompress_invalid_suffix(void **state) +{ + u8 buf[1024]; + size_t buflen = sizeof(buf); + size_t datalen = sizeof(invalid_suffix_data); + int rv; + + rv = sc_decompress(buf, &buflen, invalid_suffix_data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); /* TODO Is this fine? */ + assert_int_equal(buflen, 5); + assert_memory_equal(buf, "test\x0a", 5); +} + + + +int main(void) +{ + int rc; + struct CMUnitTest tests[] = { + /* Decompress alloc */ + cmocka_unit_test(torture_compression_decompress_alloc_empty), + cmocka_unit_test(torture_compression_decompress_alloc_gzip_empty), + cmocka_unit_test(torture_compression_decompress_alloc_zlib_empty), + cmocka_unit_test(torture_compression_decompress_alloc_header), + cmocka_unit_test(torture_compression_decompress_alloc_header_invalid), + cmocka_unit_test(torture_compression_decompress_alloc_invalid), + cmocka_unit_test(torture_compression_decompress_alloc_invalid_suffix), + cmocka_unit_test(torture_compression_decompress_alloc_valid), + /* Decompress */ + cmocka_unit_test(torture_compression_decompress_empty), + cmocka_unit_test(torture_compression_decompress_gzip_empty), + cmocka_unit_test(torture_compression_decompress_zlib_empty), + cmocka_unit_test(torture_compression_decompress_header), + cmocka_unit_test(torture_compression_decompress_header_invalid), + cmocka_unit_test(torture_compression_decompress_invalid), + cmocka_unit_test(torture_compression_decompress_invalid_suffix), + cmocka_unit_test(torture_compression_decompress_valid), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + return rc; +}