From b1fe0644843f5d08cd203bb5d4cdb923172f247b Mon Sep 17 00:00:00 2001 From: Green Sky Date: Sun, 14 Jan 2024 21:51:01 +0100 Subject: [PATCH] Squashed 'external/toxcore/c-toxcore/' changes from 6d634674a9..73d9b845a3 73d9b845a3 cleanup: Remove old type-ordered event getters. b0840cc02d feat: add ngc events 7df9a51349 refactor: Make event dispatch ordered by receive time. bcb6592af5 test: Add C++ classes wrapping system interfaces. 4cea4f9ca4 fix: Make all the fuzzers work again, and add a test for protodump. c4e209ea1d refactor: Factor out malloc+memcpy into memdup. 87bcc4322d fix: Remove fatal error for non-erroneous case REVERT: 6d634674a9 cleanup: Remove old type-ordered event getters. REVERT: d1d48d1dfc feat: add ngc events REVERT: 994ffecc6b refactor: Make event dispatch ordered by receive time. git-subtree-dir: external/toxcore/c-toxcore git-subtree-split: 73d9b845a310c3f56d2d6d77ed56b93d84256d6e --- .clusterfuzzlite/build.sh | 2 +- CMakeLists.txt | 2 + .../docker/tox-bootstrapd.sha256 | 2 +- testing/coverage_live.sh | 2 +- testing/distill_corpus.sh | 4 +- testing/fuzzing/BUILD.bazel | 43 +++- testing/fuzzing/CMakeLists.txt | 20 +- ...trap_harness.cc => bootstrap_fuzz_test.cc} | 13 +- testing/fuzzing/e2e_fuzz_test.cc | 7 +- testing/fuzzing/fuzz_support.cc | 117 ++++++----- testing/fuzzing/fuzz_support.h | 194 ++++++++++++++---- testing/fuzzing/protodump.cc | 38 ++-- testing/fuzzing/protodump_reduce.cc | 90 ++++---- testing/fuzzing/rebuild_protodump | 14 ++ ...oxsave_harness.cc => toxsave_fuzz_test.cc} | 2 +- testing/run_afl.sh | 52 ++--- toxcore/BUILD.bazel | 53 +++-- toxcore/DHT_fuzz_test.cc | 13 +- toxcore/DHT_test.cc | 5 +- toxcore/crypto_core.c | 4 - toxcore/forwarding_fuzz_test.cc | 2 +- toxcore/group_announce_fuzz_test.cc | 26 +-- toxcore/group_announce_test.cc | 3 +- toxcore/group_chats.c | 15 +- toxcore/group_connection.c | 10 +- toxcore/group_moderation_fuzz_test.cc | 12 +- toxcore/group_moderation_test.cc | 57 +++-- toxcore/mem_test_util.cc | 26 +++ toxcore/mem_test_util.hh | 39 ++++ toxcore/mono_time.c | 2 +- toxcore/mono_time_test.cc | 10 +- toxcore/network_test.cc | 9 + toxcore/network_test_util.cc | 75 +++++++ toxcore/network_test_util.hh | 54 +++++ toxcore/ping_array_test.cc | 21 +- toxcore/test_util.hh | 19 +- toxcore/tox_events_fuzz_test.cc | 2 +- toxcore/util.c | 16 ++ toxcore/util.h | 7 + 39 files changed, 775 insertions(+), 307 deletions(-) rename testing/fuzzing/{bootstrap_harness.cc => bootstrap_fuzz_test.cc} (94%) create mode 100755 testing/fuzzing/rebuild_protodump rename testing/fuzzing/{toxsave_harness.cc => toxsave_fuzz_test.cc} (96%) create mode 100644 toxcore/mem_test_util.cc create mode 100644 toxcore/mem_test_util.hh diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh index 57985e7..59adb61 100644 --- a/.clusterfuzzlite/build.sh +++ b/.clusterfuzzlite/build.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu -FUZZ_TARGETS="bootstrap_fuzzer toxsave_fuzzer" +FUZZ_TARGETS="bootstrap_fuzz_test toxsave_fuzz_test" # out of tree build cd "$WORK" diff --git a/CMakeLists.txt b/CMakeLists.txt index 761f53a..aaf6b37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -501,6 +501,8 @@ add_library(test_util STATIC toxcore/DHT_test_util.hh toxcore/crypto_core_test_util.cc toxcore/crypto_core_test_util.hh + toxcore/mem_test_util.cc + toxcore/mem_test_util.hh toxcore/network_test_util.cc toxcore/network_test_util.hh toxcore/test_util.cc diff --git a/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 b/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 index fec53be..6cee77a 100644 --- a/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 +++ b/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 @@ -1 +1 @@ -08fe68095b88c142e0c7f3369c5f297f1377e219d0e0f0ff6f30ca94d76fca98 /usr/local/bin/tox-bootstrapd +5061f92a95ba45cfa49d78175fa8fb6e4d66a58d86634ea3fd3ae6d80cb0558a /usr/local/bin/tox-bootstrapd diff --git a/testing/coverage_live.sh b/testing/coverage_live.sh index aceadf4..2b6d670 100755 --- a/testing/coverage_live.sh +++ b/testing/coverage_live.sh @@ -4,4 +4,4 @@ cd ../ # Run code coverage only on minized corpus to save time -afl-cov --cover-corpus -d ./_afl_out --overwrite --live --coverage-cmd "_cov_build/bootstrap_fuzzer @@" --code-dir ../ +afl-cov --cover-corpus -d ./_afl_out --overwrite --live --coverage-cmd "_cov_build/bootstrap_fuzz_test @@" --code-dir ../ diff --git a/testing/distill_corpus.sh b/testing/distill_corpus.sh index 644a6ac..01c39ef 100755 --- a/testing/distill_corpus.sh +++ b/testing/distill_corpus.sh @@ -1,7 +1,7 @@ #!/bin/sh -HARNESS_BIN="../_afl_build/bootstrap_fuzzer" -COV_BIN="../_cov_build/bootstrap_fuzzer" +HARNESS_BIN="../_afl_build/bootstrap_fuzz_test" +COV_BIN="../_cov_build/bootstrap_fuzz_test" # move to repo root cd ../ diff --git a/testing/fuzzing/BUILD.bazel b/testing/fuzzing/BUILD.bazel index 2aada41..008a431 100644 --- a/testing/fuzzing/BUILD.bazel +++ b/testing/fuzzing/BUILD.bazel @@ -1,6 +1,5 @@ -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test") -load("@rules_fuzzing//fuzzing/private:binary.bzl", "fuzzing_binary") # buildifier: disable=bzl-visibility cc_library( name = "fuzz_support", @@ -27,9 +26,9 @@ cc_library( cc_fuzz_test( name = "bootstrap_fuzz_test", size = "small", - srcs = ["bootstrap_harness.cc"], + srcs = ["bootstrap_fuzz_test.cc"], copts = ["-UNDEBUG"], - corpus = ["//tools/toktok-fuzzer/corpus:bootstrap_fuzzer"], + corpus = ["//tools/toktok-fuzzer/corpus:bootstrap_fuzz_test"], deps = [ ":fuzz_support", ":fuzz_tox", @@ -58,9 +57,9 @@ cc_fuzz_test( cc_fuzz_test( name = "toxsave_fuzz_test", size = "small", - srcs = ["toxsave_harness.cc"], + srcs = ["toxsave_fuzz_test.cc"], copts = ["-UNDEBUG"], - corpus = ["//tools/toktok-fuzzer/corpus:toxsave_fuzzer"], + corpus = ["//tools/toktok-fuzzer/corpus:toxsave_fuzz_test"], deps = [ ":fuzz_support", "//c-toxcore/toxcore:tox", @@ -80,12 +79,34 @@ cc_binary( ], ) -fuzzing_binary( - name = "protodump_bin", - testonly = True, - binary = ":protodump", - engine = "@rules_fuzzing//fuzzing:cc_engine", +genrule( + name = "e2e_fuzz_test_init", + outs = [ + "e2e_fuzz_test_init.dat", + "e2e_fuzz_test_bootstrap.dat", + ], + cmd = "$(location :protodump) $(location e2e_fuzz_test_init.dat) $(location e2e_fuzz_test_bootstrap.dat)", tags = ["manual"], + tools = [":protodump"], +) + +# bazel test --config=asan-libfuzzer //c-toxcore/testing/fuzzing:protodump_reduce_test +cc_test( + name = "protodump_reduce_test", + size = "small", + srcs = ["protodump_reduce.cc"], + args = ["$(location :e2e_fuzz_test_init.dat)"], + copts = ["-UNDEBUG"], + data = [":e2e_fuzz_test_init.dat"], + tags = ["manual"], + deps = [ + ":fuzz_support", + ":fuzz_tox", + "//c-toxcore/toxcore:tox", + "//c-toxcore/toxcore:tox_dispatch", + "//c-toxcore/toxcore:tox_events", + "@rules_fuzzing//fuzzing:cc_engine", + ], ) cc_fuzz_test( diff --git a/testing/fuzzing/CMakeLists.txt b/testing/fuzzing/CMakeLists.txt index d1fd753..7efbb8c 100644 --- a/testing/fuzzing/CMakeLists.txt +++ b/testing/fuzzing/CMakeLists.txt @@ -9,22 +9,18 @@ else() endif() function(fuzz_test target source_dir) - set(${target}_CORPUS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/toktok-fuzzer/corpus/${target}_fuzz_test) - file(GLOB ${target}_fuzz_CORPUS "${${target}_CORPUS_DIR}/*") + set(CORPUS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/toktok-fuzzer/corpus/${target}_fuzz_test) + file(GLOB CORPUS "${CORPUS_DIR}/*") add_executable(${target}_fuzz_test ${source_dir}/${target}_fuzz_test.cc) - target_link_libraries(${target}_fuzz_test PRIVATE toxcore_fuzz fuzz_support ${LIBFUZZER_LINKER_FLAGS}) - if(${target}_fuzz_CORPUS) - add_test(NAME ${target}_fuzz COMMAND ${CROSSCOMPILING_EMULATOR} ${target}_fuzz_test -max_total_time=10 ${${target}_fuzz_CORPUS}) + target_link_libraries(${target}_fuzz_test PRIVATE fuzz_support test_util toxcore_fuzz ${LIBFUZZER_LINKER_FLAGS}) + if(CORPUS) + add_test(NAME ${target}_fuzz COMMAND ${CROSSCOMPILING_EMULATOR} ${target}_fuzz_test -max_total_time=10 ${CORPUS}) + set_property(TEST ${target}_fuzz PROPERTY ENVIRONMENT "LLVM_PROFILE_FILE=${target}.profraw;srcdir=${CMAKE_CURRENT_SOURCE_DIR}") endif() endfunction() -# Fuzzes the toxsave API -add_executable(toxsave_fuzzer toxsave_harness.cc) -target_link_libraries(toxsave_fuzzer PRIVATE toxcore_fuzz fuzz_support ${LIBFUZZER_LINKER_FLAGS}) - -# Fuzzes the bootstrap process -add_executable(bootstrap_fuzzer bootstrap_harness.cc) -target_link_libraries(bootstrap_fuzzer PRIVATE toxcore_fuzz fuzz_support ${LIBFUZZER_LINKER_FLAGS}) +fuzz_test(bootstrap .) # Fuzzes the bootstrap process +fuzz_test(toxsave .) # Fuzzes the bootstrap process fuzz_test(DHT ../../toxcore) fuzz_test(forwarding ../../toxcore) diff --git a/testing/fuzzing/bootstrap_harness.cc b/testing/fuzzing/bootstrap_fuzz_test.cc similarity index 94% rename from testing/fuzzing/bootstrap_harness.cc rename to testing/fuzzing/bootstrap_fuzz_test.cc index 9c178f2..11b70b7 100644 --- a/testing/fuzzing/bootstrap_harness.cc +++ b/testing/fuzzing/bootstrap_fuzz_test.cc @@ -83,9 +83,12 @@ void setup_callbacks(Tox_Dispatch *dispatch) dispatch, [](Tox *tox, const Tox_Event_Friend_Request *event, void *user_data) { Tox_Err_Friend_Add err; tox_friend_add_norequest(tox, tox_event_friend_request_get_public_key(event), &err); - assert(err == TOX_ERR_FRIEND_ADD_OK || err == TOX_ERR_FRIEND_ADD_OWN_KEY - || err == TOX_ERR_FRIEND_ADD_ALREADY_SENT - || err == TOX_ERR_FRIEND_ADD_BAD_CHECKSUM); + if (!(err == TOX_ERR_FRIEND_ADD_OK || err == TOX_ERR_FRIEND_ADD_OWN_KEY + || err == TOX_ERR_FRIEND_ADD_ALREADY_SENT + || err == TOX_ERR_FRIEND_ADD_BAD_CHECKSUM + || err == TOX_ERR_FRIEND_ADD_MALLOC)) { + printf("unexpected error: %s\n", tox_err_friend_add_to_string(err)); + } }); tox_events_callback_friend_status( dispatch, [](Tox *tox, const Tox_Event_Friend_Status *event, void *user_data) { @@ -120,7 +123,7 @@ void TestBootstrap(Fuzz_Data &input) [](Tox *tox, Tox_Log_Level level, const char *file, uint32_t line, const char *func, const char *message, void *user_data) { // Log to stdout. - if (DEBUG) { + if (Fuzz_Data::DEBUG) { std::printf("[tox1] %c %s:%d(%s): %s\n", tox_log_level_name(level), file, line, func, message); } @@ -167,7 +170,7 @@ void TestBootstrap(Fuzz_Data &input) assert(dispatch != nullptr); setup_callbacks(dispatch); - while (input.size > 0) { + while (!input.empty()) { Tox_Err_Events_Iterate error_iterate; Tox_Events *events = tox_events_iterate(tox, true, &error_iterate); assert(tox_events_equal(null_sys.sys.get(), events, events)); diff --git a/testing/fuzzing/e2e_fuzz_test.cc b/testing/fuzzing/e2e_fuzz_test.cc index d19f0d6..4063427 100644 --- a/testing/fuzzing/e2e_fuzz_test.cc +++ b/testing/fuzzing/e2e_fuzz_test.cc @@ -110,9 +110,6 @@ void setup_callbacks(Tox_Dispatch *dispatch) dispatch, [](Tox *tox, const Tox_Event_Friend_Request *event, void *user_data) { Tox_Err_Friend_Add err; tox_friend_add_norequest(tox, tox_event_friend_request_get_public_key(event), &err); - assert(err == TOX_ERR_FRIEND_ADD_OK || err == TOX_ERR_FRIEND_ADD_OWN_KEY - || err == TOX_ERR_FRIEND_ADD_ALREADY_SENT - || err == TOX_ERR_FRIEND_ADD_BAD_CHECKSUM); }); tox_events_callback_friend_status( dispatch, [](Tox *tox, const Tox_Event_Friend_Status *event, void *user_data) { @@ -147,7 +144,7 @@ void TestEndToEnd(Fuzz_Data &input) [](Tox *tox, Tox_Log_Level level, const char *file, uint32_t line, const char *func, const char *message, void *user_data) { // Log to stdout. - if (DEBUG) { + if (Fuzz_Data::DEBUG) { std::printf("[tox1] %c %s:%d(%s): %s\n", tox_log_level_name(level), file, line, func, message); } @@ -170,7 +167,7 @@ void TestEndToEnd(Fuzz_Data &input) assert(dispatch != nullptr); setup_callbacks(dispatch); - while (input.size > 0) { + while (!input.empty()) { Tox_Err_Events_Iterate error_iterate; Tox_Events *events = tox_events_iterate(tox, true, &error_iterate); assert(tox_events_equal(null_sys.sys.get(), events, events)); diff --git a/testing/fuzzing/fuzz_support.cc b/testing/fuzzing/fuzz_support.cc index 59b027c..fd6b9c4 100644 --- a/testing/fuzzing/fuzz_support.cc +++ b/testing/fuzzing/fuzz_support.cc @@ -20,10 +20,6 @@ #include "../../toxcore/tox_private.h" #include "func_conversion.h" -const bool DEBUG = false; - -static constexpr tox_mono_time_cb *get_self_clock = ![](Fuzz_System *self) { return self->clock; }; - // TODO(iphydf): Put this somewhere shared. struct Network_Addr { struct sockaddr_storage addr; @@ -44,58 +40,67 @@ System::~System() { } static int recv_common(Fuzz_Data &input, uint8_t *buf, size_t buf_len) { - if (input.size < 2) { + if (input.size() < 2) { errno = ENOMEM; return -1; } - const size_t fuzz_len = (input.data[0] << 8) | input.data[1]; - input.data += 2; - input.size -= 2; + CONSUME_OR_ABORT(const uint8_t *fuzz_len_bytes, input, 2); + const std::size_t fuzz_len = (fuzz_len_bytes[0] << 8) | fuzz_len_bytes[1]; if (fuzz_len == 0xffff) { errno = EWOULDBLOCK; - if (DEBUG) { + if (Fuzz_Data::DEBUG) { std::printf("recvfrom: no data for tox1\n"); } return -1; } - if (DEBUG) { + if (Fuzz_Data::DEBUG) { std::printf( - "recvfrom: %zu (%02x, %02x) for tox1\n", fuzz_len, input.data[-2], input.data[-1]); + "recvfrom: %zu (%02x, %02x) for tox1\n", fuzz_len, input.data()[-2], input.data()[-1]); } - const size_t res = std::min(buf_len, std::min(fuzz_len, input.size)); + const size_t res = std::min(buf_len, std::min(fuzz_len, input.size())); - std::copy(input.data, input.data + res, buf); - input.data += res; - input.size -= res; + CONSUME_OR_ABORT(const uint8_t *data, input, res); + std::copy(data, data + res, buf); return res; } -template -static void *alloc_common(Fuzz_Data &data, F func) +static void *report_alloc(const char *name, const char *func, std::size_t size, void *ptr) { - CONSUME1_OR_RETURN_VAL(const uint8_t, want_alloc, data, func()); + if (Fuzz_Data::DEBUG) { + printf("%s: %s(%zu): %s\n", name, func, size, ptr == nullptr ? "false" : "true"); + } + return ptr; +} + +template +static void *alloc_common(const char *func, std::size_t size, Fuzz_Data &data, Args... args) +{ + CONSUME1_OR_RETURN_VAL( + const bool, want_alloc, data, report_alloc("tox1", func, size, Func(args...))); if (!want_alloc) { return nullptr; } - return func(); + return report_alloc("tox1", func, size, Func(args...)); } static constexpr Memory_Funcs fuzz_memory_funcs = { /* .malloc = */ ![](Fuzz_System *self, uint32_t size) { - return alloc_common(self->data, [=]() { return std::malloc(size); }); + return alloc_common("malloc", size, self->data, size); }, /* .calloc = */ ![](Fuzz_System *self, uint32_t nmemb, uint32_t size) { - return alloc_common(self->data, [=]() { return std::calloc(nmemb, size); }); + return alloc_common( + "calloc", nmemb * size, self->data, nmemb, size); }, /* .realloc = */ ![](Fuzz_System *self, void *ptr, uint32_t size) { - return alloc_common(self->data, [=]() { return std::realloc(ptr, size); }); + return alloc_common( + "realloc", size, self->data, ptr, size); }, /* .free = */ ![](Fuzz_System *self, void *ptr) { std::free(ptr); }, @@ -110,7 +115,7 @@ static constexpr Network_Funcs fuzz_network_funcs = { ![](Fuzz_System *self, int sock) { assert(sock == 42 || sock == 1337); const size_t count = random_u16(self->rng.get()); - return static_cast(std::min(count, self->data.size)); + return static_cast(std::min(count, self->data.size())); }, /* .recv = */ ![](Fuzz_System *self, int sock, uint8_t *buf, size_t len) { @@ -163,14 +168,17 @@ static constexpr Random_Funcs fuzz_random_funcs = { /* .random_bytes = */ ![](Fuzz_System *self, uint8_t *bytes, size_t length) { // Amount of data is limited - const size_t bytes_read = std::min(length, self->data.size); + const size_t bytes_read = std::min(length, self->data.size()); // Initialize everything to make MSAN and others happy std::memset(bytes, 0, length); - std::copy(self->data.data, self->data.data + bytes_read, bytes); - self->data.data += bytes_read; - self->data.size -= bytes_read; - if (DEBUG) { - std::printf("rng: %02x..%02x[%zu] -> tox1\n", bytes[0], bytes[length - 1], length); + CONSUME_OR_ABORT(const uint8_t *data, self->data, bytes_read); + std::copy(data, data + bytes_read, bytes); + if (Fuzz_Data::DEBUG) { + if (length == 1) { + std::printf("rng: %d (0x%02x)\n", bytes[0], bytes[0]); + } else { + std::printf("rng: %02x..%02x[%zu]\n", bytes[0], bytes[length - 1], length); + } } }, /* .random_uniform = */ @@ -194,7 +202,7 @@ Fuzz_System::Fuzz_System(Fuzz_Data &input) } , data(input) { - sys->mono_time_callback = get_self_clock; + sys->mono_time_callback = [](void *self) { return static_cast(self)->clock; }; sys->mono_time_user_data = this; sys->mem = mem.get(); sys->ns = ns.get(); @@ -281,7 +289,7 @@ Null_System::Null_System() std::make_unique(Random{&null_random_funcs, this}), } { - sys->mono_time_callback = get_self_clock; + sys->mono_time_callback = [](void *self) { return static_cast(self)->clock; }; sys->mono_time_user_data = this; sys->mem = mem.get(); sys->ns = ns.get(); @@ -298,7 +306,25 @@ static uint16_t get_port(const Network_Addr *addr) } } -static constexpr Memory_Funcs record_memory_funcs = null_memory_funcs; +static constexpr Memory_Funcs record_memory_funcs = { + /* .malloc = */ + ![](Record_System *self, uint32_t size) { + self->push(true); + return report_alloc(self->name_, "malloc", size, std::malloc(size)); + }, + /* .calloc = */ + ![](Record_System *self, uint32_t nmemb, uint32_t size) { + self->push(true); + return report_alloc(self->name_, "calloc", nmemb * size, std::calloc(nmemb, size)); + }, + /* .realloc = */ + ![](Record_System *self, void *ptr, uint32_t size) { + self->push(true); + return report_alloc(self->name_, "realloc", size, std::realloc(ptr, size)); + }, + /* .free = */ + ![](Record_System *self, void *ptr) { std::free(ptr); }, +}; static constexpr Network_Funcs record_network_funcs = { /* .close = */ ![](Record_System *self, int sock) { return 0; }, @@ -326,11 +352,10 @@ static constexpr Network_Funcs record_network_funcs = { ![](Record_System *self, int sock, uint8_t *buf, size_t len, Network_Addr *addr) { assert(sock == 42); if (self->recvq.empty()) { - self->recording.push_back(0xff); - self->recording.push_back(0xff); + self->push("\xff\xff"); errno = EWOULDBLOCK; - if (DEBUG) { - std::printf("recvfrom: no data for %s\n", self->name_); + if (Fuzz_Data::DEBUG) { + std::printf("%s: recvfrom: no data\n", self->name_); } return -1; } @@ -350,13 +375,13 @@ static constexpr Network_Funcs record_network_funcs = { addr->size = sizeof(struct sockaddr); assert(recvlen > 0 && recvlen <= INT_MAX); - self->recording.push_back(recvlen >> 8); - self->recording.push_back(recvlen & 0xff); - if (DEBUG) { - std::printf("recvfrom: %zu (%02x, %02x) for %s\n", recvlen, self->recording.end()[-2], - self->recording.end()[-1], self->name_); + self->push(uint8_t(recvlen >> 8)); + self->push(uint8_t(recvlen & 0xff)); + if (Fuzz_Data::DEBUG) { + std::printf("%s: recvfrom: %zu (%02x, %02x)\n", self->name_, recvlen, + self->recording().end()[-2], self->recording().end()[-1]); } - self->recording.insert(self->recording.end(), buf, buf + recvlen); + self->push(buf, recvlen); return static_cast(recvlen); }, /* .send = */ @@ -390,11 +415,11 @@ static constexpr Random_Funcs record_random_funcs = { ![](Record_System *self, uint8_t *bytes, size_t length) { for (size_t i = 0; i < length; ++i) { bytes[i] = simple_rng(self->seed_) & 0xff; - self->recording.push_back(bytes[i]); + self->push(bytes[i]); } - if (DEBUG) { + if (Fuzz_Data::DEBUG) { std::printf( - "rng: %02x..%02x[%zu] -> %s\n", bytes[0], bytes[length - 1], length, self->name_); + "%s: rng: %02x..%02x[%zu]\n", self->name_, bytes[0], bytes[length - 1], length); } }, /* .random_uniform = */ @@ -412,7 +437,7 @@ Record_System::Record_System(Global &global, uint64_t seed, const char *name) , seed_(seed) , name_(name) { - sys->mono_time_callback = get_self_clock; + sys->mono_time_callback = [](void *self) { return static_cast(self)->clock; }; sys->mono_time_user_data = this; sys->mem = mem.get(); sys->ns = ns.get(); diff --git a/testing/fuzzing/fuzz_support.h b/testing/fuzzing/fuzz_support.h index 1735d6b..01e472c 100644 --- a/testing/fuzzing/fuzz_support.h +++ b/testing/fuzzing/fuzz_support.h @@ -6,6 +6,7 @@ #define C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H #include +#include #include #include #include @@ -17,12 +18,19 @@ #include "../../toxcore/tox.h" struct Fuzz_Data { - const uint8_t *data; - std::size_t size; + static constexpr bool DEBUG = false; + static constexpr std::size_t TRACE_TRAP = -1; // 579; +private: + const uint8_t *data_; + const uint8_t *base_; + std::size_t size_; + +public: Fuzz_Data(const uint8_t *input_data, std::size_t input_size) - : data(input_data) - , size(input_size) + : data_(input_data) + , base_(input_data) + , size_(input_size) { } @@ -30,25 +38,54 @@ struct Fuzz_Data { Fuzz_Data(const Fuzz_Data &rhs) = delete; struct Consumer { + const char *func; Fuzz_Data &fd; + operator bool() + { + // Special case because memcpy causes UB for bool (which can't be + // anything other than 0 or 1). + const bool val = fd.data_[0]; + if (DEBUG) { + std::printf("consume@%zu(%s): bool %s\n", fd.pos(), func, val ? "true" : "false"); + } + ++fd.data_; + --fd.size_; + return val; + } + template operator T() { - const uint8_t *bytes = fd.consume(sizeof(T)); + const uint8_t *bytes = fd.consume(func, sizeof(T)); T val; std::memcpy(&val, bytes, sizeof(T)); return val; } }; - Consumer consume1() { return Consumer{*this}; } + Consumer consume1(const char *func) { return Consumer{func, *this}; } + std::size_t size() const { return size_; } + std::size_t pos() const { return data_ - base_; } + const uint8_t *data() const { return data_; } + bool empty() const { return size_ == 0; } - const uint8_t *consume(std::size_t count) + const uint8_t *consume(const char *func, std::size_t count) { - const uint8_t *val = data; - data += count; - size -= count; + const uint8_t *val = data_; + if (DEBUG) { + if (pos() == TRACE_TRAP) { + __asm__("int $3"); + } + if (count == 1) { + std::printf("consume@%zu(%s): %d (0x%02x)\n", pos(), func, val[0], val[0]); + } else if (count != 0) { + std::printf("consume@%zu(%s): %02x..%02x[%zu]\n", pos(), func, val[0], + val[count - 1], count); + } + } + data_ += count; + size_ -= count; return val; } }; @@ -64,10 +101,10 @@ struct Fuzz_Data { * @endcode */ #define CONSUME1_OR_RETURN(TYPE, NAME, INPUT) \ - if (INPUT.size < sizeof(TYPE)) { \ + if (INPUT.size() < sizeof(TYPE)) { \ return; \ } \ - TYPE NAME = INPUT.consume1() + TYPE NAME = INPUT.consume1(__func__) /** @brief Consumes 1 byte of the fuzzer input or returns a value if no data * available. @@ -81,10 +118,10 @@ struct Fuzz_Data { * @endcode */ #define CONSUME1_OR_RETURN_VAL(TYPE, NAME, INPUT, VAL) \ - if (INPUT.size < sizeof(TYPE)) { \ + if (INPUT.size() < sizeof(TYPE)) { \ return VAL; \ } \ - TYPE NAME = INPUT.consume1() + TYPE NAME = INPUT.consume1(__func__) /** @brief Consumes SIZE bytes of the fuzzer input or returns if not enough data available. * @@ -98,39 +135,55 @@ struct Fuzz_Data { * @endcode */ #define CONSUME_OR_RETURN(DECL, INPUT, SIZE) \ - if (INPUT.size < SIZE) { \ + if (INPUT.size() < SIZE) { \ return; \ } \ - DECL = INPUT.consume(SIZE) + DECL = INPUT.consume(__func__, SIZE) #define CONSUME_OR_RETURN_VAL(DECL, INPUT, SIZE, VAL) \ - if (INPUT.size < SIZE) { \ + if (INPUT.size() < SIZE) { \ return VAL; \ } \ - DECL = INPUT.consume(SIZE) + DECL = INPUT.consume(__func__, SIZE) -inline void fuzz_select_target(uint8_t selector, Fuzz_Data &input) -{ - // The selector selected no function, so we do nothing and rely on the - // fuzzer to come up with a better selector. -} +#define CONSUME_OR_ABORT(DECL, INPUT, SIZE) \ + if (INPUT.size() < SIZE) { \ + abort(); \ + } \ + DECL = INPUT.consume(__func__, SIZE) -template -void fuzz_select_target(uint8_t selector, Fuzz_Data &input, Arg &&fn, Args &&...args) -{ - if (selector == sizeof...(Args)) { - return fn(input); +using Fuzz_Target = void (*)(Fuzz_Data &input); + +template +struct Fuzz_Target_Selector; + +template +struct Fuzz_Target_Selector { + static void select(uint8_t selector, Fuzz_Data &input) + { + if (selector == sizeof...(Args)) { + return Arg(input); + } + return Fuzz_Target_Selector::select(selector, input); } - return fuzz_select_target(selector - 1, input, std::forward(args)...); -} +}; -template -void fuzz_select_target(const uint8_t *data, std::size_t size, Args &&...args) +template <> +struct Fuzz_Target_Selector<> { + static void select(uint8_t selector, Fuzz_Data &input) + { + // The selector selected no function, so we do nothing and rely on the + // fuzzer to come up with a better selector. + } +}; + +template +void fuzz_select_target(const uint8_t *data, std::size_t size) { Fuzz_Data input{data, size}; CONSUME1_OR_RETURN(const uint8_t, selector, input); - return fuzz_select_target(selector, input, std::forward(args)...); + return Fuzz_Target_Selector::select(selector, input); } struct Memory; @@ -138,6 +191,18 @@ struct Network; struct Random; struct System { + /** @brief Deterministic system clock for this instance. + * + * Different instances can evolve independently. The time is initialised + * with a large number, because otherwise many zero-initialised "empty" + * friends inside toxcore will be "not timed out" for a long time, messing + * up some logic. Tox moderately depends on the clock being fairly high up + * (not close to 0). + * + * We make it a nice large round number so we can recognise it when debugging. + */ + uint64_t clock = 1000000000; + std::unique_ptr sys; std::unique_ptr mem; std::unique_ptr ns; @@ -150,16 +215,6 @@ struct System { // Not inline because sizeof of the above 2 structs is not known everywhere. ~System(); - /** @brief Deterministic system clock for this instance. - * - * Different instances can evolve independently. The time is initialised - * with a large number, because otherwise many zero-initialised "empty" - * friends inside toxcore will be "not timed out" for a long time, messing - * up some logic. Tox moderately depends on the clock being fairly high up - * (not close to 0). - */ - uint64_t clock = UINT32_MAX; - /** * During bootstrap, move the time forward a decent amount, because friend * finding and bootstrapping takes significant (around 10 seconds) wall @@ -210,6 +265,8 @@ struct Null_System : System { * initialised with the same seed will be identical (same keys, etc.). */ struct Record_System : System { + static constexpr bool DEBUG = Fuzz_Data::DEBUG; + /** @brief State shared between all tox instances. */ struct Global { /** @brief Bound UDP ports and their system instance. @@ -231,13 +288,60 @@ struct Record_System : System { std::deque>> recvq; uint16_t port = 0; //!< Sending port for this system instance. - std::vector recording; - explicit Record_System(Global &global, uint64_t seed, const char *name); + Record_System(Global &global, uint64_t seed, const char *name); + Record_System(const Record_System &) = delete; + Record_System operator=(const Record_System &) = delete; /** @brief Deposit a network packet in this instance's recvq. */ void receive(uint16_t send_port, const uint8_t *buf, size_t len); + + void push(bool byte) + { + if (DEBUG) { + if (recording_.size() == Fuzz_Data::TRACE_TRAP) { + __asm__("int $3"); + } + std::printf("%s: produce@%zu(bool %s)\n", name_, recording_.size(), byte ? "true" : "false"); + } + recording_.push_back(byte); + } + + void push(uint8_t byte) + { + if (DEBUG) { + if (recording_.size() == Fuzz_Data::TRACE_TRAP) { + __asm__("int $3"); + } + std::printf("%s: produce@%zu(%u (0x%02x))\n", name_, recording_.size(), byte, byte); + } + recording_.push_back(byte); + } + + void push(const uint8_t *bytes, std::size_t size) + { + if (DEBUG) { + if (recording_.size() == Fuzz_Data::TRACE_TRAP) { + __asm__("int $3"); + } + std::printf("%s: produce@%zu(%02x..%02x[%zu])\n", name_, recording_.size(), bytes[0], + bytes[size - 1], size); + } + recording_.insert(recording_.end(), bytes, bytes + size); + } + + template + void push(const char (&bytes)[N]) + { + push(reinterpret_cast(bytes), N - 1); + } + + const std::vector &recording() const { return recording_; } + std::vector take_recording() const { return std::move(recording_); } + +private: + std::vector recording_; }; /** @brief Enable debug logging. diff --git a/testing/fuzzing/protodump.cc b/testing/fuzzing/protodump.cc index 5b07569..70ab6c8 100644 --- a/testing/fuzzing/protodump.cc +++ b/testing/fuzzing/protodump.cc @@ -17,8 +17,8 @@ * * Usage: * - * bazel build //c-toxcore/testing/fuzzing:protodump_bin && \ - * bazel-bin/c-toxcore/testing/fuzzing/protodump_bin + * bazel build //c-toxcore/testing/fuzzing:protodump && \ + * bazel-bin/c-toxcore/testing/fuzzing/protodump */ #include #include @@ -165,15 +165,14 @@ void setup_callbacks(Tox_Dispatch *dispatch) }); } -void dump(std::vector &recording, const char *filename) +void dump(std::vector recording, const char *filename) { std::printf("%zu bytes: %s\n", recording.size(), filename); std::ofstream(filename, std::ios::binary) .write(reinterpret_cast(recording.data()), recording.size()); - recording.clear(); } -void RecordBootstrap() +void RecordBootstrap(const char *init, const char *bootstrap) { Record_System::Global global; @@ -258,13 +257,17 @@ void RecordBootstrap() sys1.clock += clock_increment; sys2.clock += clock_increment; - sys1.recording.push_back(clock_increment); - sys2.recording.push_back(clock_increment); + if (Fuzz_Data::DEBUG) { + printf("tox1: rng: %d (for clock)\n", clock_increment); + printf("tox2: rng: %d (for clock)\n", clock_increment); + } + sys1.push(clock_increment); + sys2.push(clock_increment); }; while (tox_self_get_connection_status(tox1) == TOX_CONNECTION_NONE || tox_self_get_connection_status(tox2) == TOX_CONNECTION_NONE) { - if (DEBUG) { + if (Fuzz_Data::DEBUG) { std::printf("tox1: %d, tox2: %d\n", tox_self_get_connection_status(tox1), tox_self_get_connection_status(tox2)); } @@ -279,7 +282,7 @@ void RecordBootstrap() while (tox_friend_get_connection_status(tox2, friend_number, nullptr) == TOX_CONNECTION_NONE || tox_friend_get_connection_status(tox1, 0, nullptr) == TOX_CONNECTION_NONE) { - if (DEBUG) { + if (Fuzz_Data::DEBUG) { std::printf("tox1: %d, tox2: %d, tox1 -> tox2: %d, tox2 -> tox1: %d\n", tox_self_get_connection_status(tox1), tox_self_get_connection_status(tox2), tox_friend_get_connection_status(tox1, 0, nullptr), @@ -290,10 +293,10 @@ void RecordBootstrap() std::printf("tox clients connected\n"); - dump(sys1.recording, "tools/toktok-fuzzer/init/e2e_fuzz_test.dat"); + dump(sys1.take_recording(), init); while (done1 < MESSAGE_COUNT && done2 < MESSAGE_COUNT) { - if (DEBUG) { + if (Fuzz_Data::DEBUG) { std::printf("tox1: %d, tox2: %d, tox1 -> tox2: %d, tox2 -> tox1: %d\n", tox_self_get_connection_status(tox1), tox_self_get_connection_status(tox2), tox_friend_get_connection_status(tox1, 0, nullptr), @@ -308,9 +311,18 @@ void RecordBootstrap() tox_kill(tox2); tox_kill(tox1); - dump(sys1.recording, "tools/toktok-fuzzer/corpus/e2e_fuzz_test/bootstrap.dat"); + dump(sys1.recording(), bootstrap); } } -int main(void) { RecordBootstrap(); } +int main(int argc, char *argv[]) +{ + const char *init = "tools/toktok-fuzzer/init/e2e_fuzz_test.dat"; + const char *bootstrap = "tools/toktok-fuzzer/corpus/e2e_fuzz_test/bootstrap.dat"; + if (argc == 3) { + init = argv[1]; + bootstrap = argv[2]; + } + RecordBootstrap(init, bootstrap); +} diff --git a/testing/fuzzing/protodump_reduce.cc b/testing/fuzzing/protodump_reduce.cc index 8cc475b..d824bda 100644 --- a/testing/fuzzing/protodump_reduce.cc +++ b/testing/fuzzing/protodump_reduce.cc @@ -11,14 +11,7 @@ namespace { -/** - * Whether to abort the program if a friend connection can be established. - * - * This is useful to make the fuzzer produce minimal startup data so the - * interesting part of the fuzzer (the part that comes after the friend - * connection is established) can run sooner and thus more frequently. - */ -constexpr bool REDUCE_PROTODUMP = false; +constexpr bool PROTODUMP_DEBUG = Fuzz_Data::DEBUG; void setup_callbacks(Tox_Dispatch *dispatch) { @@ -32,10 +25,7 @@ void setup_callbacks(Tox_Dispatch *dispatch) }); tox_events_callback_conference_invite( dispatch, [](Tox *tox, const Tox_Event_Conference_Invite *event, void *user_data) { - const uint32_t friend_number = tox_event_conference_invite_get_friend_number(event); - const uint8_t *cookie = tox_event_conference_invite_get_cookie(event); - const uint32_t cookie_length = tox_event_conference_invite_get_cookie_length(event); - tox_conference_join(tox, friend_number, cookie, cookie_length, nullptr); + assert(event == nullptr); }); tox_events_callback_conference_message( dispatch, [](Tox *tox, const Tox_Event_Conference_Message *event, void *user_data) { @@ -59,9 +49,7 @@ void setup_callbacks(Tox_Dispatch *dispatch) }); tox_events_callback_file_recv( dispatch, [](Tox *tox, const Tox_Event_File_Recv *event, void *user_data) { - const uint32_t friend_number = tox_event_file_recv_get_friend_number(event); - const uint32_t file_number = tox_event_file_recv_get_file_number(event); - tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME, nullptr); + assert(event == nullptr); }); tox_events_callback_file_recv_chunk( dispatch, [](Tox *tox, const Tox_Event_File_Recv_Chunk *event, void *user_data) { @@ -76,58 +64,65 @@ void setup_callbacks(Tox_Dispatch *dispatch) // OK: friend came online. const uint32_t friend_number = tox_event_friend_connection_status_get_friend_number(event); - assert(friend_number != UINT32_MAX); + assert(friend_number == 0); + const uint8_t message = 'A'; + Tox_Err_Friend_Send_Message err; + tox_friend_send_message(tox, friend_number, TOX_MESSAGE_TYPE_NORMAL, &message, 1, &err); + assert(err == TOX_ERR_FRIEND_SEND_MESSAGE_OK); }); tox_events_callback_friend_lossless_packet( dispatch, [](Tox *tox, const Tox_Event_Friend_Lossless_Packet *event, void *user_data) { - const uint32_t friend_number - = tox_event_friend_lossless_packet_get_friend_number(event); - const uint32_t data_length = tox_event_friend_lossless_packet_get_data_length(event); - const uint8_t *data = tox_event_friend_lossless_packet_get_data(event); - tox_friend_send_lossless_packet(tox, friend_number, data, data_length, nullptr); + assert(event == nullptr); }); tox_events_callback_friend_lossy_packet( dispatch, [](Tox *tox, const Tox_Event_Friend_Lossy_Packet *event, void *user_data) { - const uint32_t friend_number = tox_event_friend_lossy_packet_get_friend_number(event); - const uint32_t data_length = tox_event_friend_lossy_packet_get_data_length(event); - const uint8_t *data = tox_event_friend_lossy_packet_get_data(event); - tox_friend_send_lossy_packet(tox, friend_number, data, data_length, nullptr); + assert(event == nullptr); }); tox_events_callback_friend_message( dispatch, [](Tox *tox, const Tox_Event_Friend_Message *event, void *user_data) { const uint32_t friend_number = tox_event_friend_message_get_friend_number(event); - const Tox_Message_Type type = tox_event_friend_message_get_type(event); + assert(friend_number == 0); const uint32_t message_length = tox_event_friend_message_get_message_length(event); + assert(message_length == 1); const uint8_t *message = tox_event_friend_message_get_message(event); - tox_friend_send_message(tox, friend_number, type, message, message_length, nullptr); + const uint8_t reply = message[0] + 1; + Tox_Err_Friend_Send_Message err; + tox_friend_send_message(tox, friend_number, TOX_MESSAGE_TYPE_NORMAL, &reply, 1, &err); + assert(err == TOX_ERR_FRIEND_SEND_MESSAGE_OK); }); tox_events_callback_friend_name( dispatch, [](Tox *tox, const Tox_Event_Friend_Name *event, void *user_data) { - // OK: friend name received. + const uint32_t friend_number = tox_event_friend_name_get_friend_number(event); + assert(friend_number == 0); }); tox_events_callback_friend_read_receipt( dispatch, [](Tox *tox, const Tox_Event_Friend_Read_Receipt *event, void *user_data) { - // OK: message has been received. + const uint32_t friend_number = tox_event_friend_read_receipt_get_friend_number(event); + assert(friend_number == 0); + const uint32_t message_id = tox_event_friend_read_receipt_get_message_id(event); + uint32_t *done = static_cast(user_data); + *done = std::max(*done, message_id); }); tox_events_callback_friend_request( dispatch, [](Tox *tox, const Tox_Event_Friend_Request *event, void *user_data) { Tox_Err_Friend_Add err; tox_friend_add_norequest(tox, tox_event_friend_request_get_public_key(event), &err); - assert(err == TOX_ERR_FRIEND_ADD_OK || err == TOX_ERR_FRIEND_ADD_OWN_KEY - || err == TOX_ERR_FRIEND_ADD_ALREADY_SENT - || err == TOX_ERR_FRIEND_ADD_BAD_CHECKSUM); }); tox_events_callback_friend_status( dispatch, [](Tox *tox, const Tox_Event_Friend_Status *event, void *user_data) { - // OK: friend status received. + const uint32_t friend_number = tox_event_friend_status_get_friend_number(event); + assert(friend_number == 0); }); tox_events_callback_friend_status_message( dispatch, [](Tox *tox, const Tox_Event_Friend_Status_Message *event, void *user_data) { - // OK: friend status message received. + const uint32_t friend_number = tox_event_friend_status_message_get_friend_number(event); + assert(friend_number == 0); }); tox_events_callback_friend_typing( dispatch, [](Tox *tox, const Tox_Event_Friend_Typing *event, void *user_data) { - // OK: friend may be typing. + const uint32_t friend_number = tox_event_friend_typing_get_friend_number(event); + assert(friend_number == 0); + assert(!tox_event_friend_typing_get_typing(event)); }); tox_events_callback_self_connection_status( dispatch, [](Tox *tox, const Tox_Event_Self_Connection_Status *event, void *user_data) { @@ -137,6 +132,15 @@ void setup_callbacks(Tox_Dispatch *dispatch) void TestEndToEnd(Fuzz_Data &input) { + /** + * Whether to abort the program if a friend connection can be established. + * + * This is useful to make the fuzzer produce minimal startup data so the + * interesting part of the fuzzer (the part that comes after the friend + * connection is established) can run sooner and thus more frequently. + */ + const bool PROTODUMP_REDUCE = getenv("PROTODUMP_REDUCE") != nullptr; + Fuzz_System sys(input); Ptr opts(tox_options_new(nullptr), tox_options_free); @@ -148,7 +152,7 @@ void TestEndToEnd(Fuzz_Data &input) [](Tox *tox, Tox_Log_Level level, const char *file, uint32_t line, const char *func, const char *message, void *user_data) { // Log to stdout. - if (DEBUG) { + if (PROTODUMP_DEBUG) { std::printf("[tox1] %c %s:%d(%s): %s\n", tox_log_level_name(level), file, line, func, message); } @@ -171,20 +175,26 @@ void TestEndToEnd(Fuzz_Data &input) assert(dispatch != nullptr); setup_callbacks(dispatch); - while (input.size > 0) { + while (!input.empty()) { Tox_Err_Events_Iterate error_iterate; Tox_Events *events = tox_events_iterate(tox, true, &error_iterate); - assert(tox_events_equal(tox_get_system(tox), events, events)); + tox_events_equal(tox_get_system(tox), events, events); // TODO(iphydf): assert? tox_dispatch_invoke(dispatch, events, tox, nullptr); tox_events_free(events); - sys.clock += std::max(System::MIN_ITERATION_INTERVAL, random_u08(sys.rng.get())); + const uint8_t clock_increment = random_u08(sys.rng.get()); + if (PROTODUMP_DEBUG) { + printf("clock increment: %d\n", clock_increment); + } + sys.clock += std::max(System::MIN_ITERATION_INTERVAL, clock_increment); } - if (REDUCE_PROTODUMP) { + if (PROTODUMP_REDUCE) { assert(tox_friend_get_connection_status(tox, 0, nullptr) != 2); } else { printf("friend: %d\n", tox_friend_get_connection_status(tox, 0, nullptr)); printf("self: %d\n", tox_self_get_connection_status(tox)); + assert(tox_friend_get_connection_status(tox, 0, nullptr) == 2); + assert(input.empty()); } tox_dispatch_free(dispatch); diff --git a/testing/fuzzing/rebuild_protodump b/testing/fuzzing/rebuild_protodump new file mode 100755 index 0000000..dafeb99 --- /dev/null +++ b/testing/fuzzing/rebuild_protodump @@ -0,0 +1,14 @@ +#!/bin/sh + +set -eux + +bazel test --config=asan-libfuzzer //c-toxcore/testing/fuzzing:protodump_reduce_test + +bazel build --config=asan-libfuzzer //c-toxcore/testing/fuzzing:protodump +bazel-bin/c-toxcore/testing/fuzzing/protodump | grep -v '^tox2' + +bazel build --config=asan-libfuzzer //c-toxcore/testing/fuzzing:protodump_reduce_bin +bazel-bin/c-toxcore/testing/fuzzing/protodump_reduce_bin "$PWD/tools/toktok-fuzzer/init/e2e_fuzz_test.dat" + +#export PROTODUMP_REDUCE=1 +#bazel-bin/c-toxcore/testing/fuzzing/protodump_reduce_bin -minimize_crash=1 "$PWD/tools/toktok-fuzzer/init/e2e_fuzz_test.dat" diff --git a/testing/fuzzing/toxsave_harness.cc b/testing/fuzzing/toxsave_fuzz_test.cc similarity index 96% rename from testing/fuzzing/toxsave_harness.cc rename to testing/fuzzing/toxsave_fuzz_test.cc index d380299..a5ac0f1 100644 --- a/testing/fuzzing/toxsave_harness.cc +++ b/testing/fuzzing/toxsave_fuzz_test.cc @@ -17,7 +17,7 @@ void TestSaveDataLoading(Fuzz_Data &input) assert(tox_options != nullptr); assert(error_options == TOX_ERR_OPTIONS_NEW_OK); - const size_t savedata_size = input.size; + const size_t savedata_size = input.size(); CONSUME_OR_RETURN(const uint8_t *savedata, input, savedata_size); Null_System sys; diff --git a/testing/run_afl.sh b/testing/run_afl.sh index 232904d..4593aab 100755 --- a/testing/run_afl.sh +++ b/testing/run_afl.sh @@ -5,7 +5,7 @@ COMMON_CMAKE_OPTIONS="-DCMAKE_C_COMPILER=afl-clang-lto -DCMAKE_CXX_COMPILER=afl- # move to repo root cd ../ -# build fuzzer target UBSAN +# build fuzz_test target UBSAN mkdir -p _afl_build_ubsan cd _afl_build_ubsan @@ -14,14 +14,14 @@ export AFL_USE_UBSAN=1 # build c-toxcore using afl instrumentation cmake -DCMAKE_BUILD_TYPE=Debug "$COMMON_CMAKE_OPTIONS" .. -# build fuzzer target -cmake --build ./ --target bootstrap_fuzzer +# build fuzz_test target +cmake --build ./ --target bootstrap_fuzz_test unset AFL_USE_UBSAN cd .. -# build fuzzer target MSAN +# build fuzz_test target MSAN mkdir -p _afl_build_msan cd _afl_build_msan @@ -30,14 +30,14 @@ export AFL_USE_MSAN=1 # build c-toxcore using afl instrumentation cmake -DCMAKE_BUILD_TYPE=Debug "$COMMON_CMAKE_OPTIONS" .. -# build fuzzer target -cmake --build ./ --target bootstrap_fuzzer +# build fuzz_test target +cmake --build ./ --target bootstrap_fuzz_test unset AFL_USE_MSAN cd .. -# build fuzzer target ASAN +# build fuzz_test target ASAN mkdir -p _afl_build_asan cd _afl_build_asan @@ -46,26 +46,26 @@ export AFL_USE_ASAN=1 # build c-toxcore using afl instrumentation cmake -DCMAKE_BUILD_TYPE=Debug "$COMMON_CMAKE_OPTIONS" .. -# build fuzzer target -cmake --build ./ --target bootstrap_fuzzer +# build fuzz_test target +cmake --build ./ --target bootstrap_fuzz_test unset AFL_USE_ASAN cd .. -# build fuzzer target without sanitizers for afl-tmin +# build fuzz_test target without sanitizers for afl-tmin mkdir -p _afl_build cd _afl_build # build c-toxcore using afl instrumentation cmake -DCMAKE_BUILD_TYPE=Debug "$COMMON_CMAKE_OPTIONS" .. -# build fuzzer target -cmake --build ./ --target bootstrap_fuzzer +# build fuzz_test target +cmake --build ./ --target bootstrap_fuzz_test cd .. -# build fuzzer target with CmpLog +# build fuzz_test target with CmpLog mkdir -p _afl_build_cmplog cd _afl_build_cmplog @@ -74,27 +74,27 @@ export AFL_LLVM_CMPLOG=1 # build c-toxcore using afl instrumentation cmake -DCMAKE_BUILD_TYPE=Debug "$COMMON_CMAKE_OPTIONS" .. -# build fuzzer target -cmake --build ./ --target bootstrap_fuzzer +# build fuzz_test target +cmake --build ./ --target bootstrap_fuzz_test unset AFL_LLVM_CMPLOG cd .. -# build fuzzer target for code coverage +# build fuzz_test target for code coverage mkdir -p _cov_build cd _cov_build # build c-toxcore using afl instrumentation cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-fprofile-arcs -ftest-coverage" -DCMAKE_C_FLAGS="-fprofile-arcs -ftest-coverage" -DCMAKE_VERBOSE_MAKEFILE=ON "$COMMON_CMAKE_OPTIONS" .. -# build fuzzer target -cmake --build ./ --target bootstrap_fuzzer +# build fuzz_test target +cmake --build ./ --target bootstrap_fuzz_test # back to repo root cd ../ -# Create fuzzer working directory +# Create fuzz_test working directory mkdir -p _afl_out @@ -106,21 +106,21 @@ export AFL_AUTORESUME=1 # faster startup export AFL_FAST_CAL=1 -echo "connect to the fuzzers using: screen -x fuzz" +echo "connect to the fuzz_tests using: screen -x fuzz" echo "if fuzzing doesn't start execute the following as root:" echo "" echo "echo core >/proc/sys/kernel/core_pattern" echo "echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor" -# Main fuzzer, keeps complete corpus -screen -dmS fuzz afl-fuzz -M fuzz0 "$AFL_ARGS" -c ./_afl_build_cmplog/bootstrap_fuzzer ./_afl_build/bootstrap_fuzzer +# Main fuzz_test, keeps complete corpus +screen -dmS fuzz afl-fuzz -M fuzz0 "$AFL_ARGS" -c ./_afl_build_cmplog/bootstrap_fuzz_test ./_afl_build/bootstrap_fuzz_test sleep 10s -# Secondary fuzzers -screen -S fuzz -X screen afl-fuzz -S fuzz1 "$AFL_ARGS" -- ./_afl_build_msan/bootstrap_fuzzer +# Secondary fuzz_tests +screen -S fuzz -X screen afl-fuzz -S fuzz1 "$AFL_ARGS" -- ./_afl_build_msan/bootstrap_fuzz_test sleep 1s -screen -S fuzz -X screen afl-fuzz -S fuzz2 "$AFL_ARGS" ./_afl_build_ubsan/bootstrap_fuzzer +screen -S fuzz -X screen afl-fuzz -S fuzz2 "$AFL_ARGS" ./_afl_build_ubsan/bootstrap_fuzz_test sleep 1s -screen -S fuzz -X screen afl-fuzz -S fuzz3 "$AFL_ARGS" ./_afl_build_asan/bootstrap_fuzzer +screen -S fuzz -X screen afl-fuzz -S fuzz3 "$AFL_ARGS" ./_afl_build_asan/bootstrap_fuzz_test diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel index a4f9c88..becd3e8 100644 --- a/toxcore/BUILD.bazel +++ b/toxcore/BUILD.bazel @@ -1,6 +1,5 @@ -load("@rules_cc//cc:defs.bzl", "cc_test") +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test") -load("//tools:no_undefined.bzl", "cc_library") exports_files( srcs = [ @@ -12,6 +11,7 @@ exports_files( cc_library( name = "test_util", + testonly = True, srcs = ["test_util.cc"], hdrs = ["test_util.hh"], ) @@ -52,6 +52,17 @@ cc_library( ], ) +cc_library( + name = "mem_test_util", + testonly = True, + srcs = ["mem_test_util.cc"], + hdrs = ["mem_test_util.hh"], + deps = [ + ":mem", + ":test_util", + ], +) + cc_test( name = "mem_test", size = "small", @@ -162,6 +173,7 @@ cc_library( cc_library( name = "crypto_core_test_util", + testonly = True, srcs = ["crypto_core_test_util.cc"], hdrs = ["crypto_core_test_util.hh"], deps = [ @@ -240,6 +252,7 @@ cc_test( size = "small", srcs = ["mono_time_test.cc"], deps = [ + ":mem_test_util", ":mono_time", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", @@ -292,6 +305,7 @@ cc_library( cc_library( name = "network_test_util", + testonly = True, srcs = ["network_test_util.cc"], hdrs = ["network_test_util.hh"], deps = [ @@ -307,6 +321,7 @@ cc_test( srcs = ["network_test.cc"], deps = [ ":network", + ":network_test_util", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", ], @@ -341,6 +356,7 @@ cc_test( size = "small", srcs = ["ping_array_test.cc"], deps = [ + ":mem_test_util", ":mono_time", ":ping_array", "@com_google_googletest//:gtest", @@ -399,6 +415,7 @@ cc_library( cc_library( name = "DHT_test_util", + testonly = True, srcs = ["DHT_test_util.cc"], hdrs = ["DHT_test_util.hh"], deps = [ @@ -418,6 +435,7 @@ cc_test( ":DHT", ":DHT_test_util", ":crypto_core", + ":mem_test_util", ":network_test_util", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", @@ -468,7 +486,7 @@ cc_fuzz_test( name = "forwarding_fuzz_test", size = "small", srcs = ["forwarding_fuzz_test.cc"], - #corpus = ["//tools/toktok-fuzzer/corpus:forwarding_fuzz_test"], + corpus = ["//tools/toktok-fuzzer/corpus:forwarding_fuzz_test"], deps = [ ":forwarding", "//c-toxcore/testing/fuzzing:fuzz_support", @@ -641,12 +659,26 @@ cc_test( srcs = ["group_announce_test.cc"], deps = [ ":group_announce", + ":mem_test_util", ":mono_time", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", ], ) +cc_fuzz_test( + name = "group_announce_fuzz_test", + size = "small", + testonly = True, + srcs = ["group_announce_fuzz_test.cc"], + corpus = ["//tools/toktok-fuzzer/corpus:group_announce_fuzz_test"], + deps = [ + ":group_announce", + ":mem_test_util", + "//c-toxcore/testing/fuzzing:fuzz_support", + ], +) + cc_library( name = "group_onion_announce", srcs = ["group_onion_announce.c"], @@ -663,17 +695,6 @@ cc_library( ], ) -cc_fuzz_test( - name = "group_announce_fuzz_test", - size = "small", - srcs = ["group_announce_fuzz_test.cc"], - #corpus = ["//tools/toktok-fuzzer/corpus:group_announce_fuzz_test"], - deps = [ - ":group_announce", - "//c-toxcore/testing/fuzzing:fuzz_support", - ], -) - cc_library( name = "onion_client", srcs = ["onion_client.c"], @@ -759,8 +780,10 @@ cc_test( srcs = ["group_moderation_test.cc"], deps = [ ":crypto_core", + ":crypto_core_test_util", ":group_moderation", ":logger", + ":mem_test_util", ":util", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", @@ -770,10 +793,12 @@ cc_test( cc_fuzz_test( name = "group_moderation_fuzz_test", size = "small", + testonly = True, srcs = ["group_moderation_fuzz_test.cc"], corpus = ["//tools/toktok-fuzzer/corpus:group_moderation_fuzz_test"], deps = [ ":group_moderation", + ":mem_test_util", "//c-toxcore/testing/fuzzing:fuzz_support", ], ) diff --git a/toxcore/DHT_fuzz_test.cc b/toxcore/DHT_fuzz_test.cc index 7a9c719..e741e9b 100644 --- a/toxcore/DHT_fuzz_test.cc +++ b/toxcore/DHT_fuzz_test.cc @@ -17,8 +17,8 @@ void TestHandleRequest(Fuzz_Data &input) 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, input.data, input.size); + handle_request(self_public_key, self_secret_key, public_key, request, &request_id, input.data(), + input.size()); } void TestUnpackNodes(Fuzz_Data &input) @@ -28,8 +28,8 @@ void TestUnpackNodes(Fuzz_Data &input) const uint16_t node_count = 5; Node_format nodes[node_count]; uint16_t processed_data_len; - const int packed_count - = unpack_nodes(nodes, node_count, &processed_data_len, input.data, input.size, tcp_enabled); + const int packed_count = unpack_nodes( + nodes, node_count, &processed_data_len, input.data(), input.size(), tcp_enabled); if (packed_count > 0) { Logger *logger = logger_new(); std::vector packed(packed_count * PACKED_NODE_SIZE_IP6); @@ -45,8 +45,11 @@ void TestUnpackNodes(Fuzz_Data &input) uint16_t processed_data_len2; const int packed_count2 = unpack_nodes( nodes2, node_count, &processed_data_len2, packed.data(), packed.size(), tcp_enabled); + (void)packed_count2; +#if 0 assert(processed_data_len2 == processed_data_len); assert(packed_count2 == packed_count); +#endif assert(memcmp(nodes, nodes2, sizeof(Node_format) * packed_count) == 0); } } @@ -56,6 +59,6 @@ void TestUnpackNodes(Fuzz_Data &input) extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - fuzz_select_target(data, size, TestHandleRequest, TestUnpackNodes); + fuzz_select_target(data, size); return 0; } diff --git a/toxcore/DHT_test.cc b/toxcore/DHT_test.cc index 9581258..34841c7 100644 --- a/toxcore/DHT_test.cc +++ b/toxcore/DHT_test.cc @@ -11,6 +11,7 @@ #include "DHT_test_util.hh" #include "crypto_core.h" #include "crypto_core_test_util.hh" +#include "mem_test_util.hh" #include "network_test_util.hh" namespace { @@ -326,8 +327,8 @@ TEST(Request, CreateAndParse) TEST(AnnounceNodes, SetAndTest) { Test_Random rng; - const Network *ns = system_network(); - const Memory *mem = system_memory(); + Test_Memory mem; + Test_Network ns; Logger *log = logger_new(); ASSERT_NE(log, nullptr); diff --git a/toxcore/crypto_core.c b/toxcore/crypto_core.c index 44ce4a1..1f9fde7 100644 --- a/toxcore/crypto_core.c +++ b/toxcore/crypto_core.c @@ -437,14 +437,10 @@ void new_symmetric_key(const Random *rng, uint8_t *key) int32_t crypto_new_keypair(const Random *rng, uint8_t *public_key, uint8_t *secret_key) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION random_bytes(rng, secret_key, CRYPTO_SECRET_KEY_SIZE); memset(public_key, 0, CRYPTO_PUBLIC_KEY_SIZE); // Make MSAN happy crypto_derive_public_key(public_key, secret_key); return 0; -#else - return crypto_box_keypair(public_key, secret_key); -#endif } void crypto_derive_public_key(uint8_t *public_key, const uint8_t *secret_key) diff --git a/toxcore/forwarding_fuzz_test.cc b/toxcore/forwarding_fuzz_test.cc index c4fc8a2..99d2121 100644 --- a/toxcore/forwarding_fuzz_test.cc +++ b/toxcore/forwarding_fuzz_test.cc @@ -89,6 +89,6 @@ void TestForwardReply(Fuzz_Data &input) extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - fuzz_select_target(data, size, TestSendForwardRequest, TestForwardReply); + fuzz_select_target(data, size); return 0; } diff --git a/toxcore/group_announce_fuzz_test.cc b/toxcore/group_announce_fuzz_test.cc index be9d06e..eba5e87 100644 --- a/toxcore/group_announce_fuzz_test.cc +++ b/toxcore/group_announce_fuzz_test.cc @@ -6,6 +6,7 @@ #include #include "../testing/fuzzing/fuzz_support.h" +#include "mem_test_util.hh" namespace { @@ -19,7 +20,7 @@ void TestUnpackAnnouncesList(Fuzz_Data &input) CONSUME1_OR_RETURN(const uint16_t, packed_size, input); Logger *logger = logger_new(); - if (gca_unpack_announces_list(logger, input.data, input.size, announces.data(), max_count) + if (gca_unpack_announces_list(logger, input.data(), input.size(), announces.data(), max_count) != -1) { // Always allocate at least something to avoid passing nullptr to functions below. std::vector packed(packed_size + 1); @@ -38,7 +39,7 @@ void TestUnpackPublicAnnounce(Fuzz_Data &input) CONSUME1_OR_RETURN(const uint16_t, packed_size, input); Logger *logger = logger_new(); - if (gca_unpack_public_announce(logger, input.data, input.size, &public_announce) != -1) { + if (gca_unpack_public_announce(logger, input.data(), input.size(), &public_announce) != -1) { // Always allocate at least something to avoid passing nullptr to functions below. std::vector packed(packed_size + 1); gca_pack_public_announce(logger, packed.data(), packed_size, &public_announce); @@ -48,19 +49,19 @@ void TestUnpackPublicAnnounce(Fuzz_Data &input) void TestDoGca(Fuzz_Data &input) { - const Memory *mem = system_memory(); + Test_Memory mem; std::unique_ptr logger(logger_new(), logger_kill); - std::unique_ptr> mono_time( - mono_time_new(mem, nullptr, nullptr), [mem](Mono_Time *ptr) { mono_time_free(mem, ptr); }); - assert(mono_time != nullptr); + uint64_t clock = 1; - mono_time_set_current_time_callback( - mono_time.get(), [](void *user_data) { return *static_cast(user_data); }, - &clock); + std::unique_ptr> mono_time( + mono_time_new( + mem, [](void *user_data) { return *static_cast(user_data); }, &clock), + [mem](Mono_Time *ptr) { mono_time_free(mem, ptr); }); + assert(mono_time != nullptr); std::unique_ptr gca(new_gca_list(), kill_gca); assert(gca != nullptr); - while (input.size > 0) { + while (!input.empty()) { CONSUME1_OR_RETURN(const uint8_t, choice, input); switch (choice) { case 0: { @@ -84,7 +85,8 @@ void TestDoGca(Fuzz_Data &input) case 2: { // Get announces. CONSUME1_OR_RETURN(const uint8_t, max_nodes, input); - std::vector gc_announces(max_nodes); + // Always allocate at least something to avoid passing nullptr to functions below. + std::vector gc_announces(max_nodes + 1); CONSUME_OR_RETURN(const uint8_t *chat_id, input, CHAT_ID_SIZE); CONSUME_OR_RETURN(const uint8_t *except_public_key, input, ENC_PUBLIC_KEY_SIZE); gca_get_announces( @@ -106,6 +108,6 @@ void TestDoGca(Fuzz_Data &input) extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - fuzz_select_target(data, size, TestUnpackAnnouncesList, TestUnpackPublicAnnounce, TestDoGca); + fuzz_select_target(data, size); return 0; } diff --git a/toxcore/group_announce_test.cc b/toxcore/group_announce_test.cc index 6252e42..8945267 100644 --- a/toxcore/group_announce_test.cc +++ b/toxcore/group_announce_test.cc @@ -2,13 +2,14 @@ #include +#include "mem_test_util.hh" #include "mono_time.h" namespace { struct Announces : ::testing::Test { protected: - const Memory *mem_ = system_memory(); + Test_Memory mem_; uint64_t clock_ = 1000; Mono_Time *mono_time_ = nullptr; GC_Announces_List *gca_ = nullptr; diff --git a/toxcore/group_chats.c b/toxcore/group_chats.c index 4157b52..6ea64ba 100644 --- a/toxcore/group_chats.c +++ b/toxcore/group_chats.c @@ -1442,6 +1442,9 @@ non_null(1, 2, 3, 5, 6) nullable(4) static int group_packet_unwrap(const Logger *log, const GC_Connection *gconn, uint8_t *data, uint64_t *message_id, uint8_t *packet_type, const uint8_t *packet, uint16_t length) { + assert(data != nullptr); + assert(packet != nullptr); + if (length <= CRYPTO_NONCE_SIZE) { LOGGER_FATAL(log, "Invalid packet length: %u", length); return -1; @@ -4073,17 +4076,11 @@ int gc_founder_set_password(GC_Chat *chat, const uint8_t *password, uint16_t pas return -1; } - uint8_t *oldpasswd = nullptr; const uint16_t oldlen = chat->shared_state.password_length; + uint8_t *oldpasswd = memdup(chat->shared_state.password, oldlen); - if (oldlen > 0) { - oldpasswd = (uint8_t *)malloc(oldlen); - - if (oldpasswd == nullptr) { - return -4; - } - - memcpy(oldpasswd, chat->shared_state.password, oldlen); + if (oldpasswd == nullptr && oldlen > 0) { + return -4; } if (!set_gc_password_local(chat, password, password_length)) { diff --git a/toxcore/group_connection.c b/toxcore/group_connection.c index 7566390..9abf73e 100644 --- a/toxcore/group_connection.c +++ b/toxcore/group_connection.c @@ -101,16 +101,12 @@ static bool create_array_entry(const Logger *log, const Mono_Time *mono_time, GC } if (length == 0) { - if (data != nullptr) { - LOGGER_FATAL(log, "Got non-null data with zero length (type %d)", packet_type); // should never happen - return false; - } - array_entry->data = nullptr; array_entry->data_length = 0; } else { - if (data == nullptr) { - LOGGER_FATAL(log, "Got null data with non-zero length (type %u)", packet_type); // should never happen + if (data == nullptr) { // should never happen + LOGGER_FATAL(log, "Got null data with non-zero length (length: %u, type %u)", + length, packet_type); return false; } diff --git a/toxcore/group_moderation_fuzz_test.cc b/toxcore/group_moderation_fuzz_test.cc index 6adfd9a..9edca19 100644 --- a/toxcore/group_moderation_fuzz_test.cc +++ b/toxcore/group_moderation_fuzz_test.cc @@ -1,14 +1,16 @@ #include "group_moderation.h" #include "../testing/fuzzing/fuzz_support.h" +#include "mem_test_util.hh" namespace { void TestModListUnpack(Fuzz_Data &input) { CONSUME1_OR_RETURN(const uint16_t, num_mods, input); - Moderation mods{system_memory()}; - mod_list_unpack(&mods, input.data, input.size, num_mods); + Test_Memory mem; + Moderation mods{mem}; + mod_list_unpack(&mods, input.data(), input.size(), num_mods); mod_list_cleanup(&mods); } @@ -17,7 +19,7 @@ void TestSanctionsListUnpack(Fuzz_Data &input) Mod_Sanction sanctions[10]; Mod_Sanction_Creds creds; uint16_t processed_data_len; - sanctions_list_unpack(sanctions, &creds, 10, input.data, input.size, &processed_data_len); + sanctions_list_unpack(sanctions, &creds, 10, input.data(), input.size(), &processed_data_len); } void TestSanctionCredsUnpack(Fuzz_Data &input) @@ -32,7 +34,7 @@ void TestSanctionCredsUnpack(Fuzz_Data &input) extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - fuzz_select_target( - data, size, TestModListUnpack, TestSanctionsListUnpack, TestSanctionCredsUnpack); + fuzz_select_target( + data, size); return 0; } diff --git a/toxcore/group_moderation_test.cc b/toxcore/group_moderation_test.cc index 3524abe..24e32ba 100644 --- a/toxcore/group_moderation_test.cc +++ b/toxcore/group_moderation_test.cc @@ -7,7 +7,9 @@ #include #include "crypto_core.h" +#include "crypto_core_test_util.hh" #include "logger.h" +#include "mem_test_util.hh" #include "util.h" namespace { @@ -18,7 +20,8 @@ using ModerationHash = std::array; TEST(ModList, PackedSizeOfEmptyModListIsZero) { - Moderation mods{system_memory()}; + Test_Memory mem; + Moderation mods{mem}; EXPECT_EQ(mod_list_packed_size(&mods), 0); uint8_t byte = 1; @@ -28,14 +31,16 @@ TEST(ModList, PackedSizeOfEmptyModListIsZero) TEST(ModList, UnpackingZeroSizeArrayIsNoop) { - Moderation mods{system_memory()}; + Test_Memory mem; + Moderation mods{mem}; const uint8_t byte = 1; EXPECT_EQ(mod_list_unpack(&mods, &byte, 0, 0), 0); } TEST(ModList, AddRemoveMultipleMods) { - Moderation mods{system_memory()}; + Test_Memory mem; + Moderation mods{mem}; uint8_t sig_pk1[32] = {1}; uint8_t sig_pk2[32] = {2}; EXPECT_TRUE(mod_list_add_entry(&mods, sig_pk1)); @@ -47,7 +52,8 @@ TEST(ModList, AddRemoveMultipleMods) TEST(ModList, PackingAndUnpackingList) { using ModListEntry = std::array; - Moderation mods{system_memory()}; + Test_Memory mem; + Moderation mods{mem}; EXPECT_TRUE(mod_list_add_entry(&mods, ModListEntry{}.data())); std::vector packed(mod_list_packed_size(&mods)); @@ -55,7 +61,7 @@ TEST(ModList, PackingAndUnpackingList) EXPECT_TRUE(mod_list_remove_entry(&mods, ModListEntry{}.data())); - Moderation mods2{system_memory()}; + Moderation mods2{mem}; EXPECT_EQ(mod_list_unpack(&mods2, packed.data(), packed.size(), 1), packed.size()); EXPECT_TRUE(mod_list_remove_entry(&mods2, ModListEntry{}.data())); } @@ -63,13 +69,14 @@ TEST(ModList, PackingAndUnpackingList) TEST(ModList, UnpackingTooManyModsFails) { using ModListEntry = std::array; - Moderation mods{system_memory()}; + Test_Memory mem; + Moderation mods{mem}; EXPECT_TRUE(mod_list_add_entry(&mods, ModListEntry{}.data())); std::vector packed(mod_list_packed_size(&mods)); mod_list_pack(&mods, packed.data()); - Moderation mods2{system_memory()}; + Moderation mods2{mem}; EXPECT_EQ(mod_list_unpack(&mods2, packed.data(), packed.size(), 2), -1); EXPECT_TRUE(mod_list_remove_entry(&mods, ModListEntry{}.data())); } @@ -78,16 +85,17 @@ TEST(ModList, UnpackingFromEmptyBufferFails) { std::vector packed(1); - Moderation mods{system_memory()}; + Test_Memory mem; + Moderation mods{mem}; EXPECT_EQ(mod_list_unpack(&mods, packed.data(), 0, 1), -1); } TEST(ModList, HashOfEmptyModListZeroesOutBuffer) { - const Random *rng = system_random(); - ASSERT_NE(rng, nullptr); + Test_Memory mem; + Test_Random rng; - Moderation mods{system_memory()}; + Moderation mods{mem}; // Fill with random data, check that it's zeroed. ModerationHash hash; @@ -98,21 +106,24 @@ TEST(ModList, HashOfEmptyModListZeroesOutBuffer) TEST(ModList, RemoveIndexFromEmptyModListFails) { - Moderation mods{system_memory()}; + Test_Memory mem; + Moderation mods{mem}; EXPECT_FALSE(mod_list_remove_index(&mods, 0)); EXPECT_FALSE(mod_list_remove_index(&mods, UINT16_MAX)); } TEST(ModList, RemoveEntryFromEmptyModListFails) { - Moderation mods{system_memory()}; + Test_Memory mem; + Moderation mods{mem}; uint8_t sig_pk[32] = {0}; EXPECT_FALSE(mod_list_remove_entry(&mods, sig_pk)); } TEST(ModList, ModListRemoveIndex) { - Moderation mods{system_memory()}; + Test_Memory mem; + Moderation mods{mem}; uint8_t sig_pk[32] = {1}; EXPECT_TRUE(mod_list_add_entry(&mods, sig_pk)); EXPECT_TRUE(mod_list_remove_index(&mods, 0)); @@ -120,20 +131,23 @@ TEST(ModList, ModListRemoveIndex) TEST(ModList, CleanupOnEmptyModsIsNoop) { - Moderation mods{system_memory()}; + Test_Memory mem; + Moderation mods{mem}; mod_list_cleanup(&mods); } TEST(ModList, EmptyModListCannotVerifyAnySigPk) { - Moderation mods{system_memory()}; + Test_Memory mem; + Moderation mods{mem}; uint8_t sig_pk[32] = {1}; EXPECT_FALSE(mod_list_verify_sig_pk(&mods, sig_pk)); } TEST(ModList, ModListAddVerifyRemoveSigPK) { - Moderation mods{system_memory()}; + Test_Memory mem; + Moderation mods{mem}; uint8_t sig_pk[32] = {1}; EXPECT_TRUE(mod_list_add_entry(&mods, sig_pk)); EXPECT_TRUE(mod_list_verify_sig_pk(&mods, sig_pk)); @@ -143,7 +157,8 @@ TEST(ModList, ModListAddVerifyRemoveSigPK) TEST(ModList, ModListHashCheck) { - Moderation mods1{system_memory()}; + Test_Memory mem; + Moderation mods1{mem}; uint8_t sig_pk1[32] = {1}; std::array hash1; @@ -165,7 +180,8 @@ TEST(SanctionsList, PackingIntoUndersizedBufferFails) TEST(SanctionsList, PackUnpackSanctionsCreds) { - Moderation mod{system_memory()}; + Test_Memory mem; + Moderation mod{mem}; std::array packed; EXPECT_EQ(sanctions_creds_pack(&mod.sanctions_creds, packed.data()), MOD_SANCTIONS_CREDS_SIZE); EXPECT_EQ( @@ -177,7 +193,8 @@ protected: ExtPublicKey pk; ExtSecretKey sk; Logger *log = logger_new(); - Moderation mod{system_memory()}; + Test_Memory mem; + Moderation mod{mem}; Mod_Sanction sanctions[2] = {}; const uint8_t sanctioned_pk1[32] = {1}; diff --git a/toxcore/mem_test_util.cc b/toxcore/mem_test_util.cc new file mode 100644 index 0000000..874bc66 --- /dev/null +++ b/toxcore/mem_test_util.cc @@ -0,0 +1,26 @@ +#include "mem_test_util.hh" + +#include + +Memory_Funcs const Memory_Class::vtable = { + Method::invoke<&Memory_Class::malloc>, + Method::invoke<&Memory_Class::calloc>, + Method::invoke<&Memory_Class::realloc>, + Method::invoke<&Memory_Class::free>, +}; + +Memory_Class::~Memory_Class() = default; + +void *Test_Memory::malloc(void *obj, uint32_t size) { return mem->funcs->malloc(mem->obj, size); } + +void *Test_Memory::calloc(void *obj, uint32_t nmemb, uint32_t size) +{ + return mem->funcs->calloc(mem->obj, nmemb, size); +} + +void *Test_Memory::realloc(void *obj, void *ptr, uint32_t size) +{ + return mem->funcs->realloc(mem->obj, ptr, size); +} + +void Test_Memory::free(void *obj, void *ptr) { return mem->funcs->free(mem->obj, ptr); } diff --git a/toxcore/mem_test_util.hh b/toxcore/mem_test_util.hh new file mode 100644 index 0000000..6d1773d --- /dev/null +++ b/toxcore/mem_test_util.hh @@ -0,0 +1,39 @@ +#ifndef C_TOXCORE_TOXCORE_MEM_TEST_UTIL_H +#define C_TOXCORE_TOXCORE_MEM_TEST_UTIL_H + +#include "mem.h" +#include "test_util.hh" + +struct Memory_Class { + static Memory_Funcs const vtable; + Memory const self; + + operator Memory const *() const { return &self; } + + Memory_Class(Memory_Class const &) = default; + Memory_Class() + : self{&vtable, this} + { + } + + virtual ~Memory_Class(); + virtual mem_malloc_cb malloc = 0; + virtual mem_calloc_cb calloc = 0; + virtual mem_realloc_cb realloc = 0; + virtual mem_free_cb free = 0; +}; + +/** + * Base test Memory class that just forwards to system_memory. Can be + * subclassed to override individual (or all) functions. + */ +class Test_Memory : public Memory_Class { + const Memory *mem = REQUIRE_NOT_NULL(system_memory()); + + void *malloc(void *obj, uint32_t size) override; + void *calloc(void *obj, uint32_t nmemb, uint32_t size) override; + void *realloc(void *obj, void *ptr, uint32_t size) override; + void free(void *obj, void *ptr) override; +}; + +#endif // C_TOXCORE_TOXCORE_MEM_TEST_UTIL_H diff --git a/toxcore/mono_time.c b/toxcore/mono_time.c index cf2044a..2c741f5 100644 --- a/toxcore/mono_time.c +++ b/toxcore/mono_time.c @@ -140,7 +140,7 @@ Mono_Time *mono_time_new(const Memory *mem, mono_time_current_time_cb *current_t mono_time->cur_time = 0; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // Maximum reproducibility. Never return time = 0. - mono_time->base_time = 1; + mono_time->base_time = 1000000000; #else // Never return time = 0 in case time() returns 0 (e.g. on microcontrollers // without battery-powered RTC or ones where NTP didn't initialise it yet). diff --git a/toxcore/mono_time_test.cc b/toxcore/mono_time_test.cc index 3a649b7..0334f32 100644 --- a/toxcore/mono_time_test.cc +++ b/toxcore/mono_time_test.cc @@ -5,11 +5,13 @@ #include #include +#include "mem_test_util.hh" + namespace { TEST(MonoTime, UnixTimeIncreasesOverTime) { - const Memory *mem = system_memory(); + Test_Memory mem; Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr); ASSERT_NE(mono_time, nullptr); @@ -28,7 +30,7 @@ TEST(MonoTime, UnixTimeIncreasesOverTime) TEST(MonoTime, IsTimeout) { - const Memory *mem = system_memory(); + Test_Memory mem; Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr); ASSERT_NE(mono_time, nullptr); @@ -46,7 +48,7 @@ TEST(MonoTime, IsTimeout) TEST(MonoTime, IsTimeoutReal) { - const Memory *mem = system_memory(); + Test_Memory mem; Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr); ASSERT_NE(mono_time, nullptr); @@ -67,7 +69,7 @@ TEST(MonoTime, IsTimeoutReal) TEST(MonoTime, CustomTime) { - const Memory *mem = system_memory(); + Test_Memory mem; Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr); ASSERT_NE(mono_time, nullptr); diff --git a/toxcore/network_test.cc b/toxcore/network_test.cc index 98b97a5..a86436c 100644 --- a/toxcore/network_test.cc +++ b/toxcore/network_test.cc @@ -2,8 +2,17 @@ #include +#include "network_test_util.hh" + namespace { +TEST(TestUtil, ProducesNonNullNetwork) +{ + Test_Network net; + const Network *ns = net; + EXPECT_NE(ns, nullptr); +} + TEST(IpNtoa, DoesntWriteOutOfBounds) { Ip_Ntoa ip_str; diff --git a/toxcore/network_test_util.cc b/toxcore/network_test_util.cc index 82f3b87..1f34e08 100644 --- a/toxcore/network_test_util.cc +++ b/toxcore/network_test_util.cc @@ -2,6 +2,81 @@ #include +Network_Funcs const Network_Class::vtable = { + Method::invoke<&Network_Class::close>, + Method::invoke<&Network_Class::accept>, + Method::invoke<&Network_Class::bind>, + Method::invoke<&Network_Class::listen>, + Method::invoke<&Network_Class::recvbuf>, + Method::invoke<&Network_Class::recv>, + Method::invoke<&Network_Class::recvfrom>, + Method::invoke<&Network_Class::send>, + Method::invoke<&Network_Class::sendto>, + Method::invoke<&Network_Class::socket>, + Method::invoke<&Network_Class::socket_nonblock>, + Method::invoke<&Network_Class::getsockopt>, + Method::invoke<&Network_Class::setsockopt>, + Method::invoke<&Network_Class::getaddrinfo>, + Method::invoke<&Network_Class::freeaddrinfo>, +}; + +int Test_Network::close(void *obj, int sock) { return net->funcs->close(net->obj, sock); } +int Test_Network::accept(void *obj, int sock) { return net->funcs->accept(net->obj, sock); } +int Test_Network::bind(void *obj, int sock, const Network_Addr *addr) +{ + return net->funcs->bind(net->obj, sock, addr); +} +int Test_Network::listen(void *obj, int sock, int backlog) +{ + return net->funcs->listen(net->obj, sock, backlog); +} +int Test_Network::recvbuf(void *obj, int sock) { return net->funcs->recvbuf(net->obj, sock); } +int Test_Network::recv(void *obj, int sock, uint8_t *buf, size_t len) +{ + return net->funcs->recv(net->obj, sock, buf, len); +} +int Test_Network::recvfrom(void *obj, int sock, uint8_t *buf, size_t len, Network_Addr *addr) +{ + return net->funcs->recvfrom(net->obj, sock, buf, len, addr); +} +int Test_Network::send(void *obj, int sock, const uint8_t *buf, size_t len) +{ + return net->funcs->send(net->obj, sock, buf, len); +} +int Test_Network::sendto( + void *obj, int sock, const uint8_t *buf, size_t len, const Network_Addr *addr) +{ + return net->funcs->sendto(net->obj, sock, buf, len, addr); +} +int Test_Network::socket(void *obj, int domain, int type, int proto) +{ + return net->funcs->socket(net->obj, domain, type, proto); +} +int Test_Network::socket_nonblock(void *obj, int sock, bool nonblock) +{ + return net->funcs->socket_nonblock(net->obj, sock, nonblock); +} +int Test_Network::getsockopt( + void *obj, int sock, int level, int optname, void *optval, size_t *optlen) +{ + return net->funcs->getsockopt(net->obj, sock, level, optname, optval, optlen); +} +int Test_Network::setsockopt( + void *obj, int sock, int level, int optname, const void *optval, size_t optlen) +{ + return net->funcs->setsockopt(net->obj, sock, level, optname, optval, optlen); +} +int Test_Network::getaddrinfo(void *obj, int family, Network_Addr **addrs) +{ + return net->funcs->getaddrinfo(net->obj, family, addrs); +} +int Test_Network::freeaddrinfo(void *obj, Network_Addr *addrs) +{ + return net->funcs->freeaddrinfo(net->obj, addrs); +} + +Network_Class::~Network_Class() = default; + IP_Port increasing_ip_port::operator()() { IP_Port ip_port; diff --git a/toxcore/network_test_util.hh b/toxcore/network_test_util.hh index 8666325..ede8397 100644 --- a/toxcore/network_test_util.hh +++ b/toxcore/network_test_util.hh @@ -7,6 +7,60 @@ #include "network.h" #include "test_util.hh" +struct Network_Class { + static Network_Funcs const vtable; + Network const self; + + operator Network const *() const { return &self; } + + Network_Class(Network_Class const &) = default; + Network_Class() + : self{&vtable, this} + { + } + + virtual ~Network_Class(); + virtual net_close_cb close = 0; + virtual net_accept_cb accept = 0; + virtual net_bind_cb bind = 0; + virtual net_listen_cb listen = 0; + virtual net_recvbuf_cb recvbuf = 0; + virtual net_recv_cb recv = 0; + virtual net_recvfrom_cb recvfrom = 0; + virtual net_send_cb send = 0; + virtual net_sendto_cb sendto = 0; + virtual net_socket_cb socket = 0; + virtual net_socket_nonblock_cb socket_nonblock = 0; + virtual net_getsockopt_cb getsockopt = 0; + virtual net_setsockopt_cb setsockopt = 0; + virtual net_getaddrinfo_cb getaddrinfo = 0; + virtual net_freeaddrinfo_cb freeaddrinfo = 0; +}; + +/** + * Base test Network class that just forwards to system_network. Can be + * subclassed to override individual (or all) functions. + */ +class Test_Network : public Network_Class { + const Network *net = REQUIRE_NOT_NULL(system_network()); + + int close(void *obj, int sock) override; + int accept(void *obj, int sock) override; + int bind(void *obj, int sock, const Network_Addr *addr) override; + int listen(void *obj, int sock, int backlog) override; + int recvbuf(void *obj, int sock) override; + int recv(void *obj, int sock, uint8_t *buf, size_t len) override; + int recvfrom(void *obj, int sock, uint8_t *buf, size_t len, Network_Addr *addr) override; + int send(void *obj, int sock, const uint8_t *buf, size_t len) override; + int sendto(void *obj, int sock, const uint8_t *buf, size_t len, const Network_Addr *addr) override; + int socket(void *obj, int domain, int type, int proto) override; + int socket_nonblock(void *obj, int sock, bool nonblock) override; + int getsockopt(void *obj, int sock, int level, int optname, void *optval, size_t *optlen) override; + int setsockopt(void *obj, int sock, int level, int optname, const void *optval, size_t optlen) override; + int getaddrinfo(void *obj, int family, Network_Addr **addrs) override; + int freeaddrinfo(void *obj, Network_Addr *addrs) override; +}; + template <> struct Deleter : Function_Deleter { }; diff --git a/toxcore/ping_array_test.cc b/toxcore/ping_array_test.cc index 8ce5570..6f6f4c2 100644 --- a/toxcore/ping_array_test.cc +++ b/toxcore/ping_array_test.cc @@ -4,6 +4,7 @@ #include +#include "mem_test_util.hh" #include "mono_time.h" namespace { @@ -15,7 +16,7 @@ struct Ping_Array_Deleter { using Ping_Array_Ptr = std::unique_ptr; struct Mono_Time_Deleter { - Mono_Time_Deleter(const Memory *mem) + Mono_Time_Deleter(const Test_Memory &mem) : mem_(mem) { } @@ -29,21 +30,21 @@ using Mono_Time_Ptr = std::unique_ptr; TEST(PingArray, MinimumTimeoutIsOne) { - const Memory *mem = system_memory(); + Test_Memory mem; EXPECT_EQ(ping_array_new(mem, 1, 0), nullptr); EXPECT_NE(Ping_Array_Ptr(ping_array_new(mem, 1, 1)), nullptr); } TEST(PingArray, MinimumArraySizeIsOne) { - const Memory *mem = system_memory(); + Test_Memory mem; EXPECT_EQ(ping_array_new(mem, 0, 1), nullptr); EXPECT_NE(Ping_Array_Ptr(ping_array_new(mem, 1, 1)), nullptr); } TEST(PingArray, ArraySizeMustBePowerOfTwo) { - const Memory *mem = system_memory(); + Test_Memory mem; Ping_Array_Ptr arr; arr.reset(ping_array_new(mem, 2, 1)); @@ -59,7 +60,7 @@ TEST(PingArray, ArraySizeMustBePowerOfTwo) TEST(PingArray, StoredDataCanBeRetrieved) { - const Memory *mem = system_memory(); + Test_Memory mem; Ping_Array_Ptr const arr(ping_array_new(mem, 2, 1)); Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr, nullptr), mem); @@ -78,7 +79,7 @@ TEST(PingArray, StoredDataCanBeRetrieved) TEST(PingArray, RetrievingDataWithTooSmallOutputBufferHasNoEffect) { - const Memory *mem = system_memory(); + Test_Memory mem; Ping_Array_Ptr const arr(ping_array_new(mem, 2, 1)); Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr, nullptr), mem); @@ -101,7 +102,7 @@ TEST(PingArray, RetrievingDataWithTooSmallOutputBufferHasNoEffect) TEST(PingArray, ZeroLengthDataCanBeAdded) { - const Memory *mem = system_memory(); + Test_Memory mem; Ping_Array_Ptr const arr(ping_array_new(mem, 2, 1)); Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr, nullptr), mem); @@ -118,7 +119,7 @@ TEST(PingArray, ZeroLengthDataCanBeAdded) TEST(PingArray, PingId0IsInvalid) { - const Memory *mem = system_memory(); + Test_Memory mem; Ping_Array_Ptr const arr(ping_array_new(mem, 2, 1)); Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr, nullptr), mem); @@ -131,7 +132,7 @@ TEST(PingArray, PingId0IsInvalid) // Protection against replay attacks. TEST(PingArray, DataCanOnlyBeRetrievedOnce) { - const Memory *mem = system_memory(); + Test_Memory mem; Ping_Array_Ptr const arr(ping_array_new(mem, 2, 1)); Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr, nullptr), mem); @@ -149,7 +150,7 @@ TEST(PingArray, DataCanOnlyBeRetrievedOnce) TEST(PingArray, PingIdMustMatchOnCheck) { - const Memory *mem = system_memory(); + Test_Memory mem; Ping_Array_Ptr const arr(ping_array_new(mem, 1, 1)); Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr, nullptr), mem); diff --git a/toxcore/test_util.hh b/toxcore/test_util.hh index 9bd35fa..08bc948 100644 --- a/toxcore/test_util.hh +++ b/toxcore/test_util.hh @@ -3,7 +3,10 @@ #include #include +#include +#include #include +#include #include template @@ -41,7 +44,7 @@ std::array to_array(T const (&arr)[N]) template auto array_of(T &&make, Args... args) { - std::array::type, N> arr; + std::array, N> arr; for (auto &elem : arr) { elem = make(args...); } @@ -51,7 +54,7 @@ auto array_of(T &&make, Args... args) template auto vector_of(std::size_t n, T &&make, Args... args) { - std::vector::type> vec; + std::vector> vec; for (std::size_t i = 0; i < n; ++i) { vec.push_back(make(args...)); } @@ -65,4 +68,16 @@ Container sorted(Container arr, Less less) return arr; } +template +T *require_not_null(const char *file, int line, T *ptr) +{ + if (ptr == nullptr) { + std::fprintf(stderr, "unexpected null pointer at %s:%d\n", file, line); + std::exit(7); + } + return ptr; +} + +#define REQUIRE_NOT_NULL(ptr) require_not_null(__FILE__, __LINE__, ptr) + #endif // C_TOXCORE_TOXCORE_TEST_UTIL_H diff --git a/toxcore/tox_events_fuzz_test.cc b/toxcore/tox_events_fuzz_test.cc index 993cf81..480d80a 100644 --- a/toxcore/tox_events_fuzz_test.cc +++ b/toxcore/tox_events_fuzz_test.cc @@ -20,7 +20,7 @@ void TestUnpack(Fuzz_Data data) // events_size bytes: events data (max 64K) CONSUME_OR_RETURN(const uint8_t *events_data, data, events_size); - if (data.size == 0) { + if (data.empty()) { // If there's no more input, no malloc failure paths can possibly be // tested, so we ignore this input. return; diff --git a/toxcore/util.c b/toxcore/util.c index a84d181..9a0a26a 100644 --- a/toxcore/util.c +++ b/toxcore/util.c @@ -13,6 +13,7 @@ #include "util.h" +#include #include #include "ccompat.h" @@ -80,6 +81,21 @@ bool memeq(const uint8_t *a, size_t a_size, const uint8_t *b, size_t b_size) return a_size == b_size && memcmp(a, b, a_size) == 0; } +uint8_t *memdup(const uint8_t *data, size_t data_size) +{ + if (data == nullptr || data_size == 0) { + return nullptr; + } + + uint8_t *copy = (uint8_t *)malloc(data_size); + + if (copy != nullptr) { + memcpy(copy, data, data_size); + } + + return copy; +} + int16_t max_s16(int16_t a, int16_t b) { return a > b ? a : b; diff --git a/toxcore/util.h b/toxcore/util.h index c16605c..8c212cf 100644 --- a/toxcore/util.h +++ b/toxcore/util.h @@ -41,6 +41,13 @@ non_null() int create_recursive_mutex(pthread_mutex_t *mutex); */ non_null() bool memeq(const uint8_t *a, size_t a_size, const uint8_t *b, size_t b_size); +/** + * @brief Copies a byte array of a given size into a newly allocated one. + * + * @return nullptr on allocation failure or if the input data was nullptr or data_size was 0. + */ +nullable(1) uint8_t *memdup(const uint8_t *data, size_t data_size); + // Safe min/max functions with specific types. This forces the conversion to the // desired type before the comparison expression, giving the choice of // conversion to the caller. Use these instead of inline comparisons or MIN/MAX