From 727637082f1427d239c389d8fcd330ef65fa8831 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sun, 26 Apr 2020 14:53:22 +0100 Subject: [PATCH 1/7] Problem: test_shutdown_stress_tipc fails sometimes Solution: create socket in the same thread where it is used --- tests/test_shutdown_stress_tipc.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/test_shutdown_stress_tipc.cpp b/tests/test_shutdown_stress_tipc.cpp index abcd2b6c..9d91ff03 100644 --- a/tests/test_shutdown_stress_tipc.cpp +++ b/tests/test_shutdown_stress_tipc.cpp @@ -43,12 +43,13 @@ void tearDown () #define THREAD_COUNT 100 extern "C" { -static void *worker (void *s_) +static void *worker (void *ctx) { - TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (s_, "tipc://{5560,0}@0.0.0")); + void *s = zmq_socket (ctx, ZMQ_SUB); + TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (s, "tipc://{5560,0}@0.0.0")); // Start closing the socket while the connecting process is underway. - TEST_ASSERT_SUCCESS_ERRNO (zmq_close (s_)); + TEST_ASSERT_SUCCESS_ERRNO (zmq_close (s)); return NULL; } @@ -57,7 +58,6 @@ static void *worker (void *s_) void test_shutdown_stress_tipc () { void *s1; - void *s2; int i; int j; pthread_t threads[THREAD_COUNT]; @@ -72,9 +72,8 @@ void test_shutdown_stress_tipc () TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (s1, "tipc://{5560,0,0}")); for (i = 0; i != THREAD_COUNT; i++) { - s2 = zmq_socket (get_test_context (), ZMQ_SUB); TEST_ASSERT_SUCCESS_RAW_ERRNO ( - pthread_create (&threads[i], NULL, worker, s2)); + pthread_create (&threads[i], NULL, worker, get_test_context ())); } for (i = 0; i != THREAD_COUNT; i++) { From c81a973cd895eef8e69328fdc2e96be0c20562ce Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sun, 26 Apr 2020 14:37:08 +0100 Subject: [PATCH 2/7] Problem: assert macros not detecting errors from syscall that do not return -1 on failure Solution: add a new TEST_ASSERT_SUCCESS_RAW_ZERO_ERRNO macro so that it can check explicitly for non-zero values. This will be used for getaddrinfo(). --- tests/testutil_unity.cpp | 24 ++++++++++++++++-------- tests/testutil_unity.hpp | 24 ++++++++++++++++++++---- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/tests/testutil_unity.cpp b/tests/testutil_unity.cpp index e35272de..1c273058 100644 --- a/tests/testutil_unity.cpp +++ b/tests/testutil_unity.cpp @@ -55,12 +55,10 @@ int test_assert_success_message_errno_helper (int rc_, return rc_; } -int test_assert_success_message_raw_errno_helper (int rc_, - const char *msg_, - const char *expr_, - int line_) +int test_assert_success_message_raw_errno_helper ( + int rc_, const char *msg_, const char *expr_, int line_, bool zero) { - if (rc_ == -1) { + if (rc_ == -1 || (zero && rc_ != 0)) { #if defined ZMQ_HAVE_WINDOWS int current_errno = WSAGetLastError (); #else @@ -70,14 +68,24 @@ int test_assert_success_message_raw_errno_helper (int rc_, char buffer[512]; buffer[sizeof (buffer) - 1] = 0; // to ensure defined behavior with VC++ <= 2013 - snprintf (buffer, sizeof (buffer) - 1, "%s failed%s%s%s, errno = %i", - expr_, msg_ ? " (additional info: " : "", msg_ ? msg_ : "", - msg_ ? ")" : "", current_errno); + snprintf ( + buffer, sizeof (buffer) - 1, "%s failed%s%s%s with %d, errno = %i/%s", + expr_, msg_ ? " (additional info: " : "", msg_ ? msg_ : "", + msg_ ? ")" : "", rc_, current_errno, strerror (current_errno)); UNITY_TEST_FAIL (line_, buffer); } return rc_; } +int test_assert_success_message_raw_zero_errno_helper (int rc_, + const char *msg_, + const char *expr_, + int line_) +{ + return test_assert_success_message_raw_errno_helper (rc_, msg_, expr_, + line_, true); +} + int test_assert_failure_message_raw_errno_helper ( int rc_, int expected_errno_, const char *msg_, const char *expr_, int line_) { diff --git a/tests/testutil_unity.hpp b/tests/testutil_unity.hpp index a682a49e..0be46db2 100644 --- a/tests/testutil_unity.hpp +++ b/tests/testutil_unity.hpp @@ -43,10 +43,13 @@ int test_assert_success_message_errno_helper (int rc_, const char *expr_, int line); -int test_assert_success_message_raw_errno_helper (int rc_, - const char *msg_, - const char *expr_, - int line); +int test_assert_success_message_raw_errno_helper ( + int rc_, const char *msg_, const char *expr_, int line, bool zero_ = false); + +int test_assert_success_message_raw_zero_errno_helper (int rc_, + const char *msg_, + const char *expr_, + int line); int test_assert_failure_message_raw_errno_helper ( int rc_, int expected_errno_, const char *msg_, const char *expr_, int line); @@ -88,9 +91,22 @@ int test_assert_failure_message_raw_errno_helper ( // A typical use would be: // TEST_ASSERT_SUCCESS_RAW_ERRNO (send (fd, buffer, 64, 0)); // In case of success, the result of the macro is the result of 'expr'. +// Success is strictly defined by a return value different from -1, as opposed +// to checking that it is 0, like TEST_ASSERT_FAILURE_RAW_ZERO_ERRNO does. #define TEST_ASSERT_SUCCESS_RAW_ERRNO(expr) \ test_assert_success_message_raw_errno_helper (expr, NULL, #expr, __LINE__) +// Asserts that the socket API 'expr' is successful. In case of a failure, the +// assertion message includes the literal 'expr' and the error code. +// A typical use would be: +// TEST_ASSERT_SUCCESS_RAW_ZERO_ERRNO (send (fd, buffer, 64, 0)); +// In case of success, the result of the macro is the result of 'expr'. +// Success is strictly defined by a return value of 0, as opposed to checking +// that it is not -1, like TEST_ASSERT_FAILURE_RAW_ERRNO does. +#define TEST_ASSERT_SUCCESS_RAW_ZERO_ERRNO(expr) \ + test_assert_success_message_raw_zero_errno_helper (expr, NULL, #expr, \ + __LINE__) + // Asserts that the socket API 'expr' is not successful, and the error code is // 'error_code'. In case of an unexpected succces, or a failure with an // unexpected error code, the assertion message includes the literal 'expr' From 4f35d1af1aeac59345aeec4390f920d3f3d3e1fa Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sat, 25 Apr 2020 19:53:44 +0100 Subject: [PATCH 3/7] Problem: using BSD sockets in test is duplicated across many tests Solution: refactor in testutil.lib, so that they can be used for fuzzers too --- tests/test_heartbeats.cpp | 16 +-- tests/test_mock_pub_sub.cpp | 31 +----- tests/test_security_curve.cpp | 45 +-------- tests/test_security_gssapi.cpp | 29 +----- tests/test_security_null.cpp | 19 +--- tests/test_security_plain.cpp | 32 +----- tests/test_socks.cpp | 19 +--- tests/test_stream_exceeds_buffer.cpp | 17 +--- tests/test_use_fd.cpp | 98 ++---------------- tests/testutil.cpp | 146 +++++++++++++++++++++++++++ tests/testutil.hpp | 24 +++++ 11 files changed, 196 insertions(+), 280 deletions(-) diff --git a/tests/test_heartbeats.cpp b/tests/test_heartbeats.cpp index 943b6792..b1dac3a1 100644 --- a/tests/test_heartbeats.cpp +++ b/tests/test_heartbeats.cpp @@ -226,21 +226,7 @@ static void test_heartbeat_timeout (int server_type_, int mock_ping_) prep_server_socket (!mock_ping_, 0, &server, &server_mon, my_endpoint, MAX_SOCKET_STRING, server_type_); - struct sockaddr_in ip4addr; - raw_socket s; - - ip4addr.sin_family = AF_INET; - ip4addr.sin_port = htons (atoi (strrchr (my_endpoint, ':') + 1)); -#if defined(ZMQ_HAVE_WINDOWS) && (_WIN32_WINNT < 0x0600) - ip4addr.sin_addr.s_addr = inet_addr ("127.0.0.1"); -#else - inet_pton (AF_INET, "127.0.0.1", &ip4addr.sin_addr); -#endif - - s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - rc = TEST_ASSERT_SUCCESS_RAW_ERRNO ( - connect (s, (struct sockaddr *) &ip4addr, sizeof ip4addr)); - TEST_ASSERT_GREATER_THAN_INT (-1, rc); + fd_t s = connect_socket (my_endpoint); // Mock a ZMTP 3 client so we can forcibly time out a connection mock_handshake (s, mock_ping_); diff --git a/tests/test_mock_pub_sub.cpp b/tests/test_mock_pub_sub.cpp index 6795bb7e..d1940d0f 100644 --- a/tests/test_mock_pub_sub.cpp +++ b/tests/test_mock_pub_sub.cpp @@ -19,17 +19,6 @@ #include "testutil.hpp" #include "testutil_unity.hpp" -#if defined(ZMQ_HAVE_WINDOWS) -#include -#include -#include -#define close closesocket -typedef SOCKET raw_socket; -#else -#include -#include -typedef int raw_socket; -#endif #include #include @@ -67,7 +56,7 @@ static int get_monitor_event (void *monitor_) return -1; } -static void recv_with_retry (raw_socket fd_, char *buffer_, int bytes_) +static void recv_with_retry (fd_t fd_, char *buffer_, int bytes_) { int received = 0; while (true) { @@ -81,7 +70,7 @@ static void recv_with_retry (raw_socket fd_, char *buffer_, int bytes_) } } -static void mock_handshake (raw_socket fd_, bool sub_command, bool mock_pub) +static void mock_handshake (fd_t fd_, bool sub_command, bool mock_pub) { const uint8_t zmtp_greeting[33] = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0x7f, 3, 0, 'N', 'U', 'L', 'L', 0}; @@ -158,21 +147,7 @@ static void test_mock_pub_sub (bool sub_command_, bool mock_pub_) prep_server_socket (&server, &server_mon, my_endpoint, MAX_SOCKET_STRING, mock_pub_ ? ZMQ_SUB : ZMQ_XPUB); - struct sockaddr_in ip4addr; - raw_socket s; - - ip4addr.sin_family = AF_INET; - ip4addr.sin_port = htons (atoi (strrchr (my_endpoint, ':') + 1)); -#if defined(ZMQ_HAVE_WINDOWS) && (_WIN32_WINNT < 0x0600) - ip4addr.sin_addr.s_addr = inet_addr ("127.0.0.1"); -#else - inet_pton (AF_INET, "127.0.0.1", &ip4addr.sin_addr); -#endif - - s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - rc = TEST_ASSERT_SUCCESS_RAW_ERRNO ( - connect (s, (struct sockaddr *) &ip4addr, sizeof ip4addr)); - TEST_ASSERT_GREATER_THAN_INT (-1, rc); + fd_t s = connect_socket (my_endpoint); // Mock a ZMTP 3 client so we can forcibly try sub commands mock_handshake (s, sub_command_, mock_pub_); diff --git a/tests/test_security_curve.cpp b/tests/test_security_curve.cpp index bcf8076b..796f7894 100644 --- a/tests/test_security_curve.cpp +++ b/tests/test_security_curve.cpp @@ -47,17 +47,6 @@ #include "testutil.hpp" #include "testutil_security.hpp" -#if defined(ZMQ_HAVE_WINDOWS) -#include -#include -#include -#define close closesocket -#else -#include -#include -#include -#include -#endif #include #include "../src/tweetnacl.h" @@ -223,34 +212,10 @@ void test_curve_security_with_plain_client_credentials () expect_zmtp_mechanism_mismatch (client, my_endpoint, server, server_mon); } -fd_t connect_vanilla_socket (char *my_endpoint_) -{ - fd_t s; - struct sockaddr_in ip4addr; - - unsigned short int port; - int rc = sscanf (my_endpoint_, "tcp://127.0.0.1:%hu", &port); - TEST_ASSERT_EQUAL_INT (1, rc); - - ip4addr.sin_family = AF_INET; - ip4addr.sin_port = htons (port); -#if defined(ZMQ_HAVE_WINDOWS) && (_WIN32_WINNT < 0x0600) - ip4addr.sin_addr.s_addr = inet_addr ("127.0.0.1"); -#else - inet_pton (AF_INET, "127.0.0.1", &ip4addr.sin_addr); -#endif - - s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - rc = connect (s, reinterpret_cast (&ip4addr), - sizeof (ip4addr)); - TEST_ASSERT_GREATER_THAN_INT (-1, rc); - return s; -} - void test_curve_security_unauthenticated_message () { // Unauthenticated messages from a vanilla socket shouldn't be received - fd_t s = connect_vanilla_socket (my_endpoint); + fd_t s = connect_socket (my_endpoint); // send anonymous ZMTP/1.0 greeting send (s, "\x01\x00", 2, 0); // send sneaky message that shouldn't be received @@ -288,7 +253,7 @@ void send_greeting (fd_t s_) void test_curve_security_invalid_hello_wrong_length () { - fd_t s = connect_vanilla_socket (my_endpoint); + fd_t s = connect_socket (my_endpoint); // send GREETING send_greeting (s); @@ -355,7 +320,7 @@ template void send_command (fd_t s_, char (&command_)[N]) void test_curve_security_invalid_hello_command_name () { - fd_t s = connect_vanilla_socket (my_endpoint); + fd_t s = connect_socket (my_endpoint); send_greeting (s); @@ -377,7 +342,7 @@ void test_curve_security_invalid_hello_command_name () void test_curve_security_invalid_hello_version () { - fd_t s = connect_vanilla_socket (my_endpoint); + fd_t s = connect_socket (my_endpoint); send_greeting (s); @@ -429,7 +394,7 @@ void recv_greeting (fd_t fd_) fd_t connect_exchange_greeting_and_send_hello ( char *my_endpoint_, zmq::curve_client_tools_t &tools_) { - fd_t s = connect_vanilla_socket (my_endpoint_); + fd_t s = connect_socket (my_endpoint_); send_greeting (s); recv_greeting (s); diff --git a/tests/test_security_gssapi.cpp b/tests/test_security_gssapi.cpp index 39d66f94..ea4de370 100644 --- a/tests/test_security_gssapi.cpp +++ b/tests/test_security_gssapi.cpp @@ -30,17 +30,6 @@ #include "testutil.hpp" #include "testutil_monitoring.hpp" #include "testutil_unity.hpp" -#if defined(ZMQ_HAVE_WINDOWS) -#include -#include -#include -#define close closesocket -#else -#include -#include -#include -#include -#endif #include #include @@ -253,23 +242,7 @@ void test_plain_creds () // Unauthenticated messages from a vanilla socket shouldn't be received void test_vanilla_socket () { - struct sockaddr_in ip4addr; - int s; - unsigned short int port; - int rc = sscanf (my_endpoint, "tcp://127.0.0.1:%hu", &port); - TEST_ASSERT_EQUAL_INT (1, rc); - ip4addr.sin_family = AF_INET; - ip4addr.sin_port = htons (port); -#if defined(ZMQ_HAVE_WINDOWS) && (_WIN32_WINNT < 0x0600) - ip4addr.sin_addr.s_addr = inet_addr ("127.0.0.1"); -#else - inet_pton (AF_INET, "127.0.0.1", &ip4addr.sin_addr); -#endif - - s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - rc = connect (s, reinterpret_cast (&ip4addr), - sizeof (ip4addr)); - TEST_ASSERT_GREATER_THAN (-1, rc); + fd_t s = connect_socket (my_endpoint); // send anonymous ZMTP/1.0 greeting send (s, "\x01\x00", 2, 0); // send sneaky message that shouldn't be received diff --git a/tests/test_security_null.cpp b/tests/test_security_null.cpp index 069c543c..d6e17a2d 100644 --- a/tests/test_security_null.cpp +++ b/tests/test_security_null.cpp @@ -169,25 +169,8 @@ void test_vanilla_socket () char my_endpoint[MAX_SOCKET_STRING]; bind_loopback_ipv4 (server, my_endpoint, sizeof my_endpoint); - struct sockaddr_in ip4addr; - fd_t s; + fd_t s = connect_socket (my_endpoint); - unsigned short int port; - int rc = sscanf (my_endpoint, "tcp://127.0.0.1:%hu", &port); - TEST_ASSERT_EQUAL_INT (1, rc); - - ip4addr.sin_family = AF_INET; - ip4addr.sin_port = htons (port); -#if defined(ZMQ_HAVE_WINDOWS) && (_WIN32_WINNT < 0x0600) - ip4addr.sin_addr.s_addr = inet_addr ("127.0.0.1"); -#else - inet_pton (AF_INET, "127.0.0.1", &ip4addr.sin_addr); -#endif - - s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - rc = connect (s, reinterpret_cast (&ip4addr), - sizeof ip4addr); - TEST_ASSERT_GREATER_THAN_INT (-1, rc); // send anonymous ZMTP/1.0 greeting send (s, "\x01\x00", 2, 0); // send sneaky message that shouldn't be received diff --git a/tests/test_security_plain.cpp b/tests/test_security_plain.cpp index acb3789c..7cfc53c9 100644 --- a/tests/test_security_plain.cpp +++ b/tests/test_security_plain.cpp @@ -30,18 +30,6 @@ #include "testutil.hpp" #include "testutil_unity.hpp" -#if defined(ZMQ_HAVE_WINDOWS) -#include -#include -#include -#define close closesocket -#else -#include -#include -#include -#include -#endif - #include #include @@ -195,25 +183,7 @@ void test_plain_wrong_credentials_fails () void test_plain_vanilla_socket () { // Unauthenticated messages from a vanilla socket shouldn't be received - struct sockaddr_in ip4addr; - fd_t s; - - unsigned short int port; - int rc = sscanf (my_endpoint, "tcp://127.0.0.1:%hu", &port); - TEST_ASSERT_EQUAL_INT (1, rc); - - ip4addr.sin_family = AF_INET; - ip4addr.sin_port = htons (port); -#if defined(ZMQ_HAVE_WINDOWS) && (_WIN32_WINNT < 0x0600) - ip4addr.sin_addr.s_addr = inet_addr ("127.0.0.1"); -#else - inet_pton (AF_INET, "127.0.0.1", &ip4addr.sin_addr); -#endif - - s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - rc = connect (s, reinterpret_cast (&ip4addr), - sizeof (ip4addr)); - TEST_ASSERT_GREATER_THAN_INT (-1, rc); + fd_t s = connect_socket (my_endpoint); // send anonymous ZMTP/1.0 greeting send (s, "\x01\x00", 2, 0); // send sneaky message that shouldn't be received diff --git a/tests/test_socks.cpp b/tests/test_socks.cpp index 89c3b933..396c76fb 100644 --- a/tests/test_socks.cpp +++ b/tests/test_socks.cpp @@ -105,21 +105,10 @@ void *setup_socks_server (char *socks_server_address, int socks_server_address_len) { fprintf (stderr, "socks_server: setup socks server\n"); - int server_fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - TEST_ASSERT_NOT_EQUAL (-1, server_fd); - int flag = 1; - int res; -#ifdef _WIN32 - res = setsockopt (server_fd, SOL_SOCKET, SO_REUSEADDR, (const char *) &flag, - sizeof (int)); -#else - res = setsockopt (server_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int)); -#endif - TEST_ASSERT_SUCCESS_RAW_ERRNO (res); - struct sockaddr_in saddr = bind_bsd_socket (server_fd); - int nbytes = snprintf (socks_server_address, socks_server_address_len, - "127.0.0.1:%d", ntohs (saddr.sin_port)); - TEST_ASSERT (nbytes >= 0 && nbytes < socks_server_address_len); + int server_fd = + bind_socket_resolve_port ("127.0.0.1", "0", socks_server_address); + memmove (socks_server_address, strchr (socks_server_address, '/') + 2, + strlen (strchr (socks_server_address, '/') + 1)); fprintf (stderr, "socks_server: bound to: tcp://%s\n", socks_server_address); return (void *) (intptr_t) server_fd; diff --git a/tests/test_stream_exceeds_buffer.cpp b/tests/test_stream_exceeds_buffer.cpp index 767946d7..e8478ad4 100644 --- a/tests/test_stream_exceeds_buffer.cpp +++ b/tests/test_stream_exceeds_buffer.cpp @@ -32,12 +32,6 @@ #include -#ifndef _WIN32 -#include -#include -#include -#endif - SETUP_TEARDOWN_TESTCONTEXT void test_stream_exceeds_buffer () @@ -47,16 +41,7 @@ void test_stream_exceeds_buffer () unsigned char rcvbuf[msgsize]; char my_endpoint[MAX_SOCKET_STRING]; - int server_sock = - TEST_ASSERT_SUCCESS_RAW_ERRNO (socket (AF_INET, SOCK_STREAM, 0)); - int enable = 1; - TEST_ASSERT_SUCCESS_RAW_ERRNO (setsockopt (server_sock, SOL_SOCKET, - SO_REUSEADDR, (char *) &enable, - sizeof (enable))); - struct sockaddr_in saddr = bind_bsd_socket (server_sock); - TEST_ASSERT_SUCCESS_RAW_ERRNO (listen (server_sock, 1)); - - sprintf (my_endpoint, "tcp://127.0.0.1:%d", ntohs (saddr.sin_port)); + int server_sock = bind_socket_resolve_port ("127.0.0.1", "0", my_endpoint); void *zsock = test_context_socket (ZMQ_STREAM); TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (zsock, my_endpoint)); diff --git a/tests/test_use_fd.cpp b/tests/test_use_fd.cpp index 98de2199..f15ffea0 100644 --- a/tests/test_use_fd.cpp +++ b/tests/test_use_fd.cpp @@ -37,33 +37,12 @@ SETUP_TEARDOWN_TESTCONTEXT #if !defined(ZMQ_HAVE_WINDOWS) -#include -#include -#include -#include -int setup_socket_and_set_fd (void *zmq_socket_, - int af_, - int protocol_, - const sockaddr *addr_, - size_t addr_len_) +void pre_allocate_sock_tcp (void *socket_, char *my_endpoint_) { - const int s_pre = - TEST_ASSERT_SUCCESS_ERRNO (socket (af_, SOCK_STREAM, protocol_)); - - if (af_ == AF_INET) { - int flag = 1; - TEST_ASSERT_SUCCESS_ERRNO ( - setsockopt (s_pre, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int))); - } - - TEST_ASSERT_SUCCESS_ERRNO (bind (s_pre, addr_, addr_len_)); - TEST_ASSERT_SUCCESS_ERRNO (listen (s_pre, SOMAXCONN)); - + fd_t s = bind_socket_resolve_port ("127.0.0.1", "0", my_endpoint_); TEST_ASSERT_SUCCESS_ERRNO ( - zmq_setsockopt (zmq_socket_, ZMQ_USE_FD, &s_pre, sizeof (s_pre))); - - return s_pre; + zmq_setsockopt (socket_, ZMQ_USE_FD, &s, sizeof (s))); } typedef void (*pre_allocate_sock_fun_t) (void *, char *); @@ -166,41 +145,6 @@ void test_client_server (pre_allocate_sock_fun_t pre_allocate_sock_fun_) #endif } -uint16_t pre_allocate_sock_tcp_int (void *zmq_socket_, - const char *address_, - const char *port_) -{ - struct addrinfo *addr, hint; - hint.ai_flags = 0; - hint.ai_family = AF_INET; - hint.ai_socktype = SOCK_STREAM; - hint.ai_protocol = IPPROTO_TCP; - hint.ai_addrlen = 0; - hint.ai_canonname = NULL; - hint.ai_addr = NULL; - hint.ai_next = NULL; - - TEST_ASSERT_SUCCESS_ERRNO (getaddrinfo (address_, port_, &hint, &addr)); - - const int s_pre = setup_socket_and_set_fd ( - zmq_socket_, AF_INET, IPPROTO_TCP, addr->ai_addr, addr->ai_addrlen); - - struct sockaddr_in sin; - socklen_t len = sizeof (sin); - TEST_ASSERT_SUCCESS_ERRNO ( - getsockname (s_pre, (struct sockaddr *) &sin, &len)); - - freeaddrinfo (addr); - - return ntohs (sin.sin_port); -} - -void pre_allocate_sock_tcp (void *socket_, char *my_endpoint_) -{ - const uint16_t port = pre_allocate_sock_tcp_int (socket_, "127.0.0.1", "0"); - sprintf (my_endpoint_, "tcp://127.0.0.1:%u", port); -} - void test_req_rep_tcp () { test_req_rep (pre_allocate_sock_tcp); @@ -218,38 +162,14 @@ void test_client_server_tcp () #endif } -void pre_allocate_sock_ipc_int (void *zmq_socket_, const char *path_) -{ - struct sockaddr_un addr; - addr.sun_family = AF_UNIX; - strcpy (addr.sun_path, path_); - - // TODO check return value of unlink - unlink (path_); - - setup_socket_and_set_fd (zmq_socket_, AF_UNIX, 0, - reinterpret_cast (&addr), - sizeof (struct sockaddr_un)); -} - -char ipc_endpoint[16]; +char ipc_endpoint[MAX_SOCKET_STRING] = ""; void pre_allocate_sock_ipc (void *sb_, char *my_endpoint_) { - strcpy (ipc_endpoint, "tmpXXXXXX"); - -#ifdef HAVE_MKDTEMP - TEST_ASSERT_TRUE (mkdtemp (ipc_endpoint)); - strcat (ipc_endpoint, "/ipc"); -#else - int fd = mkstemp (ipc_endpoint); - TEST_ASSERT_TRUE (fd != -1); - close (fd); -#endif - - pre_allocate_sock_ipc_int (sb_, ipc_endpoint); - strcpy (my_endpoint_, "ipc://"); - strcat (my_endpoint_, ipc_endpoint); + fd_t s = bind_socket_resolve_port ("", "", my_endpoint_, AF_UNIX, 0); + TEST_ASSERT_SUCCESS_ERRNO ( + zmq_setsockopt (sb_, ZMQ_USE_FD, &s, sizeof (s))); + strcpy (ipc_endpoint, strchr (my_endpoint_, '/') + 2); } void test_req_rep_ipc () @@ -277,7 +197,7 @@ void test_client_server_ipc () int main () { - setup_test_environment (); + setup_test_environment (0); UNITY_BEGIN (); RUN_TEST (test_req_rep_tcp); diff --git a/tests/testutil.cpp b/tests/testutil.cpp index c63cfabf..c602b0ca 100644 --- a/tests/testutil.cpp +++ b/tests/testutil.cpp @@ -35,6 +35,10 @@ #if defined _WIN32 #include "../src/windows.hpp" #if defined _MSC_VER +#if defined ZMQ_HAVE_IPC +#include +#include +#endif #include #pragma warning(disable : 4996) // iphlpapi is needed for if_nametoindex (not on Windows XP) @@ -55,6 +59,7 @@ #include #include #include +#include #if defined(ZMQ_HAVE_AIX) #include #include @@ -365,6 +370,147 @@ sockaddr_in bind_bsd_socket (int socket_) return saddr; } +fd_t connect_socket (const char *endpoint_, const int af_, const int protocol_) +{ + struct sockaddr_storage addr; + // OSX is very opinionated and wants the size to match the AF family type + socklen_t addr_len = sizeof (addr); + const fd_t s_pre = socket (af_, SOCK_STREAM, protocol_); + TEST_ASSERT_NOT_EQUAL (-1, s_pre); + + if (af_ == AF_INET || af_ == AF_INET6) { + const char *port = strrchr (endpoint_, ':') + 1; + char address[MAX_SOCKET_STRING]; + // getaddrinfo does not like [x:y::z] + if (*strchr (endpoint_, '/') + 2 == '[') { + strcpy (address, strchr (endpoint_, '[') + 1); + address[strlen (address) - strlen (port) - 2] = '\0'; + } else { + strcpy (address, strchr (endpoint_, '/') + 2); + address[strlen (address) - strlen (port) - 1] = '\0'; + } + + struct addrinfo *in, hint; + hint.ai_flags = AI_NUMERICSERV; + hint.ai_family = af_; + hint.ai_socktype = SOCK_STREAM; + hint.ai_protocol = protocol_; + hint.ai_addrlen = 0; + hint.ai_canonname = NULL; + hint.ai_addr = NULL; + hint.ai_next = NULL; + + TEST_ASSERT_SUCCESS_RAW_ZERO_ERRNO ( + getaddrinfo (address, port, &hint, &in)); + TEST_ASSERT_NOT_NULL (in); + memcpy (&addr, in->ai_addr, in->ai_addrlen); + addr_len = (socklen_t) in->ai_addrlen; + freeaddrinfo (in); + } +#if defined(ZMQ_HAVE_IPC) + else { + struct sockaddr_un *un_addr = (struct sockaddr_un *) &addr; + addr_len = sizeof (struct sockaddr_un); + un_addr->sun_family = AF_UNIX; + strcpy (un_addr->sun_path, endpoint_); + } +#endif + + TEST_ASSERT_SUCCESS_RAW_ERRNO ( + connect (s_pre, (struct sockaddr *) &addr, addr_len)); + + return s_pre; +} + +fd_t bind_socket_resolve_port (const char *address_, + const char *port_, + char *my_endpoint_, + const int af_, + const int protocol_) +{ + struct sockaddr_storage addr; + // OSX is very opinionated and wants the size to match the AF family type + socklen_t addr_len = sizeof (addr); + const fd_t s_pre = socket (af_, SOCK_STREAM, protocol_); + TEST_ASSERT_NOT_EQUAL (-1, s_pre); + + if (af_ == AF_INET || af_ == AF_INET6) { +#ifdef ZMQ_HAVE_WINDOWS + const char flag = '\1'; +#elif defined ZMQ_HAVE_VXWORKS + char flag = '\1'; +#else + int flag = 1; +#endif + struct addrinfo *in, hint; + hint.ai_flags = AI_NUMERICSERV; + hint.ai_family = af_; + hint.ai_socktype = protocol_ == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM; + hint.ai_protocol = protocol_; + hint.ai_addrlen = 0; + hint.ai_canonname = NULL; + hint.ai_addr = NULL; + hint.ai_next = NULL; + + TEST_ASSERT_SUCCESS_RAW_ERRNO ( + setsockopt (s_pre, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int))); + TEST_ASSERT_SUCCESS_RAW_ZERO_ERRNO ( + getaddrinfo (address_, port_, &hint, &in)); + TEST_ASSERT_NOT_NULL (in); + memcpy (&addr, in->ai_addr, in->ai_addrlen); + addr_len = (socklen_t) in->ai_addrlen; + freeaddrinfo (in); + } +#if defined(ZMQ_HAVE_IPC) + else { + struct sockaddr_un *un_addr = (struct sockaddr_un *) &addr; + addr_len = sizeof (struct sockaddr_un); + un_addr->sun_family = AF_UNIX; +#if defined ZMQ_HAVE_WINDOWS + char buffer[MAX_PATH] = ""; + + TEST_ASSERT_SUCCESS_RAW_ERRNO (tmpnam_s (buffer)); + TEST_ASSERT_SUCCESS_RAW_ERRNO (_mkdir (buffer)); + strcat (buffer, "/ipc"); +#else + char buffer[PATH_MAX] = ""; + strcpy (buffer, "tmpXXXXXX"); +#ifdef HAVE_MKDTEMP + TEST_ASSERT_TRUE (mkdtemp (buffer)); + strcat (buffer, "/socket"); +#else + int fd = mkstemp (buffer); + TEST_ASSERT_TRUE (fd != -1); + close (fd); +#endif +#endif + strcpy (un_addr->sun_path, buffer); + memcpy (my_endpoint_, "ipc://", 7); + strcat (my_endpoint_, buffer); + + // TODO check return value of unlink + unlink (buffer); + } +#endif + + TEST_ASSERT_SUCCESS_RAW_ERRNO ( + bind (s_pre, (struct sockaddr *) &addr, addr_len)); + TEST_ASSERT_SUCCESS_RAW_ERRNO (listen (s_pre, SOMAXCONN)); + + if (af_ == AF_INET || af_ == AF_INET6) { + addr_len = sizeof (struct sockaddr_storage); + TEST_ASSERT_SUCCESS_RAW_ERRNO ( + getsockname (s_pre, (struct sockaddr *) &addr, &addr_len)); + sprintf (my_endpoint_, "%s://%s:%u", + protocol_ == IPPROTO_TCP ? "tcp" : "udp", address_, + af_ == AF_INET + ? ntohs (((struct sockaddr_in *) &addr)->sin_port) + : ntohs (((struct sockaddr_in6 *) &addr)->sin6_port)); + } + + return s_pre; +} + bool streq (const char *lhs_, const char *rhs_) { return strcmp (lhs_, rhs_) == 0; diff --git a/tests/testutil.hpp b/tests/testutil.hpp index 6661f33b..ab009c70 100644 --- a/tests/testutil.hpp +++ b/tests/testutil.hpp @@ -38,6 +38,14 @@ #include "../include/zmq.h" #include "../src/stdint.hpp" +// For AF_INET and IPPROTO_TCP +#if defined _WIN32 +#include "../src/windows.hpp" +#else +#include +#include +#endif + // This defines the settle time used in tests; raise this if we // get test failures on slower systems due to binds/connects not // settled. Tested to work reliably at 1 msec on a fast PC. @@ -172,4 +180,20 @@ int test_inet_pton (int af_, const char *src_, void *dst_); // Binds an ipv4 BSD socket to an ephemeral port, returns the compiled sockaddr struct sockaddr_in bind_bsd_socket (int socket); +// Connects a BSD socket to the ZMQ endpoint. Works with ipv4/ipv6/unix. +fd_t connect_socket (const char *endpoint_, + const int af_ = AF_INET, + const int protocol_ = IPPROTO_TCP); + +// Binds a BSD socket to an ephemeral port, returns the file descriptor. +// The resulting ZMQ endpoint will be stored in my_endpoint, including the protocol +// prefix, so ensure it is writable and of appropriate size. +// Works with ipv4/ipv6/unix. With unix sockets address_/port_ can be empty and +// my_endpoint_ will contain a random path. +fd_t bind_socket_resolve_port (const char *address_, + const char *port_, + char *my_endpoint_, + const int af_ = AF_INET, + const int protocol_ = IPPROTO_TCP); + #endif From c6d3bc68c832d6a7830bd7c9bb623b75422de0b9 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sun, 26 Apr 2020 13:33:32 +0100 Subject: [PATCH 4/7] Problem: ZMTP mocks duplicated across tests Solution: define buffers in common header --- tests/test_heartbeats.cpp | 25 ++++++++++--------------- tests/test_mock_pub_sub.cpp | 26 ++++++++++---------------- tests/test_security_curve.cpp | 17 ++++++----------- tests/testutil.hpp | 21 +++++++++++++++++++++ 4 files changed, 47 insertions(+), 42 deletions(-) diff --git a/tests/test_heartbeats.cpp b/tests/test_heartbeats.cpp index b1dac3a1..420dd33b 100644 --- a/tests/test_heartbeats.cpp +++ b/tests/test_heartbeats.cpp @@ -91,29 +91,24 @@ static void recv_with_retry (raw_socket fd_, char *buffer_, int bytes_) static void mock_handshake (raw_socket fd_, int mock_ping_) { - const uint8_t zmtp_greeting[33] = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, - 0x7f, 3, 0, 'N', 'U', 'L', 'L', 0}; char buffer[128]; memset (buffer, 0, sizeof (buffer)); - memcpy (buffer, zmtp_greeting, sizeof (zmtp_greeting)); + memcpy (buffer, zmtp_greeting_null, sizeof (zmtp_greeting_null)); - int rc = TEST_ASSERT_SUCCESS_RAW_ERRNO (send (fd_, buffer, 64, 0)); - TEST_ASSERT_EQUAL_INT (64, rc); + int rc = TEST_ASSERT_SUCCESS_RAW_ERRNO ( + send (fd_, buffer, sizeof (zmtp_greeting_null), 0)); + TEST_ASSERT_EQUAL_INT (sizeof (zmtp_greeting_null), rc); - recv_with_retry (fd_, buffer, 64); - - const uint8_t zmtp_ready[43] = { - 4, 41, 5, 'R', 'E', 'A', 'D', 'Y', 11, 'S', 'o', 'c', 'k', 'e', 't', - '-', 'T', 'y', 'p', 'e', 0, 0, 0, 6, 'D', 'E', 'A', 'L', 'E', 'R', - 8, 'I', 'd', 'e', 'n', 't', 'i', 't', 'y', 0, 0, 0, 0}; + recv_with_retry (fd_, buffer, sizeof (zmtp_greeting_null)); memset (buffer, 0, sizeof (buffer)); - memcpy (buffer, zmtp_ready, 43); - rc = TEST_ASSERT_SUCCESS_RAW_ERRNO (send (fd_, buffer, 43, 0)); - TEST_ASSERT_EQUAL_INT (43, rc); + memcpy (buffer, zmtp_ready_dealer, sizeof (zmtp_ready_dealer)); + rc = TEST_ASSERT_SUCCESS_RAW_ERRNO ( + send (fd_, buffer, sizeof (zmtp_ready_dealer), 0)); + TEST_ASSERT_EQUAL_INT (sizeof (zmtp_ready_dealer), rc); // greeting - recv_with_retry (fd_, buffer, 43); + recv_with_retry (fd_, buffer, sizeof (zmtp_ready_dealer)); if (mock_ping_) { // test PING context - should be replicated in the PONG diff --git a/tests/test_mock_pub_sub.cpp b/tests/test_mock_pub_sub.cpp index d1940d0f..da0d60b2 100644 --- a/tests/test_mock_pub_sub.cpp +++ b/tests/test_mock_pub_sub.cpp @@ -72,11 +72,9 @@ static void recv_with_retry (fd_t fd_, char *buffer_, int bytes_) static void mock_handshake (fd_t fd_, bool sub_command, bool mock_pub) { - const uint8_t zmtp_greeting[33] = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, - 0x7f, 3, 0, 'N', 'U', 'L', 'L', 0}; char buffer[128]; memset (buffer, 0, sizeof (buffer)); - memcpy (buffer, zmtp_greeting, sizeof (zmtp_greeting)); + memcpy (buffer, zmtp_greeting_null, sizeof (zmtp_greeting_null)); // Mock ZMTP 3.1 which uses commands if (sub_command) { @@ -88,24 +86,20 @@ static void mock_handshake (fd_t fd_, bool sub_command, bool mock_pub) recv_with_retry (fd_, buffer, 64); if (!mock_pub) { - const uint8_t zmtp_ready[27] = { - 4, 25, 5, 'R', 'E', 'A', 'D', 'Y', 11, 'S', 'o', 'c', 'k', 'e', - 't', '-', 'T', 'y', 'p', 'e', 0, 0, 0, 3, 'S', 'U', 'B'}; - rc = TEST_ASSERT_SUCCESS_RAW_ERRNO ( - send (fd_, (const char *) zmtp_ready, 27, 0)); - TEST_ASSERT_EQUAL_INT (27, rc); + rc = TEST_ASSERT_SUCCESS_RAW_ERRNO (send ( + fd_, (const char *) zmtp_ready_sub, sizeof (zmtp_ready_sub), 0)); + TEST_ASSERT_EQUAL_INT (sizeof (zmtp_ready_sub), rc); } else { - const uint8_t zmtp_ready[28] = { - 4, 26, 5, 'R', 'E', 'A', 'D', 'Y', 11, 'S', 'o', 'c', 'k', 'e', - 't', '-', 'T', 'y', 'p', 'e', 0, 0, 0, 4, 'X', 'P', 'U', 'B'}; - rc = TEST_ASSERT_SUCCESS_RAW_ERRNO ( - send (fd_, (const char *) zmtp_ready, 28, 0)); - TEST_ASSERT_EQUAL_INT (28, rc); + rc = TEST_ASSERT_SUCCESS_RAW_ERRNO (send ( + fd_, (const char *) zmtp_ready_xpub, sizeof (zmtp_ready_xpub), 0)); + TEST_ASSERT_EQUAL_INT (sizeof (zmtp_ready_xpub), rc); } // greeting - XPUB has one extra byte memset (buffer, 0, sizeof (buffer)); - recv_with_retry (fd_, buffer, mock_pub ? 27 : 28); + recv_with_retry (fd_, buffer, + mock_pub ? sizeof (zmtp_ready_sub) + : sizeof (zmtp_ready_xpub)); } static void prep_server_socket (void **server_out_, diff --git a/tests/test_security_curve.cpp b/tests/test_security_curve.cpp index 796f7894..45de7581 100644 --- a/tests/test_security_curve.cpp +++ b/tests/test_security_curve.cpp @@ -242,21 +242,16 @@ template void send (fd_t fd_, const char (&data_)[N]) send_all (fd_, data_, N - 1); } -void send_greeting (fd_t s_) +template void send (fd_t fd_, const uint8_t (&data_)[N]) { - send (s_, "\xff\0\0\0\0\0\0\0\0\x7f"); // signature - send (s_, "\x03\x00"); // version 3.0 - send (s_, "CURVE\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); // mechanism CURVE - send (s_, "\0"); // as-server == false - send (s_, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); + send_all (fd_, reinterpret_cast (&data_), N); } void test_curve_security_invalid_hello_wrong_length () { fd_t s = connect_socket (my_endpoint); - // send GREETING - send_greeting (s); + send (s, zmtp_greeting_curve); // send CURVE HELLO of wrong size send (s, "\x04\x06\x05HELLO"); @@ -322,7 +317,7 @@ void test_curve_security_invalid_hello_command_name () { fd_t s = connect_socket (my_endpoint); - send_greeting (s); + send (s, zmtp_greeting_curve); zmq::curve_client_tools_t tools = make_curve_client_tools (); @@ -344,7 +339,7 @@ void test_curve_security_invalid_hello_version () { fd_t s = connect_socket (my_endpoint); - send_greeting (s); + send (s, zmtp_greeting_curve); zmq::curve_client_tools_t tools = make_curve_client_tools (); @@ -396,7 +391,7 @@ fd_t connect_exchange_greeting_and_send_hello ( { fd_t s = connect_socket (my_endpoint_); - send_greeting (s); + send (s, zmtp_greeting_curve); recv_greeting (s); // send valid CURVE HELLO diff --git a/tests/testutil.hpp b/tests/testutil.hpp index ab009c70..147584f4 100644 --- a/tests/testutil.hpp +++ b/tests/testutil.hpp @@ -65,6 +65,27 @@ #define ENDPOINT_5 "udp://127.0.0.1:5560" #define PORT_6 5561 +// For tests that mock ZMTP +const uint8_t zmtp_greeting_null[64] = { + 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0x7f, 3, 0, 'N', 'U', 'L', 'L', + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +const uint8_t zmtp_greeting_curve[64] = { + 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0x7f, 3, 0, 'C', 'U', 'R', 'V', + 'E', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +const uint8_t zmtp_ready_dealer[43] = { + 4, 41, 5, 'R', 'E', 'A', 'D', 'Y', 11, 'S', 'o', 'c', 'k', 'e', 't', + '-', 'T', 'y', 'p', 'e', 0, 0, 0, 6, 'D', 'E', 'A', 'L', 'E', 'R', + 8, 'I', 'd', 'e', 'n', 't', 'i', 't', 'y', 0, 0, 0, 0}; +const uint8_t zmtp_ready_xpub[28] = { + 4, 26, 5, 'R', 'E', 'A', 'D', 'Y', 11, 'S', 'o', 'c', 'k', 'e', + 't', '-', 'T', 'y', 'p', 'e', 0, 0, 0, 4, 'X', 'P', 'U', 'B'}; +const uint8_t zmtp_ready_sub[27] = { + 4, 25, 5, 'R', 'E', 'A', 'D', 'Y', 11, 'S', 'o', 'c', 'k', 'e', + 't', '-', 'T', 'y', 'p', 'e', 0, 0, 0, 3, 'S', 'U', 'B'}; + #undef NDEBUG // duplicated from fd.hpp From 8e774f3cd64ab37e16b0abd3a12d06e6829654bb Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sat, 25 Apr 2020 13:53:53 +0100 Subject: [PATCH 5/7] Problem: no integration with fuzz testing Solution: port the 2 new tests from oss-fuzz, and wire them up to be ran manually with a static input in normal builds. Add a specific configure option to use the external fuzzing engine from oss-fuzz. --- Makefile.am | 35 +++++++++++++ configure.ac | 19 +++++++ tests/CMakeLists.txt | 2 +- tests/test_bind_null_fuzzer.cpp | 76 ++++++++++++++++++++++++++++ tests/test_connect_null_fuzzer.cpp | 80 ++++++++++++++++++++++++++++++ tests/testutil.hpp | 4 ++ 6 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 tests/test_bind_null_fuzzer.cpp create mode 100644 tests/test_connect_null_fuzzer.cpp diff --git a/Makefile.am b/Makefile.am index d70058df..47475bca 100755 --- a/Makefile.am +++ b/Makefile.am @@ -1106,6 +1106,41 @@ tests_test_disconnect_msg_LDADD = ${TESTUTIL_LIBS} src/libzmq.la tests_test_disconnect_msg_CPPFLAGS = ${TESTUTIL_CPPFLAGS} endif +if FUZZING_ENGINE_LIB +fuzzer_apps = tests/test_bind_null_fuzzer \ + tests/test_connect_null_fuzzer + +tests_test_bind_null_fuzzer_DEPENDENCIES = src/libzmq.la +tests_test_bind_null_fuzzer_SOURCES = tests/test_bind_null_fuzzer.cpp +tests_test_bind_null_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} \ + $(top_builddir)/src/.libs/libzmq.a \ + ${src_libzmq_la_LIBADD} +tests_test_bind_null_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_bind_null_fuzzer_CXXFLAGS = -std=c++11 + +tests_test_connect_null_fuzzer_DEPENDENCIES = src/libzmq.la +tests_test_connect_null_fuzzer_SOURCES = tests/test_connect_null_fuzzer.cpp +tests_test_connect_null_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} \ + $(top_builddir)/src/.libs/libzmq.a \ + ${src_libzmq_la_LIBADD} +tests_test_connect_null_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_connect_null_fuzzer_CXXFLAGS = -std=c++11 + +FUZZINGdir = ${prefix}/${FUZZING_INSTALLDIR} +FUZZING_PROGRAMS = ${fuzzer_apps} +else +test_apps += tests/test_bind_null_fuzzer \ + tests/test_connect_null_fuzzer + +tests_test_bind_null_fuzzer_SOURCES = tests/test_bind_null_fuzzer.cpp +tests_test_bind_null_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la +tests_test_bind_null_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} + +tests_test_connect_null_fuzzer_SOURCES = tests/test_connect_null_fuzzer.cpp +tests_test_connect_null_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la +tests_test_connect_null_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +endif + if ENABLE_STATIC # unit tests - these include individual source files and test the internal functions test_apps += \ diff --git a/configure.ac b/configure.ac index aa532a77..ab0ed176 100644 --- a/configure.ac +++ b/configure.ac @@ -1005,6 +1005,25 @@ AM_CONDITIONAL([WITH_CLANG_FORMAT], [$WITH_CLANG_FORMAT]) # unittests will not build without the static libzmq.a AM_CONDITIONAL(ENABLE_STATIC, test "x$enable_static" = "xyes") +# build using a fuzzing engine - fuzzers will be built separately and statically +AC_ARG_WITH([fuzzing-engine], [AS_HELP_STRING([--with-fuzzing-engine], + [build libzmq with an exernal fuzzing engine [default=no]])], + [have_fuzzing_engine_ext=$withval], + [have_fuzzing_engine_ext=no]) +AC_ARG_WITH([fuzzing-installdir], + AS_HELP_STRING([--with-fuzzing-installdir=PATH], + [Path where to install fuzzer binaries]), + [fuzzing_installdir="$withval"]) + +if test "x$have_fuzzing_engine_ext" != "xno" && test "x$fuzzing_installdir" != "x" && test "x$enable_static" = "xyes"; then + FUZZING_ENGINE_LIB="${have_fuzzing_engine_ext}" + FUZZING_INSTALLDIR="${fuzzing_installdir}" + AC_SUBST(FUZZING_ENGINE_LIB) + AC_SUBST(FUZZING_INSTALLDIR) + AC_DEFINE(ZMQ_USE_FUZZING_ENGINE, 1, [fuzz tests will be built with fuzzing engine]) +fi +AM_CONDITIONAL(FUZZING_ENGINE_LIB, test "x$FUZZING_ENGINE_LIB" != "x") + # clang 6 has a warning that does not make sense on multi-platform code AC_LANG_PUSH([C]) AX_CHECK_COMPILE_FLAG([-Wno-tautological-constant-compare], diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 10e91ed4..a77b52b4 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -75,7 +75,7 @@ set(tests test_mock_pub_sub) if(NOT WIN32) - list(APPEND tests test_security_gssapi test_socks) + list(APPEND tests test_security_gssapi test_socks test_connect_null_fuzzer test_bind_null_fuzzer) endif() if(ZMQ_HAVE_CURVE) diff --git a/tests/test_bind_null_fuzzer.cpp b/tests/test_bind_null_fuzzer.cpp new file mode 100644 index 00000000..b94718e2 --- /dev/null +++ b/tests/test_bind_null_fuzzer.cpp @@ -0,0 +1,76 @@ +/* + 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_PUB); + bind_loopback_ipv4 (server, my_endpoint, sizeof (my_endpoint)); + fd_t client = connect_socket (my_endpoint); + + 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); + + close (client); + + test_context_socket_close_zero_linger (server); + teardown_test_context (); + + return 0; +} + +#ifndef ZMQ_USE_FUZZING_ENGINE +void test_bind_null_fuzzer () +{ + TEST_ASSERT_SUCCESS_ERRNO ( + LLVMFuzzerTestOneInput (zmtp_greeting_null, sizeof (zmtp_greeting_null))); +} + +int main (int argc, char **argv) +{ + setup_test_environment (); + + UNITY_BEGIN (); + RUN_TEST (test_bind_null_fuzzer); + + return UNITY_END (); +} +#endif diff --git a/tests/test_connect_null_fuzzer.cpp b/tests/test_connect_null_fuzzer.cpp new file mode 100644 index 00000000..e078b49f --- /dev/null +++ b/tests/test_connect_null_fuzzer.cpp @@ -0,0 +1,80 @@ +/* + 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_PUB); + TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (client, my_endpoint)); + + fd_t server_accept = + TEST_ASSERT_SUCCESS_RAW_ERRNO (accept (server, NULL, NULL)); + 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); + + 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 () +{ + TEST_ASSERT_SUCCESS_ERRNO ( + LLVMFuzzerTestOneInput (zmtp_greeting_null, sizeof (zmtp_greeting_null))); +} + +int main (int argc, char **argv) +{ + setup_test_environment (); + + UNITY_BEGIN (); + RUN_TEST (test_connect_null_fuzzer); + + return UNITY_END (); +} +#endif diff --git a/tests/testutil.hpp b/tests/testutil.hpp index 147584f4..00c23038 100644 --- a/tests/testutil.hpp +++ b/tests/testutil.hpp @@ -88,6 +88,10 @@ const uint8_t zmtp_ready_sub[27] = { #undef NDEBUG +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + // duplicated from fd.hpp #ifdef ZMQ_HAVE_WINDOWS #ifndef NOMINMAX From 57df836381d5a8e19a3368ef32ada810c0d2958e Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sun, 26 Apr 2020 21:59:28 +0100 Subject: [PATCH 6/7] Problem: no fuzz testing for CURVE Solution: add simple, initial tests for client and server --- Makefile.am | 32 +++++++++++ tests/CMakeLists.txt | 3 + tests/test_bind_curve_fuzzer.cpp | 82 ++++++++++++++++++++++++++++ tests/test_connect_curve_fuzzer.cpp | 85 +++++++++++++++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 tests/test_bind_curve_fuzzer.cpp create mode 100644 tests/test_connect_curve_fuzzer.cpp diff --git a/Makefile.am b/Makefile.am index 47475bca..acf4ba3e 100755 --- a/Makefile.am +++ b/Makefile.am @@ -1126,6 +1126,26 @@ tests_test_connect_null_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} \ tests_test_connect_null_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} tests_test_connect_null_fuzzer_CXXFLAGS = -std=c++11 +if HAVE_CURVE +fuzzer_apps += tests/test_bind_curve_fuzzer \ + tests/test_connect_curve_fuzzer + +tests_test_bind_curve_fuzzer_DEPENDENCIES = src/libzmq.la +tests_test_bind_curve_fuzzer_SOURCES = tests/test_bind_curve_fuzzer.cpp +tests_test_bind_curve_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} \ + $(top_builddir)/src/.libs/libzmq.a \ + ${src_libzmq_la_LIBADD} +tests_test_bind_curve_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_bind_curve_fuzzer_CXXFLAGS = -std=c++11 + +tests_test_connect_curve_fuzzer_DEPENDENCIES = src/libzmq.la +tests_test_connect_curve_fuzzer_SOURCES = tests/test_connect_curve_fuzzer.cpp +tests_test_connect_curve_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} \ + $(top_builddir)/src/.libs/libzmq.a \ + ${src_libzmq_la_LIBADD} +tests_test_connect_curve_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_connect_curve_fuzzer_CXXFLAGS = -std=c++11 + FUZZINGdir = ${prefix}/${FUZZING_INSTALLDIR} FUZZING_PROGRAMS = ${fuzzer_apps} else @@ -1139,6 +1159,18 @@ tests_test_bind_null_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} tests_test_connect_null_fuzzer_SOURCES = tests/test_connect_null_fuzzer.cpp tests_test_connect_null_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la tests_test_connect_null_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} + +if HAVE_CURVE +test_apps += tests/test_bind_curve_fuzzer \ + tests/test_connect_curve_fuzzer + +tests_test_bind_curve_fuzzer_SOURCES = tests/test_bind_curve_fuzzer.cpp +tests_test_bind_curve_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la +tests_test_bind_curve_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} + +tests_test_connect_curve_fuzzer_SOURCES = tests/test_connect_curve_fuzzer.cpp +tests_test_connect_curve_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la +tests_test_connect_curve_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} endif if ENABLE_STATIC diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a77b52b4..359c56e2 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -80,6 +80,9 @@ endif() if(ZMQ_HAVE_CURVE) list(APPEND tests test_security_curve) + if(NOT WIN32) + list(APPEND tests test_connect_curve_fuzzer test_bind_curve_fuzzer) + endif() endif() option(ENABLE_CAPSH "Run tests that require sudo and capsh (for cap_net_admin)" OFF) diff --git a/tests/test_bind_curve_fuzzer.cpp b/tests/test_bind_curve_fuzzer.cpp new file mode 100644 index 00000000..46548ce2 --- /dev/null +++ b/tests/test_bind_curve_fuzzer.cpp @@ -0,0 +1,82 @@ +/* + 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_security.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) +{ + void *handler; + void *zap_thread; + void *server; + void *server_mon; + char my_endpoint[MAX_SOCKET_STRING]; + + setup_test_context (); + setup_testutil_security_curve (); + setup_context_and_server_side (&handler, &zap_thread, &server, &server_mon, + my_endpoint); + fd_t client = connect_socket (my_endpoint); + + 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); + + close (client); + + shutdown_context_and_server_side (zap_thread, server, server_mon, handler); + teardown_test_context (); + + return 0; +} + +#ifndef ZMQ_USE_FUZZING_ENGINE +void test_bind_curve_fuzzer () +{ + TEST_ASSERT_SUCCESS_ERRNO (LLVMFuzzerTestOneInput ( + zmtp_greeting_curve, sizeof (zmtp_greeting_curve))); +} + +int main (int argc, char **argv) +{ + setup_test_environment (); + + UNITY_BEGIN (); + RUN_TEST (test_bind_curve_fuzzer); + + return UNITY_END (); +} +#endif diff --git a/tests/test_connect_curve_fuzzer.cpp b/tests/test_connect_curve_fuzzer.cpp new file mode 100644 index 00000000..aa03e0d4 --- /dev/null +++ b/tests/test_connect_curve_fuzzer.cpp @@ -0,0 +1,85 @@ +/* + 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_security.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 (); + setup_testutil_security_curve (); + char my_endpoint[MAX_SOCKET_STRING]; + fd_t server = bind_socket_resolve_port ("127.0.0.1", "0", my_endpoint); + + curve_client_data_t curve_client_data = { + valid_server_public, valid_client_public, valid_client_secret}; + void *client_mon; + void *client = create_and_connect_client ( + my_endpoint, socket_config_curve_client, &curve_client_data, &client_mon); + + fd_t server_accept = + TEST_ASSERT_SUCCESS_RAW_ERRNO (accept (server, NULL, NULL)); + 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); + + close (server_accept); + close (server); + + test_context_socket_close_zero_linger (client); + test_context_socket_close_zero_linger (client_mon); + teardown_test_context (); + + return 0; +} + +#ifndef ZMQ_USE_FUZZING_ENGINE +void test_connect_curve_fuzzer () +{ + TEST_ASSERT_SUCCESS_ERRNO (LLVMFuzzerTestOneInput ( + zmtp_greeting_curve, sizeof (zmtp_greeting_curve))); +} + +int main (int argc, char **argv) +{ + setup_test_environment (); + + UNITY_BEGIN (); + RUN_TEST (test_connect_curve_fuzzer); + + return UNITY_END (); +} +#endif From 96787c35f84c2eb5e4c723ed0cef57a136bb9f99 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sun, 26 Apr 2020 22:23:04 +0100 Subject: [PATCH 7/7] Problem: no fuzz testing for API with variable input Solution: add tests for zmq_bind, zmq_connect and zmq_z85_decode --- Makefile.am | 52 ++++++++++++++++++++-- tests/CMakeLists.txt | 4 +- tests/test_bind_fuzzer.cpp | 71 ++++++++++++++++++++++++++++++ tests/test_connect_fuzzer.cpp | 71 ++++++++++++++++++++++++++++++ tests/test_z85_decode_fuzzer.cpp | 75 ++++++++++++++++++++++++++++++++ 5 files changed, 267 insertions(+), 6 deletions(-) create mode 100644 tests/test_bind_fuzzer.cpp create mode 100644 tests/test_connect_fuzzer.cpp create mode 100644 tests/test_z85_decode_fuzzer.cpp diff --git a/Makefile.am b/Makefile.am index acf4ba3e..b3a8b12c 100755 --- a/Makefile.am +++ b/Makefile.am @@ -1108,7 +1108,9 @@ endif if FUZZING_ENGINE_LIB fuzzer_apps = tests/test_bind_null_fuzzer \ - tests/test_connect_null_fuzzer + tests/test_connect_null_fuzzer \ + tests/test_bind_fuzzer \ + tests/test_connect_fuzzer tests_test_bind_null_fuzzer_DEPENDENCIES = src/libzmq.la tests_test_bind_null_fuzzer_SOURCES = tests/test_bind_null_fuzzer.cpp @@ -1126,9 +1128,26 @@ tests_test_connect_null_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} \ tests_test_connect_null_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} tests_test_connect_null_fuzzer_CXXFLAGS = -std=c++11 +tests_test_bind_fuzzer_DEPENDENCIES = src/libzmq.la +tests_test_bind_fuzzer_SOURCES = tests/test_bind_fuzzer.cpp +tests_test_bind_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} \ + $(top_builddir)/src/.libs/libzmq.a \ + ${src_libzmq_la_LIBADD} +tests_test_bind_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_bind_fuzzer_CXXFLAGS = -std=c++11 + +tests_test_connect_fuzzer_DEPENDENCIES = src/libzmq.la +tests_test_connect_fuzzer_SOURCES = tests/test_connect_fuzzer.cpp +tests_test_connect_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} \ + $(top_builddir)/src/.libs/libzmq.a \ + ${src_libzmq_la_LIBADD} +tests_test_connect_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_connect_fuzzer_CXXFLAGS = -std=c++11 + if HAVE_CURVE fuzzer_apps += tests/test_bind_curve_fuzzer \ - tests/test_connect_curve_fuzzer + tests/test_connect_curve_fuzzer \ + tests/test_z85_decode_fuzzer tests_test_bind_curve_fuzzer_DEPENDENCIES = src/libzmq.la tests_test_bind_curve_fuzzer_SOURCES = tests/test_bind_curve_fuzzer.cpp @@ -1146,11 +1165,22 @@ tests_test_connect_curve_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} \ tests_test_connect_curve_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} tests_test_connect_curve_fuzzer_CXXFLAGS = -std=c++11 +tests_test_z85_decode_fuzzer_DEPENDENCIES = src/libzmq.la +tests_test_z85_decode_fuzzer_SOURCES = tests/test_z85_decode_fuzzer.cpp +tests_test_z85_decode_fuzzer_LDADD = ${TESTUTIL_LIBS} ${FUZZING_ENGINE_LIB} \ + $(top_builddir)/src/.libs/libzmq.a \ + ${src_libzmq_la_LIBADD} +tests_test_z85_decode_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_z85_decode_fuzzer_CXXFLAGS = -std=c++11 +endif + FUZZINGdir = ${prefix}/${FUZZING_INSTALLDIR} FUZZING_PROGRAMS = ${fuzzer_apps} else test_apps += tests/test_bind_null_fuzzer \ - tests/test_connect_null_fuzzer + tests/test_connect_null_fuzzer \ + tests/test_bind_fuzzer \ + tests/test_connect_fuzzer tests_test_bind_null_fuzzer_SOURCES = tests/test_bind_null_fuzzer.cpp tests_test_bind_null_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la @@ -1160,9 +1190,18 @@ tests_test_connect_null_fuzzer_SOURCES = tests/test_connect_null_fuzzer.cpp tests_test_connect_null_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la tests_test_connect_null_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_bind_fuzzer_SOURCES = tests/test_bind_fuzzer.cpp +tests_test_bind_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la +tests_test_bind_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} + +tests_test_connect_fuzzer_SOURCES = tests/test_connect_fuzzer.cpp +tests_test_connect_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la +tests_test_connect_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} + if HAVE_CURVE test_apps += tests/test_bind_curve_fuzzer \ - tests/test_connect_curve_fuzzer + tests/test_connect_curve_fuzzer \ + tests/test_z85_decode_fuzzer tests_test_bind_curve_fuzzer_SOURCES = tests/test_bind_curve_fuzzer.cpp tests_test_bind_curve_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la @@ -1171,6 +1210,11 @@ tests_test_bind_curve_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} tests_test_connect_curve_fuzzer_SOURCES = tests/test_connect_curve_fuzzer.cpp tests_test_connect_curve_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la tests_test_connect_curve_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} + +tests_test_z85_decode_fuzzer_SOURCES = tests/test_z85_decode_fuzzer.cpp +tests_test_z85_decode_fuzzer_LDADD = ${TESTUTIL_LIBS} src/libzmq.la +tests_test_z85_decode_fuzzer_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +endif endif if ENABLE_STATIC diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 359c56e2..213022f8 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -75,13 +75,13 @@ set(tests test_mock_pub_sub) if(NOT WIN32) - list(APPEND tests test_security_gssapi test_socks test_connect_null_fuzzer test_bind_null_fuzzer) + list(APPEND tests test_security_gssapi test_socks test_connect_null_fuzzer test_bind_null_fuzzer test_connect_fuzzer test_bind_fuzzer) endif() if(ZMQ_HAVE_CURVE) list(APPEND tests test_security_curve) if(NOT WIN32) - list(APPEND tests test_connect_curve_fuzzer test_bind_curve_fuzzer) + list(APPEND tests test_connect_curve_fuzzer test_bind_curve_fuzzer test_z85_decode_fuzzer) endif() endif() diff --git a/tests/test_bind_fuzzer.cpp b/tests/test_bind_fuzzer.cpp new file mode 100644 index 00000000..3c8e6e87 --- /dev/null +++ b/tests/test_bind_fuzzer.cpp @@ -0,0 +1,71 @@ +/* + 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 + +#include "testutil.hpp" +#include "testutil_unity.hpp" + +// Test that zmq_bind can handle malformed strings +extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) +{ + setup_test_context (); + std::string my_endpoint (reinterpret_cast (data), size); + void *socket = test_context_socket (ZMQ_PUB); + zmq_bind (socket, my_endpoint.c_str ()); + + test_context_socket_close_zero_linger (socket); + teardown_test_context (); + + return 0; +} + +#ifndef ZMQ_USE_FUZZING_ENGINE +void test_bind_fuzzer () +{ + uint8_t buffer[32] = {0}; + + TEST_ASSERT_SUCCESS_ERRNO ( + LLVMFuzzerTestOneInput (buffer, sizeof (buffer))); +} + +int main (int argc, char **argv) +{ + setup_test_environment (); + + UNITY_BEGIN (); + RUN_TEST (test_bind_fuzzer); + + return UNITY_END (); +} +#endif diff --git a/tests/test_connect_fuzzer.cpp b/tests/test_connect_fuzzer.cpp new file mode 100644 index 00000000..4ef2cfdd --- /dev/null +++ b/tests/test_connect_fuzzer.cpp @@ -0,0 +1,71 @@ +/* + 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 + +#include "testutil.hpp" +#include "testutil_unity.hpp" + +// Test that zmq_connect can handle malformed strings +extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) +{ + setup_test_context (); + std::string my_endpoint (reinterpret_cast (data), size); + void *socket = test_context_socket (ZMQ_PUB); + zmq_connect (socket, my_endpoint.c_str ()); + + test_context_socket_close_zero_linger (socket); + teardown_test_context (); + + return 0; +} + +#ifndef ZMQ_USE_FUZZING_ENGINE +void test_connect_fuzzer () +{ + uint8_t buffer[32] = {0}; + + TEST_ASSERT_SUCCESS_ERRNO ( + LLVMFuzzerTestOneInput (buffer, sizeof (buffer))); +} + +int main (int argc, char **argv) +{ + setup_test_environment (); + + UNITY_BEGIN (); + RUN_TEST (test_connect_fuzzer); + + return UNITY_END (); +} +#endif diff --git a/tests/test_z85_decode_fuzzer.cpp b/tests/test_z85_decode_fuzzer.cpp new file mode 100644 index 00000000..4a6d29ce --- /dev/null +++ b/tests/test_z85_decode_fuzzer.cpp @@ -0,0 +1,75 @@ +/* + 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 +#include + +#include "testutil.hpp" +#include "testutil_unity.hpp" + +extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) +{ + uint8_t *secret_key; + + // As per API definition, input must be divisible by 5, so truncate it if it's not + size -= size % 5; + // As per API definition, the destination must be at least 0.8 times the input data + TEST_ASSERT_NOT_NULL (secret_key = (uint8_t *) malloc (size * 4 / 5)); + + std::string z85_secret_key (reinterpret_cast (data), size); + TEST_ASSERT_NOT_NULL (zmq_z85_decode (secret_key, z85_secret_key.c_str ())); + + free (secret_key); + + return 0; +} + +#ifndef ZMQ_USE_FUZZING_ENGINE +void test_bind_null_fuzzer () +{ + uint8_t buffer[32] = {0}; + + TEST_ASSERT_SUCCESS_ERRNO ( + LLVMFuzzerTestOneInput (buffer, sizeof (buffer))); +} + +int main (int argc, char **argv) +{ + setup_test_environment (); + + UNITY_BEGIN (); + RUN_TEST (test_bind_null_fuzzer); + + return UNITY_END (); +} +#endif