From e708623c12e3179c5aebcc5005a56e0b44ac7ecf Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sat, 5 Sep 2020 18:05:22 +0100 Subject: [PATCH] Problem: no fuzzing coverage for ZMQ_STREAM Solution: add tests --- Makefile.am | 28 ++++++ tests/test_bind_stream_fuzzer.cpp | 132 +++++++++++++++++++++++++++ tests/test_connect_stream_fuzzer.cpp | 117 ++++++++++++++++++++++++ 3 files changed, 277 insertions(+) create mode 100644 tests/test_bind_stream_fuzzer.cpp create mode 100644 tests/test_connect_stream_fuzzer.cpp diff --git a/Makefile.am b/Makefile.am index 44ca9a54..3b1ae7a1 100755 --- a/Makefile.am +++ b/Makefile.am @@ -1128,6 +1128,8 @@ fuzzer_apps = tests/test_bind_null_fuzzer \ tests/test_connect_null_fuzzer \ tests/test_bind_fuzzer \ tests/test_connect_fuzzer \ + tests/test_bind_stream_fuzzer \ + tests/test_connect_stream_fuzzer \ tests/test_socket_options_fuzzer tests_test_bind_null_fuzzer_DEPENDENCIES = src/libzmq.la @@ -1170,6 +1172,22 @@ tests_test_socket_options_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} tests_test_socket_options_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} tests_test_socket_options_fuzzer_CXXFLAGS = -std=c++11 +tests_test_bind_stream_fuzzer_DEPENDENCIES = src/libzmq.la +tests_test_bind_stream_fuzzer_SOURCES = tests/test_bind_stream_fuzzer.cpp +tests_test_bind_stream_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} \ + $(top_builddir)/src/.libs/libzmq.a \ + ${src_libzmq_la_LIBADD} +tests_test_bind_stream_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_bind_stream_fuzzer_CXXFLAGS = -std=c++11 + +tests_test_connect_stream_fuzzer_DEPENDENCIES = src/libzmq.la +tests_test_connect_stream_fuzzer_SOURCES = tests/test_connect_stream_fuzzer.cpp +tests_test_connect_stream_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} \ + $(top_builddir)/src/.libs/libzmq.a \ + ${src_libzmq_la_LIBADD} +tests_test_connect_stream_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_connect_stream_fuzzer_CXXFLAGS = -std=c++11 + if HAVE_CURVE fuzzer_apps += tests/test_bind_curve_fuzzer \ tests/test_connect_curve_fuzzer \ @@ -1228,6 +1246,8 @@ test_apps += tests/test_bind_null_fuzzer \ tests/test_connect_null_fuzzer \ tests/test_bind_fuzzer \ tests/test_connect_fuzzer \ + tests/test_bind_stream_fuzzer \ + tests/test_connect_stream_fuzzer \ tests/test_socket_options_fuzzer tests_test_bind_null_fuzzer_SOURCES = tests/test_bind_null_fuzzer.cpp @@ -1250,6 +1270,14 @@ tests_test_socket_options_fuzzer_SOURCES = tests/test_socket_options_fuzzer.cpp tests_test_socket_options_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la tests_test_socket_options_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_bind_stream_fuzzer_SOURCES = tests/test_bind_stream_fuzzer.cpp +tests_test_bind_stream_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la +tests_test_bind_stream_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} + +tests_test_connect_stream_fuzzer_SOURCES = tests/test_connect_stream_fuzzer.cpp +tests_test_connect_stream_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la +tests_test_connect_stream_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} + if HAVE_CURVE test_apps += tests/test_bind_curve_fuzzer \ tests/test_connect_curve_fuzzer \ diff --git a/tests/test_bind_stream_fuzzer.cpp b/tests/test_bind_stream_fuzzer.cpp new file mode 100644 index 00000000..0d43ca3d --- /dev/null +++ b/tests/test_bind_stream_fuzzer.cpp @@ -0,0 +1,132 @@ +/* + Copyright (c) 2020 Contributors as noted in the AUTHORS file + + This file is part of libzmq, the ZeroMQ core engine in C++. + + libzmq is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + As a special exception, the Contributors give you permission to link + this library with independent modules to produce an executable, + regardless of the license terms of these independent modules, and to + copy and distribute the resulting executable under terms of your choice, + provided that you also meet, for each linked independent module, the + terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. + If you modify this library, you must extend this exception to your + version of the library. + + libzmq 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 Lesser General Public License + along with this program. If not, see . +*/ + +#ifdef ZMQ_USE_FUZZING_ENGINE +#include +#endif + +#include "testutil.hpp" +#include "testutil_unity.hpp" + +// Test that the ZMTP engine handles invalid handshake when binding +// https://rfc.zeromq.org/spec/37/ +extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) +{ + setup_test_context (); + char my_endpoint[MAX_SOCKET_STRING]; + void *server = test_context_socket (ZMQ_STREAM); + // As per API by default there's no limit to the size of a message, + // but the sanitizer allocator will barf over a gig or so + int64_t max_msg_size = 64 * 1024 * 1024; + TEST_ASSERT_SUCCESS_ERRNO ( + zmq_setsockopt (server, ZMQ_MAXMSGSIZE, &max_msg_size, sizeof (int64_t))); + bind_loopback_ipv4 (server, my_endpoint, sizeof (my_endpoint)); + fd_t client = connect_socket (my_endpoint); + + // If there is not enough data for a full greeting, just send what we can + // Otherwise send greeting first, as expected by the protocol + uint8_t buf[64]; + if (size >= 64) { + send (client, (void *) data, 64, MSG_NOSIGNAL); + data += 64; + size -= 64; + } + recv (client, buf, 64, MSG_DONTWAIT); + msleep (250); + for (ssize_t sent = 0; size > 0 && (sent != -1 || errno == EINTR); + size -= sent > 0 ? sent : 0, data += sent > 0 ? sent : 0) + sent = send (client, (const char *) data, size, MSG_NOSIGNAL); + msleep (250); + + zmq_msg_t msg; + zmq_msg_init (&msg); + while (-1 != zmq_msg_recv (&msg, server, ZMQ_DONTWAIT)) { + zmq_msg_close (&msg); + zmq_msg_init (&msg); + } + + void *client_good = test_context_socket (ZMQ_DEALER); + TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (client_good, my_endpoint)); + + TEST_ASSERT_EQUAL_INT (6, zmq_send_const (client_good, "HELLO", 6, 0)); + zmq_msg_t routing_id; + TEST_ASSERT_SUCCESS_ERRNO (zmq_msg_init (&routing_id)); + TEST_ASSERT_SUCCESS_ERRNO (zmq_msg_recv (&routing_id, server, 0)); + TEST_ASSERT_TRUE (zmq_msg_more (&routing_id)); + char const *peer_address = zmq_msg_gets (&routing_id, "Peer-Address"); + zmq_msg_close (&routing_id); + TEST_ASSERT_NOT_NULL (peer_address); + TEST_ASSERT_EQUAL_STRING ("127.0.0.1", peer_address); + TEST_ASSERT_EQUAL_INT ( + 0, TEST_ASSERT_SUCCESS_ERRNO (zmq_recv (server, buf, 64, 0))); + zmq_msg_init (&msg); + while (-1 != zmq_msg_recv (&msg, server, ZMQ_DONTWAIT)) { + zmq_msg_close (&msg); + zmq_msg_init (&msg); + } + + close (client); + test_context_socket_close_zero_linger (client_good); + test_context_socket_close_zero_linger (server); + teardown_test_context (); + + return 0; +} + +#ifndef ZMQ_USE_FUZZING_ENGINE +void test_bind_stream_fuzzer () +{ + uint8_t **data; + size_t *len, num_cases = 0; + if (fuzzer_corpus_encode ( + "tests/libzmq-fuzz-corpora/test_bind_stream_fuzzer_seed_corpus", + &data, &len, &num_cases) + != 0) + exit (77); + + while (num_cases-- > 0) { + TEST_ASSERT_SUCCESS_ERRNO ( + LLVMFuzzerTestOneInput (data[num_cases], len[num_cases])); + free (data[num_cases]); + } + + free (data); + free (len); +} + +int main (int argc, char **argv) +{ + setup_test_environment (); + + UNITY_BEGIN (); + RUN_TEST (test_bind_stream_fuzzer); + + return UNITY_END (); +} +#endif diff --git a/tests/test_connect_stream_fuzzer.cpp b/tests/test_connect_stream_fuzzer.cpp new file mode 100644 index 00000000..998d0154 --- /dev/null +++ b/tests/test_connect_stream_fuzzer.cpp @@ -0,0 +1,117 @@ +/* + Copyright (c) 2020 Contributors as noted in the AUTHORS file + + This file is part of libzmq, the ZeroMQ core engine in C++. + + libzmq is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + As a special exception, the Contributors give you permission to link + this library with independent modules to produce an executable, + regardless of the license terms of these independent modules, and to + copy and distribute the resulting executable under terms of your choice, + provided that you also meet, for each linked independent module, the + terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. + If you modify this library, you must extend this exception to your + version of the library. + + libzmq 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 Lesser General Public License + along with this program. If not, see . +*/ + +#ifdef ZMQ_USE_FUZZING_ENGINE +#include +#endif + +#include "testutil.hpp" +#include "testutil_unity.hpp" + +// Test that the ZMTP engine handles invalid handshake when connecting +// https://rfc.zeromq.org/spec/37/ +extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) +{ + setup_test_context (); + char my_endpoint[MAX_SOCKET_STRING]; + fd_t server = bind_socket_resolve_port ("127.0.0.1", "0", my_endpoint); + + void *client = test_context_socket (ZMQ_STREAM); + // As per API by default there's no limit to the size of a message, + // but the sanitizer allocator will barf over a gig or so + int64_t max_msg_size = 64 * 1024 * 1024; + TEST_ASSERT_SUCCESS_ERRNO ( + zmq_setsockopt (client, ZMQ_MAXMSGSIZE, &max_msg_size, sizeof (int64_t))); + TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (client, my_endpoint)); + + fd_t server_accept = + TEST_ASSERT_SUCCESS_RAW_ERRNO (accept (server, NULL, NULL)); + + // If there is not enough data for a full greeting, just send what we can + // Otherwise send greeting first, as expected by the protocol + uint8_t buf[64]; + if (size >= 64) { + send (server_accept, (void *) data, 64, MSG_NOSIGNAL); + data += 64; + size -= 64; + } + recv (server_accept, buf, 64, MSG_DONTWAIT); + msleep (250); + for (ssize_t sent = 0; size > 0 && (sent != -1 || errno == EINTR); + size -= sent > 0 ? sent : 0, data += sent > 0 ? sent : 0) + sent = send (server_accept, (const char *) data, size, MSG_NOSIGNAL); + msleep (250); + + zmq_msg_t msg; + zmq_msg_init (&msg); + while (-1 != zmq_msg_recv (&msg, client, ZMQ_DONTWAIT)) { + zmq_msg_close (&msg); + zmq_msg_init (&msg); + } + + close (server_accept); + close (server); + + test_context_socket_close_zero_linger (client); + teardown_test_context (); + + return 0; +} + +#ifndef ZMQ_USE_FUZZING_ENGINE +void test_connect_null_fuzzer () +{ + uint8_t **data; + size_t *len, num_cases = 0; + if (fuzzer_corpus_encode ( + "tests/libzmq-fuzz-corpora/test_connect_null_fuzzer_seed_corpus", + &data, &len, &num_cases) + != 0) + exit (77); + + while (num_cases-- > 0) { + TEST_ASSERT_SUCCESS_ERRNO ( + LLVMFuzzerTestOneInput (data[num_cases], len[num_cases])); + free (data[num_cases]); + } + + free (data); + free (len); +} + +int main (int argc, char **argv) +{ + setup_test_environment (); + + UNITY_BEGIN (); + RUN_TEST (test_connect_null_fuzzer); + + return UNITY_END (); +} +#endif