diff --git a/.cirrus.yml b/.cirrus.yml index a3f70757..d1db3927 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -102,8 +102,8 @@ bazel-valgrind_task: test_all_script: - cd /src/workspace && bazel test -k --remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST - --build_tag_filters=-haskell - --test_tag_filters=-haskell + --build_tag_filters=-haskell,-fuzz-test + --test_tag_filters=-haskell,-fuzz-test --remote_download_minimal --config=valgrind -- diff --git a/.clang-format b/.clang-format index e159ed9b..d4728187 100644 --- a/.clang-format +++ b/.clang-format @@ -8,7 +8,7 @@ AlwaysBreakTemplateDeclarations: Yes SpaceBeforeCpp11BracedList: false Cpp11BracedListStyle: true -IncludeIsMainRegex: '([-_](test))?$' +IncludeIsMainRegex: '([-_](test|fuzz_test))?$' IncludeBlocks: Regroup IncludeCategories: - Regex: '^<.*\.h>' diff --git a/other/analysis/gen-file.sh b/other/analysis/gen-file.sh index 504b4e37..88582f5a 100644 --- a/other/analysis/gen-file.sh +++ b/other/analysis/gen-file.sh @@ -55,6 +55,7 @@ FIND_QUERY="$FIND_QUERY -and -not -name amalgamation.cc" FIND_QUERY="$FIND_QUERY -and -not -name av_test.c" FIND_QUERY="$FIND_QUERY -and -not -name cracker.c" FIND_QUERY="$FIND_QUERY -and -not -name version_test.c" +FIND_QUERY="$FIND_QUERY -and -not -name '*_fuzz_test.cc'" FIND_QUERY="$FIND_QUERY -and -not -wholename './testing/fuzzing/*'" if [ "$SKIP_GTEST" == 1 ]; then diff --git a/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 b/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 index c03661e4..6e08e20b 100644 --- a/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 +++ b/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 @@ -1 +1 @@ -591106a972c27f19e72bd8927964e1ecc57976f4f4ff538699eaf3eb6236f9ea /usr/local/bin/tox-bootstrapd +c93318d8f05a4e9c9bd74bfb6f01b776c7dcb7450745dfe5f31dac592353cd74 /usr/local/bin/tox-bootstrapd diff --git a/other/bootstrap_daemon/websocket/websockify/BUILD.bazel b/other/bootstrap_daemon/websocket/websockify/BUILD.bazel index bfe2fc53..a364319f 100644 --- a/other/bootstrap_daemon/websocket/websockify/BUILD.bazel +++ b/other/bootstrap_daemon/websocket/websockify/BUILD.bazel @@ -1,5 +1,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") +package(features = ["-layering_check"]) + go_library( name = "go_default_library", srcs = ["websockify.go"], diff --git a/testing/fuzzing/BUILD.bazel b/testing/fuzzing/BUILD.bazel new file mode 100644 index 00000000..3b256dc7 --- /dev/null +++ b/testing/fuzzing/BUILD.bazel @@ -0,0 +1,25 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test") + +cc_library( + name = "fuzz_adapter", + srcs = ["fuzz_adapter.c"], + hdrs = ["fuzz_adapter.h"], + visibility = ["//c-toxcore:__subpackages__"], +) + +cc_fuzz_test( + name = "bootstrap_fuzzer", + srcs = ["bootstrap_harness.cc"], + copts = ["-UNDEBUG"], + corpus = ["//tools/toktok-fuzzer/corpus:bootstrap_fuzzer"], + deps = ["//c-toxcore/toxcore:tox"], +) + +cc_fuzz_test( + name = "toxsave_fuzz_test", + srcs = ["toxsave_harness.cc"], + copts = ["-UNDEBUG"], + corpus = ["//tools/toktok-fuzzer/corpus:toxsave_fuzzer"], + deps = ["//c-toxcore/toxcore:tox"], +) diff --git a/testing/fuzzing/bootstrap_harness.cc b/testing/fuzzing/bootstrap_harness.cc index eb1b62bf..70b139a5 100644 --- a/testing/fuzzing/bootstrap_harness.cc +++ b/testing/fuzzing/bootstrap_harness.cc @@ -4,12 +4,13 @@ #include "../../toxcore/tox.h" #include "fuzz_adapter.h" +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { network_adapter_init(data, size); Tox_Err_New error_new; - Tox *tox = tox_new(NULL, &error_new); + Tox *tox = tox_new(nullptr, &error_new); assert(tox != nullptr); assert(error_new == TOX_ERR_NEW_OK); diff --git a/testing/fuzzing/fuzz_adapter.c b/testing/fuzzing/fuzz_adapter.c index 69fbd2fc..da7785a9 100644 --- a/testing/fuzzing/fuzz_adapter.c +++ b/testing/fuzzing/fuzz_adapter.c @@ -66,7 +66,7 @@ ssize_t fuzz_recvfrom(int sockfd, void *buf, size_t len, src_addr->sa_family = AF_INET; // We want an AF_INET address with dummy values - struct sockaddr_in *addr_in = (struct sockaddr_in *) src_addr; + struct sockaddr_in *addr_in = (struct sockaddr_in *)(void *)src_addr; addr_in->sin_port = 12356; addr_in->sin_addr.s_addr = INADDR_LOOPBACK + 1; *addr_len = sizeof(struct sockaddr); diff --git a/testing/fuzzing/toxsave_harness.cc b/testing/fuzzing/toxsave_harness.cc index 6b53f869..80a1ba69 100644 --- a/testing/fuzzing/toxsave_harness.cc +++ b/testing/fuzzing/toxsave_harness.cc @@ -2,6 +2,7 @@ #include "../../toxcore/tox.h" +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { Tox_Err_Options_New error_options; diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel index a447bd57..a5adac80 100644 --- a/toxcore/BUILD.bazel +++ b/toxcore/BUILD.bazel @@ -1,4 +1,5 @@ load("@rules_cc//cc:defs.bzl", "cc_test") +load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test") load("//tools:no_undefined.bzl", "cc_library") package(features = ["layering_check"]) @@ -51,6 +52,7 @@ cc_library( visibility = ["//c-toxcore:__subpackages__"], deps = [ ":ccompat", + "//c-toxcore/testing/fuzzing:fuzz_adapter", "@libsodium", ], ) @@ -121,6 +123,7 @@ cc_library( ], deps = [ ":ccompat", + "//c-toxcore/testing/fuzzing:fuzz_adapter", "@pthread", ], ) @@ -169,6 +172,7 @@ cc_library( ":logger", ":mono_time", ":util", + "//c-toxcore/testing/fuzzing:fuzz_adapter", "@libsodium", "@psocket", "@pthread", @@ -279,6 +283,13 @@ cc_test( ], ) +cc_fuzz_test( + name = "DHT_fuzz_test", + srcs = ["DHT_fuzz_test.cc"], + corpus = ["//tools/toktok-fuzzer/corpus:DHT_fuzz_test"], + deps = [":DHT"], +) + cc_library( name = "onion", srcs = ["onion.c"], @@ -561,6 +572,25 @@ cc_library( ], ) +cc_test( + name = "tox_events_test", + size = "small", + srcs = ["tox_events_test.cc"], + deps = [ + ":crypto_core", + ":tox_events", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + +cc_fuzz_test( + name = "tox_events_fuzz_test", + srcs = ["tox_events_fuzz_test.cc"], + corpus = ["//tools/toktok-fuzzer/corpus:tox_events_fuzz_test"], + deps = [":tox_events"], +) + cc_library( name = "tox_dispatch", srcs = ["tox_dispatch.c"], diff --git a/toxcore/DHT_fuzz_test.cc b/toxcore/DHT_fuzz_test.cc new file mode 100644 index 00000000..a8fa7b34 --- /dev/null +++ b/toxcore/DHT_fuzz_test.cc @@ -0,0 +1,80 @@ +#include "DHT.h" + +#include +#include + +namespace { + +void TestHandleRequest(const uint8_t *input_data, size_t input_size) +{ + const uint8_t *data = input_data; + size_t size = input_size; + + const uint8_t *self_public_key = data; + data += CRYPTO_PUBLIC_KEY_SIZE; + size -= CRYPTO_PUBLIC_KEY_SIZE; + + const uint8_t *self_secret_key = data; + data += CRYPTO_SECRET_KEY_SIZE; + size -= CRYPTO_SECRET_KEY_SIZE; + + uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t request[MAX_CRYPTO_REQUEST_SIZE]; + uint8_t request_id; + handle_request(self_public_key, self_secret_key, public_key, request, &request_id, data, size); +} + +void TestUnpackNodes(const uint8_t *input_data, size_t input_size) +{ + const uint8_t *data = input_data; + size_t size = input_size; + + if (size < 1) { + return; + } + + const bool tcp_enabled = data[0]; + ++data; + --size; + + Node_format nodes[5]; + uint16_t processed_data_len; + const int packed_count = unpack_nodes(nodes, 5, &processed_data_len, data, size, tcp_enabled); + if (packed_count > 0) { + Logger *logger = logger_new(); + std::vector packed(packed_count * PACKED_NODE_SIZE_IP6); + const int packed_size + = pack_nodes(logger, packed.data(), packed.size(), nodes, packed_count); + LOGGER_ASSERT(logger, packed_size == processed_data_len, + "packed size (%d) != unpacked size (%d)", packed_size, processed_data_len); + logger_kill(logger); + } +} + +} // namespace + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *input_data, size_t input_size); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *input_data, size_t input_size) +{ + const uint8_t *data = input_data; + size_t size = input_size; + + if (size < 1) { + return 0; + } + + const uint8_t func = data[0]; + ++data; + --size; + + switch (func) { + case 0: + TestHandleRequest(data, size); + return 0; + case 1: + TestUnpackNodes(data, size); + return 0; + default: + return 0; + } +} diff --git a/toxcore/logger.h b/toxcore/logger.h index 0866f2d9..64cd667b 100644 --- a/toxcore/logger.h +++ b/toxcore/logger.h @@ -13,6 +13,10 @@ #include "attributes.h" +#ifdef __cplusplus +extern "C" { +#endif + #ifndef MIN_LOGGER_LEVEL #define MIN_LOGGER_LEVEL LOGGER_LEVEL_INFO #endif @@ -93,4 +97,8 @@ void logger_write( } \ } while (0) +#ifdef __cplusplus +} // extern "C" +#endif + #endif // C_TOXCORE_TOXCORE_LOGGER_H diff --git a/toxcore/network.c b/toxcore/network.c index b986f8ce..fafd4a06 100644 --- a/toxcore/network.c +++ b/toxcore/network.c @@ -127,10 +127,12 @@ static bool should_ignore_recv_error(int err) return err == EWOULDBLOCK; } +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION static bool should_ignore_connect_error(int err) { return err == EWOULDBLOCK || err == EINPROGRESS; } +#endif non_null() static const char *inet_ntop4(const struct in_addr *addr, char *buf, size_t bufsize) @@ -274,8 +276,9 @@ static int make_socktype(int type) return type; } } -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || !defined(NDEBUG) static int make_family(Family tox_family) { switch (tox_family.value) { @@ -292,6 +295,7 @@ static int make_family(Family tox_family) return tox_family.value; } } +#endif // !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && !defined(NDEBUG) static const Family *make_tox_family(int family) { @@ -1033,7 +1037,7 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por for (uint16_t tries = port_from; tries <= port_to; ++tries) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - int res = 0; + int res = addrsize > 0 ? 0 : -1; #else int res = bind(temp->sock.sock, (struct sockaddr *)&addr, addrsize); #endif @@ -1459,7 +1463,7 @@ bool net_connect(const Logger *log, Socket sock, const IP_Port *ip_port) } #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return true; + return addrsize != 0; #else LOGGER_DEBUG(log, "connecting socket %d to %s:%d", (int)sock.sock, ip_ntoa(&ip_port->ip, ip_str, sizeof(ip_str)), net_ntohs(ip_port->port)); @@ -1610,7 +1614,7 @@ bool bind_to_port(Socket sock, Family family, uint16_t port) } #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return true; + return addrsize != 0; #else return bind(sock.sock, (struct sockaddr *)&addr, addrsize) == 0; #endif diff --git a/toxcore/tox_events.c b/toxcore/tox_events.c index 4de5c0b7..e1466d5b 100644 --- a/toxcore/tox_events.c +++ b/toxcore/tox_events.c @@ -246,9 +246,9 @@ void tox_events_get_bytes(const Tox_Events *events, uint8_t *bytes) Tox_Events *tox_events_load(const uint8_t *bytes, uint32_t bytes_size) { msgpack_unpacked msg; + msgpack_unpacked_init(&msg); size_t offset = 0; - msgpack_unpacked_init(&msg); const msgpack_unpack_return result = msgpack_unpack_next(&msg, (const char *)bytes, bytes_size, &offset); if (result != MSGPACK_UNPACK_SUCCESS) { diff --git a/toxcore/tox_events_fuzz_test.cc b/toxcore/tox_events_fuzz_test.cc new file mode 100644 index 00000000..fdbff071 --- /dev/null +++ b/toxcore/tox_events_fuzz_test.cc @@ -0,0 +1,14 @@ +#include "tox_events.h" + +namespace { + +void TestUnpack(const uint8_t *data, size_t size) { tox_events_free(tox_events_load(data, size)); } + +} // namespace + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + TestUnpack(data, size); + return 0; +} diff --git a/toxcore/tox_events_test.cc b/toxcore/tox_events_test.cc new file mode 100644 index 00000000..71b77485 --- /dev/null +++ b/toxcore/tox_events_test.cc @@ -0,0 +1,44 @@ +#include "tox_events.h" + +#include + +#include +#include + +#include "crypto_core.h" + +namespace { + +TEST(ToxEvents, UnpackRandomDataDoesntCrash) +{ + std::array data; + random_bytes(data.data(), data.size()); + tox_events_free(tox_events_load(data.data(), data.size())); +} + +TEST(ToxEvents, UnpackEmptyDataFails) +{ + std::array data; + Tox_Events *events = tox_events_load(data.end(), 0); + EXPECT_EQ(events, nullptr); +} + +TEST(ToxEvents, UnpackEmptyArrayCreatesEmptyEvents) +{ + std::array data{0x90}; // empty msgpack array + Tox_Events *events = tox_events_load(data.data(), data.size()); + ASSERT_NE(events, nullptr); + EXPECT_EQ(tox_events_get_conference_connected_size(events), 0); + tox_events_free(events); +} + +// TODO(iphydf): Enable this test once we've fully moved away from msgpack-c. +#if 0 +TEST(ToxEvents, DealsWithHugeMsgpackArrays) +{ + std::vector data{0xdd, 0xff, 0xff, 0xff, 0xff}; + EXPECT_EQ(tox_events_load(data.data(), data.size()), nullptr); +} +#endif + +} // namespace