Squashed 'external/toxcore/c-toxcore/' changes from 1828c5356..c9cdae001
c9cdae001 fix(toxav): remove extra copy of video frame on encode 4f6d4546b test: Improve the fake network library. a2581e700 refactor(toxcore): generate `Friend_Request` and `Dht_Nodes_Response` 2aaa11770 refactor(toxcore): use Tox_Memory in generated events 5c367452b test(toxcore): fix incorrect mutex in tox_scenario_get_time 8f92e710f perf: Add a timed limit of number of cookie requests. 695b6417a test: Add some more simulated network support. 815ae9ce9 test(toxcore): fix thread-safety in scenario framework 6d85c754e test(toxcore): add unit tests for net_crypto 9c22e79cc test(support): add SimulatedEnvironment for deterministic testing f34fcb195 chore: Update windows Dockerfile to debian stable (trixie). ece0e8980 fix(group_moderation): allow validating unsorted sanction list signatures a4fa754d7 refactor: rename struct Packet to struct Net_Packet d6f330f85 cleanup: Fix some warnings from coverity. e206bffa2 fix(group_chats): fix sync packets reverting topics 0e4715598 test: Add new scenario testing framework. 668291f44 refactor(toxcore): decouple Network_Funcs from sockaddr via IP_Port fc4396cef fix: potential division by zero in toxav and unsafe hex parsing 8e8b352ab refactor: Add nullable annotations to struct members. 7740bb421 refactor: decouple net_crypto from DHT 1936d4296 test: add benchmark for toxav audio and video 46bfdc2df fix: correct printf format specifiers for unsigned integers REVERT: 1828c5356 fix(toxav): remove extra copy of video frame on encode git-subtree-dir: external/toxcore/c-toxcore git-subtree-split: c9cdae001341e701fca980c9bb9febfeb95d2902
This commit is contained in:
@@ -1,28 +1,6 @@
|
||||
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
|
||||
load("@rules_cc//cc:defs.bzl", "cc_binary")
|
||||
load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test")
|
||||
|
||||
cc_library(
|
||||
name = "fuzz_support",
|
||||
srcs = [
|
||||
"func_conversion.hh",
|
||||
"fuzz_support.cc",
|
||||
],
|
||||
hdrs = ["fuzz_support.hh"],
|
||||
visibility = ["//c-toxcore:__subpackages__"],
|
||||
deps = [
|
||||
"//c-toxcore/toxcore:crypto_core",
|
||||
"//c-toxcore/toxcore:network",
|
||||
"//c-toxcore/toxcore:tox",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "fuzz_tox",
|
||||
hdrs = ["fuzz_tox.hh"],
|
||||
visibility = ["//c-toxcore:__subpackages__"],
|
||||
deps = [":fuzz_support"],
|
||||
)
|
||||
|
||||
cc_fuzz_test(
|
||||
name = "bootstrap_fuzz_test",
|
||||
size = "small",
|
||||
@@ -30,8 +8,7 @@ cc_fuzz_test(
|
||||
copts = ["-UNDEBUG"],
|
||||
corpus = ["//tools/toktok-fuzzer/corpus:bootstrap_fuzz_test"],
|
||||
deps = [
|
||||
":fuzz_support",
|
||||
":fuzz_tox",
|
||||
"//c-toxcore/testing/support",
|
||||
"//c-toxcore/toxcore:tox",
|
||||
"//c-toxcore/toxcore:tox_dispatch",
|
||||
"//c-toxcore/toxcore:tox_events",
|
||||
@@ -46,8 +23,7 @@ cc_fuzz_test(
|
||||
corpus = ["//tools/toktok-fuzzer/corpus:e2e_fuzz_test"],
|
||||
data = ["//tools/toktok-fuzzer/init:e2e_fuzz_test.dat"],
|
||||
deps = [
|
||||
":fuzz_support",
|
||||
":fuzz_tox",
|
||||
"//c-toxcore/testing/support",
|
||||
"//c-toxcore/toxcore:crypto_core",
|
||||
"//c-toxcore/toxcore:tox",
|
||||
"//c-toxcore/toxcore:tox_dispatch",
|
||||
@@ -62,7 +38,7 @@ cc_fuzz_test(
|
||||
copts = ["-UNDEBUG"],
|
||||
corpus = ["//tools/toktok-fuzzer/corpus:toxsave_fuzz_test"],
|
||||
deps = [
|
||||
":fuzz_support",
|
||||
"//c-toxcore/testing/support",
|
||||
"//c-toxcore/toxcore:tox",
|
||||
],
|
||||
)
|
||||
@@ -72,7 +48,7 @@ cc_binary(
|
||||
srcs = ["protodump.cc"],
|
||||
copts = ["-UNDEBUG"],
|
||||
deps = [
|
||||
":fuzz_support",
|
||||
"//c-toxcore/testing/support",
|
||||
"//c-toxcore/toxcore:tox",
|
||||
"//c-toxcore/toxcore:tox_dispatch",
|
||||
"//c-toxcore/toxcore:tox_events",
|
||||
@@ -90,38 +66,3 @@ genrule(
|
||||
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:crypto_core",
|
||||
"//c-toxcore/toxcore:tox",
|
||||
"//c-toxcore/toxcore:tox_dispatch",
|
||||
"//c-toxcore/toxcore:tox_events",
|
||||
"@rules_fuzzing//fuzzing:cc_engine",
|
||||
],
|
||||
)
|
||||
|
||||
cc_fuzz_test(
|
||||
name = "protodump_reduce",
|
||||
size = "small",
|
||||
srcs = ["protodump_reduce.cc"],
|
||||
copts = ["-UNDEBUG"],
|
||||
deps = [
|
||||
":fuzz_support",
|
||||
":fuzz_tox",
|
||||
"//c-toxcore/toxcore:crypto_core",
|
||||
"//c-toxcore/toxcore:tox",
|
||||
"//c-toxcore/toxcore:tox_dispatch",
|
||||
"//c-toxcore/toxcore:tox_events",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# Override network and random functions
|
||||
add_library(fuzz_support func_conversion.hh fuzz_support.cc fuzz_support.hh)
|
||||
|
||||
set(LIBFUZZER_LINKER_FLAGS)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
@@ -12,9 +11,9 @@ function(fuzz_test target source_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 fuzz_support test_util toxcore_fuzz ${LIBFUZZER_LINKER_FLAGS})
|
||||
target_link_libraries(${target}_fuzz_test PRIVATE 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})
|
||||
add_test(NAME ${target}_fuzz COMMAND ${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()
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "../../toxcore/tox.h"
|
||||
#include "../../toxcore/tox_dispatch.h"
|
||||
#include "../../toxcore/tox_events.h"
|
||||
#include "fuzz_support.hh"
|
||||
#include "fuzz_tox.hh"
|
||||
#include "../support/public/fuzz_data.hh"
|
||||
#include "../support/public/fuzz_helpers.hh"
|
||||
#include "../support/public/simulated_environment.hh"
|
||||
|
||||
namespace {
|
||||
|
||||
using tox::test::configure_fuzz_memory_source;
|
||||
using tox::test::configure_fuzz_packet_source;
|
||||
using tox::test::FakeClock;
|
||||
using tox::test::Fuzz_Data;
|
||||
using tox::test::SimulatedEnvironment;
|
||||
|
||||
template <typename T>
|
||||
using Ptr = std::unique_ptr<T, void (*)(T *)>;
|
||||
|
||||
void setup_callbacks(Tox_Dispatch *dispatch)
|
||||
{
|
||||
tox_events_callback_conference_connected(
|
||||
@@ -97,10 +109,15 @@ void setup_callbacks(Tox_Dispatch *dispatch)
|
||||
|
||||
void TestBootstrap(Fuzz_Data &input)
|
||||
{
|
||||
// Null system for regularly working memory allocations needed in
|
||||
// tox_events_equal.
|
||||
Null_System null_sys;
|
||||
Fuzz_System sys(input);
|
||||
SimulatedEnvironment env;
|
||||
env.fake_clock().advance(1000000000); // Match legacy behavior
|
||||
auto node = env.create_node(33445);
|
||||
configure_fuzz_memory_source(env.fake_memory(), input);
|
||||
configure_fuzz_packet_source(*node->endpoint, input);
|
||||
|
||||
// Create a second null system for tox_events_equal check
|
||||
SimulatedEnvironment null_env;
|
||||
auto null_node = null_env.create_node(0); // Port 0 (unbound/irrelevant)
|
||||
|
||||
Ptr<Tox_Options> opts(tox_options_new(nullptr), tox_options_free);
|
||||
assert(opts != nullptr);
|
||||
@@ -110,8 +127,26 @@ void TestBootstrap(Fuzz_Data &input)
|
||||
const char *message, void *user_data) {
|
||||
// Log to stdout.
|
||||
if (Fuzz_Data::FUZZ_DEBUG) {
|
||||
std::printf("[tox1] %c %s:%u(%s): %s\n", tox_log_level_name(level), file, line,
|
||||
func, message);
|
||||
// Approximate level name mapping
|
||||
char level_char = '?';
|
||||
switch (level) {
|
||||
case TOX_LOG_LEVEL_TRACE:
|
||||
level_char = 'T';
|
||||
break;
|
||||
case TOX_LOG_LEVEL_DEBUG:
|
||||
level_char = 'D';
|
||||
break;
|
||||
case TOX_LOG_LEVEL_INFO:
|
||||
level_char = 'I';
|
||||
break;
|
||||
case TOX_LOG_LEVEL_WARNING:
|
||||
level_char = 'W';
|
||||
break;
|
||||
case TOX_LOG_LEVEL_ERROR:
|
||||
level_char = 'E';
|
||||
break;
|
||||
}
|
||||
std::printf("[tox1] %c %s:%u(%s): %s\n", level_char, file, line, func, message);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -134,7 +169,7 @@ void TestBootstrap(Fuzz_Data &input)
|
||||
}
|
||||
|
||||
Tox_Options_Testing tox_options_testing;
|
||||
tox_options_testing.operating_system = sys.sys.get();
|
||||
tox_options_testing.operating_system = &node->system;
|
||||
|
||||
Tox_Err_New error_new;
|
||||
Tox_Err_New_Testing error_new_testing;
|
||||
@@ -165,15 +200,17 @@ void TestBootstrap(Fuzz_Data &input)
|
||||
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));
|
||||
assert(tox_events_equal(&null_node->system, events, events));
|
||||
|
||||
tox_dispatch_invoke(dispatch, events, tox);
|
||||
tox_events_free(events);
|
||||
// Move the clock forward a decent amount so all the time-based checks
|
||||
// trigger more quickly.
|
||||
sys.clock += 200;
|
||||
|
||||
// If no input was consumed, something went wrong.
|
||||
assert(input_size != input.size());
|
||||
env.advance_time(200);
|
||||
|
||||
// If no input was consumed, stop.
|
||||
if (input_size == input.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
input_size = input.size();
|
||||
}
|
||||
|
||||
@@ -5,17 +5,29 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "../../toxcore/crypto_core.h"
|
||||
#include "../../toxcore/tox.h"
|
||||
#include "../../toxcore/tox_dispatch.h"
|
||||
#include "../../toxcore/tox_events.h"
|
||||
#include "fuzz_support.hh"
|
||||
#include "fuzz_tox.hh"
|
||||
#include "../support/public/fuzz_data.hh"
|
||||
#include "../support/public/fuzz_helpers.hh"
|
||||
#include "../support/public/simulated_environment.hh"
|
||||
|
||||
namespace {
|
||||
|
||||
using tox::test::configure_fuzz_memory_source;
|
||||
using tox::test::configure_fuzz_packet_source;
|
||||
using tox::test::configure_fuzz_random_source;
|
||||
using tox::test::FakeClock;
|
||||
using tox::test::Fuzz_Data;
|
||||
using tox::test::SimulatedEnvironment;
|
||||
|
||||
template <typename T>
|
||||
using Ptr = std::unique_ptr<T, void (*)(T *)>;
|
||||
|
||||
void setup_callbacks(Tox_Dispatch *dispatch)
|
||||
{
|
||||
tox_events_callback_conference_connected(
|
||||
@@ -131,9 +143,16 @@ void setup_callbacks(Tox_Dispatch *dispatch)
|
||||
|
||||
void TestEndToEnd(Fuzz_Data &input)
|
||||
{
|
||||
Fuzz_System sys(input);
|
||||
// Used for places where we want all allocations to succeed.
|
||||
Null_System null_sys;
|
||||
SimulatedEnvironment env;
|
||||
env.fake_clock().advance(1000000000); // Match legacy behavior
|
||||
auto node = env.create_node(33445);
|
||||
configure_fuzz_memory_source(env.fake_memory(), input);
|
||||
configure_fuzz_packet_source(*node->endpoint, input);
|
||||
configure_fuzz_random_source(env.fake_random(), input);
|
||||
|
||||
// Null system replacement for event comparison
|
||||
SimulatedEnvironment null_env;
|
||||
auto null_node = null_env.create_node(0);
|
||||
|
||||
Ptr<Tox_Options> opts(tox_options_new(nullptr), tox_options_free);
|
||||
assert(opts != nullptr);
|
||||
@@ -144,13 +163,31 @@ void TestEndToEnd(Fuzz_Data &input)
|
||||
const char *message, void *user_data) {
|
||||
// Log to stdout.
|
||||
if (Fuzz_Data::FUZZ_DEBUG) {
|
||||
std::printf("[tox1] %c %s:%u(%s): %s\n", tox_log_level_name(level), file, line,
|
||||
func, message);
|
||||
// Approximate level name mapping
|
||||
char level_char = '?';
|
||||
switch (level) {
|
||||
case TOX_LOG_LEVEL_TRACE:
|
||||
level_char = 'T';
|
||||
break;
|
||||
case TOX_LOG_LEVEL_DEBUG:
|
||||
level_char = 'D';
|
||||
break;
|
||||
case TOX_LOG_LEVEL_INFO:
|
||||
level_char = 'I';
|
||||
break;
|
||||
case TOX_LOG_LEVEL_WARNING:
|
||||
level_char = 'W';
|
||||
break;
|
||||
case TOX_LOG_LEVEL_ERROR:
|
||||
level_char = 'E';
|
||||
break;
|
||||
}
|
||||
std::printf("[tox1] %c %s:%u(%s): %s\n", level_char, file, line, func, message);
|
||||
}
|
||||
});
|
||||
|
||||
Tox_Options_Testing tox_options_testing;
|
||||
tox_options_testing.operating_system = sys.sys.get();
|
||||
tox_options_testing.operating_system = &node->system;
|
||||
|
||||
Tox_Err_New error_new;
|
||||
Tox_Err_New_Testing error_new_testing;
|
||||
@@ -171,15 +208,18 @@ void TestEndToEnd(Fuzz_Data &input)
|
||||
assert(dispatch != nullptr);
|
||||
setup_callbacks(dispatch);
|
||||
|
||||
// MIN_ITERATION_INTERVAL = 20
|
||||
const uint8_t MIN_ITERATION_INTERVAL = 20;
|
||||
|
||||
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));
|
||||
assert(tox_events_equal(&null_node->system, events, events));
|
||||
tox_dispatch_invoke(dispatch, events, tox);
|
||||
tox_events_free(events);
|
||||
// Move the clock forward a decent amount so all the time-based checks
|
||||
// trigger more quickly.
|
||||
sys.clock += std::max(System::MIN_ITERATION_INTERVAL, random_u08(sys.rng.get()));
|
||||
|
||||
uint32_t rand_val = env.fake_random().uniform(256);
|
||||
env.fake_clock().advance(std::max<uint64_t>(MIN_ITERATION_INTERVAL, rand_val));
|
||||
}
|
||||
|
||||
tox_dispatch_free(dispatch);
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Copyright © 2022-2025 The TokTok team.
|
||||
*/
|
||||
|
||||
#ifndef C_TOXCORE_TESTING_FUZZING_FUNC_CONVERSION_H
|
||||
#define C_TOXCORE_TESTING_FUZZING_FUNC_CONVERSION_H
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename F, F f>
|
||||
struct func_conversion {
|
||||
private:
|
||||
template <typename R, typename... Args>
|
||||
using func_pointer = R (*)(Args...);
|
||||
|
||||
template <typename From>
|
||||
struct static_caster {
|
||||
From obj;
|
||||
|
||||
template <typename To>
|
||||
operator To() const
|
||||
{
|
||||
return static_cast<To>(obj);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
template <typename R, typename Arg, typename... Args>
|
||||
constexpr operator func_pointer<R, Arg, Args...>()
|
||||
{
|
||||
return [](Arg obj, auto... args) { return f(static_caster<Arg>{obj}, args...); };
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
struct make_funptr;
|
||||
|
||||
template <typename T, typename R, typename... Args>
|
||||
struct make_funptr<R (T::*)(Args...) const> {
|
||||
using type = R (*)(Args...);
|
||||
};
|
||||
|
||||
/** @brief Turn a memfunptr type into a plain funptr type.
|
||||
*
|
||||
* Not needed in C++20, because we can pass the lambda itself as template
|
||||
* argument, but in C++17, we need to do an early conversion.
|
||||
*/
|
||||
template <typename F>
|
||||
using make_funptr_t = typename make_funptr<F>::type;
|
||||
|
||||
}
|
||||
|
||||
/** @brief Turn a C++ lambda into a C function pointer with `void*` param.
|
||||
*
|
||||
* Takes a lambda function with any pointer type as first parameter and turns it
|
||||
* into a C function pointer with `void*` as the first parameter. Internally, it
|
||||
* `static_cast`s that `void*` to the lambda's parameter type, avoiding a bunch
|
||||
* of casts inside the lambdas.
|
||||
*
|
||||
* This works on any type `T` that can be `static_cast` to `U`, not just `void*`
|
||||
* to `U*`, but the common case for C callbacks is `void*`.
|
||||
*/
|
||||
template <typename F>
|
||||
static constexpr auto operator!(F f)
|
||||
{
|
||||
return detail::func_conversion<detail::make_funptr_t<decltype(&F::operator())>, f>{};
|
||||
}
|
||||
|
||||
#endif // C_TOXCORE_TESTING_FUZZING_FUNC_CONVERSION_H
|
||||
@@ -1,479 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Copyright © 2021-2025 The TokTok team.
|
||||
*/
|
||||
|
||||
#include "fuzz_support.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
// Comment line here to avoid reordering by source code formatters.
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "../../toxcore/crypto_core.h"
|
||||
#include "../../toxcore/network.h"
|
||||
#include "../../toxcore/tox_memory_impl.h"
|
||||
#include "../../toxcore/tox_private.h"
|
||||
#include "../../toxcore/tox_random_impl.h"
|
||||
#include "func_conversion.hh"
|
||||
|
||||
// TODO(iphydf): Put this somewhere shared.
|
||||
struct Network_Addr {
|
||||
struct sockaddr_storage addr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
System::System(std::unique_ptr<Tox_System> in_sys, std::unique_ptr<Tox_Memory> in_mem,
|
||||
std::unique_ptr<Network> in_ns, std::unique_ptr<Tox_Random> in_rng)
|
||||
: sys(std::move(in_sys))
|
||||
, mem(std::move(in_mem))
|
||||
, ns(std::move(in_ns))
|
||||
, rng(std::move(in_rng))
|
||||
{
|
||||
}
|
||||
System::System(System &&) = default;
|
||||
|
||||
System::~System() { }
|
||||
|
||||
static int recv_common(Fuzz_Data &input, uint8_t *buf, size_t buf_len)
|
||||
{
|
||||
if (input.size() < 2) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 (Fuzz_Data::FUZZ_DEBUG) {
|
||||
std::printf("recvfrom: no data for tox1\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (Fuzz_Data::FUZZ_DEBUG) {
|
||||
std::printf(
|
||||
"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()));
|
||||
|
||||
CONSUME_OR_ABORT(const uint8_t *data, input, res);
|
||||
std::copy(data, data + res, buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void *report_alloc(const char *name, const char *func, std::size_t size, void *ptr)
|
||||
{
|
||||
if (Fuzz_Data::FUZZ_DEBUG) {
|
||||
printf("%s: %s(%zu): %s\n", name, func, size, ptr == nullptr ? "false" : "true");
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename F, F Func, typename... Args>
|
||||
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 report_alloc("tox1", func, size, Func(args...));
|
||||
}
|
||||
|
||||
static constexpr Tox_Memory_Funcs fuzz_memory_funcs = {
|
||||
/* .malloc = */
|
||||
 {
|
||||
return alloc_common<decltype(std::malloc), std::malloc>("malloc", size, self->data, size);
|
||||
},
|
||||
/* .realloc = */
|
||||
 {
|
||||
return alloc_common<decltype(std::realloc), std::realloc>(
|
||||
"realloc", size, self->data, ptr, size);
|
||||
},
|
||||
/* .dealloc = */
|
||||
 { std::free(ptr); },
|
||||
};
|
||||
|
||||
static constexpr Network_Funcs fuzz_network_funcs = {
|
||||
/* .close = */  { return 0; },
|
||||
/* .accept = */  { return Socket{1337}; },
|
||||
/* .bind = */  { return 0; },
|
||||
/* .listen = */  { return 0; },
|
||||
/* .connect = */  { return 0; },
|
||||
/* .recvbuf = */
|
||||
 {
|
||||
assert(sock.value == 42 || sock.value == 1337);
|
||||
const size_t count = random_u16(self->rng.get());
|
||||
return static_cast<int>(std::min(count, self->data.size()));
|
||||
},
|
||||
/* .recv = */
|
||||
 {
|
||||
assert(sock.value == 42 || sock.value == 1337);
|
||||
// Receive data from the fuzzer.
|
||||
return recv_common(self->data, buf, len);
|
||||
},
|
||||
/* .recvfrom = */
|
||||
 {
|
||||
assert(sock.value == 42 || sock.value == 1337);
|
||||
|
||||
addr->addr = sockaddr_storage{};
|
||||
// Dummy Addr
|
||||
addr->addr.ss_family = AF_INET;
|
||||
|
||||
// We want an AF_INET address with dummy values
|
||||
sockaddr_in *addr_in = reinterpret_cast<sockaddr_in *>(&addr->addr);
|
||||
addr_in->sin_port = htons(33446);
|
||||
addr_in->sin_addr.s_addr = htonl(0x7f000002); // 127.0.0.2
|
||||
addr->size = sizeof(struct sockaddr);
|
||||
|
||||
return recv_common(self->data, buf, len);
|
||||
},
|
||||
/* .send = */
|
||||
 {
|
||||
assert(sock.value == 42 || sock.value == 1337);
|
||||
// Always succeed.
|
||||
return static_cast<int>(len);
|
||||
},
|
||||
/* .sendto = */
|
||||
 {
|
||||
assert(sock.value == 42 || sock.value == 1337);
|
||||
// Always succeed.
|
||||
return static_cast<int>(len);
|
||||
},
|
||||
/* .socket = */  { return Socket{42}; },
|
||||
/* .socket_nonblock = */  { return 0; },
|
||||
/* .getsockopt = */
|
||||
 {
|
||||
std::memset(optval, 0, *optlen);
|
||||
return 0;
|
||||
},
|
||||
/* .setsockopt = */
|
||||
 {
|
||||
return 0;
|
||||
},
|
||||
};
|
||||
|
||||
static constexpr Tox_Random_Funcs fuzz_random_funcs = {
|
||||
/* .bytes_callback = */
|
||||
 {
|
||||
// Initialize the buffer with zeros in case there's no randomness left.
|
||||
std::fill_n(bytes, length, 0);
|
||||
|
||||
// For integers, we copy bytes directly, because we want to control the
|
||||
// exact values.
|
||||
if (length == sizeof(uint8_t) || length == sizeof(uint16_t) || length == sizeof(uint32_t)
|
||||
|| length == sizeof(uint64_t)) {
|
||||
CONSUME_OR_RETURN(const uint8_t *data, self->data, length);
|
||||
std::copy(data, data + length, bytes);
|
||||
if (Fuzz_Data::FUZZ_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);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// For nonces and keys, we fill the buffer with the same 1-2 bytes
|
||||
// repeated. We only need these to be different enough to not often be
|
||||
// the same.
|
||||
assert(length == 24 || length == 32);
|
||||
// We must cover the case of having only 1 byte left in the input. In
|
||||
// that case, we will use the same byte for all the bytes in the output.
|
||||
const size_t chunk_size = std::max(self->data.size(), static_cast<std::size_t>(2));
|
||||
CONSUME_OR_RETURN(const uint8_t *chunk, self->data, chunk_size);
|
||||
if (chunk_size == 2) {
|
||||
std::fill_n(bytes, length / 2, chunk[0]);
|
||||
std::fill_n(bytes + length / 2, length / 2, chunk[1]);
|
||||
} else {
|
||||
std::fill_n(bytes, length, chunk[0]);
|
||||
}
|
||||
if (Fuzz_Data::FUZZ_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);
|
||||
}
|
||||
}
|
||||
},
|
||||
/* .uniform_callback = */
|
||||
 {
|
||||
uint32_t randnum = 0;
|
||||
if (upper_bound > 0) {
|
||||
self->rng->funcs->bytes_callback(
|
||||
self, reinterpret_cast<uint8_t *>(&randnum), sizeof(randnum));
|
||||
randnum %= upper_bound;
|
||||
}
|
||||
return randnum;
|
||||
},
|
||||
};
|
||||
|
||||
Fuzz_System::Fuzz_System(Fuzz_Data &input)
|
||||
: System{
|
||||
std::make_unique<Tox_System>(),
|
||||
std::make_unique<Tox_Memory>(Tox_Memory{&fuzz_memory_funcs, this}),
|
||||
std::make_unique<Network>(Network{&fuzz_network_funcs, this}),
|
||||
std::make_unique<Tox_Random>(Tox_Random{&fuzz_random_funcs, this}),
|
||||
}
|
||||
, data(input)
|
||||
{
|
||||
sys->mono_time_callback = [](void *self) { return static_cast<Fuzz_System *>(self)->clock; };
|
||||
sys->mono_time_user_data = this;
|
||||
sys->mem = mem.get();
|
||||
sys->ns = ns.get();
|
||||
sys->rng = rng.get();
|
||||
}
|
||||
|
||||
static constexpr Tox_Memory_Funcs null_memory_funcs = {
|
||||
/* .malloc = */
|
||||
 { return std::malloc(size); },
|
||||
/* .realloc = */
|
||||
 { return std::realloc(ptr, size); },
|
||||
/* .dealloc = */
|
||||
 { std::free(ptr); },
|
||||
};
|
||||
|
||||
static constexpr Network_Funcs null_network_funcs = {
|
||||
/* .close = */  { return 0; },
|
||||
/* .accept = */  { return Socket{1337}; },
|
||||
/* .bind = */  { return 0; },
|
||||
/* .listen = */  { return 0; },
|
||||
/* .connect = */  { return 0; },
|
||||
/* .recvbuf = */  { return 0; },
|
||||
/* .recv = */
|
||||
 {
|
||||
// Always fail.
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
},
|
||||
/* .recvfrom = */
|
||||
 {
|
||||
// Always fail.
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
},
|
||||
/* .send = */
|
||||
 {
|
||||
// Always succeed.
|
||||
return static_cast<int>(len);
|
||||
},
|
||||
/* .sendto = */
|
||||
 {
|
||||
// Always succeed.
|
||||
return static_cast<int>(len);
|
||||
},
|
||||
/* .socket = */  { return Socket{42}; },
|
||||
/* .socket_nonblock = */  { return 0; },
|
||||
/* .getsockopt = */
|
||||
 {
|
||||
std::memset(optval, 0, *optlen);
|
||||
return 0;
|
||||
},
|
||||
/* .setsockopt = */
|
||||
 {
|
||||
return 0;
|
||||
},
|
||||
};
|
||||
|
||||
static uint64_t simple_rng(uint64_t &seed)
|
||||
{
|
||||
// https://nuclear.llnl.gov/CNP/rng/rngman/node4.html
|
||||
seed = 2862933555777941757LL * seed + 3037000493LL;
|
||||
return seed;
|
||||
}
|
||||
|
||||
static constexpr Tox_Random_Funcs null_random_funcs = {
|
||||
/* .bytes_callback = */
|
||||
 {
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
bytes[i] = simple_rng(self->seed) & 0xff;
|
||||
}
|
||||
},
|
||||
/* .uniform_callback = */
|
||||
 {
|
||||
return static_cast<uint32_t>(simple_rng(self->seed)) % upper_bound;
|
||||
},
|
||||
};
|
||||
|
||||
Null_System::Null_System()
|
||||
: System{
|
||||
std::make_unique<Tox_System>(),
|
||||
std::make_unique<Tox_Memory>(Tox_Memory{&null_memory_funcs, this}),
|
||||
std::make_unique<Network>(Network{&null_network_funcs, this}),
|
||||
std::make_unique<Tox_Random>(Tox_Random{&null_random_funcs, this}),
|
||||
}
|
||||
{
|
||||
sys->mono_time_callback = [](void *self) { return static_cast<Null_System *>(self)->clock; };
|
||||
sys->mono_time_user_data = this;
|
||||
sys->mem = mem.get();
|
||||
sys->ns = ns.get();
|
||||
sys->rng = rng.get();
|
||||
}
|
||||
|
||||
static uint16_t get_port(const Network_Addr *addr)
|
||||
{
|
||||
if (addr->addr.ss_family == AF_INET6) {
|
||||
return reinterpret_cast<const sockaddr_in6 *>(&addr->addr)->sin6_port;
|
||||
} else {
|
||||
assert(addr->addr.ss_family == AF_INET);
|
||||
return reinterpret_cast<const sockaddr_in *>(&addr->addr)->sin_port;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr Tox_Memory_Funcs record_memory_funcs = {
|
||||
/* .malloc = */
|
||||
 {
|
||||
self->push(true);
|
||||
return report_alloc(self->name_, "malloc", size, std::malloc(size));
|
||||
},
|
||||
/* .realloc = */
|
||||
 {
|
||||
self->push(true);
|
||||
return report_alloc(self->name_, "realloc", size, std::realloc(ptr, size));
|
||||
},
|
||||
/* .dealloc = */
|
||||
 { std::free(ptr); },
|
||||
};
|
||||
|
||||
static constexpr Network_Funcs record_network_funcs = {
|
||||
/* .close = */  { return 0; },
|
||||
/* .accept = */  { return Socket{2}; },
|
||||
/* .bind = */
|
||||
 {
|
||||
const uint16_t port = get_port(addr);
|
||||
if (self->global_.bound.find(port) != self->global_.bound.end()) {
|
||||
errno = EADDRINUSE;
|
||||
return -1;
|
||||
}
|
||||
self->global_.bound.emplace(port, self);
|
||||
self->port = port;
|
||||
return 0;
|
||||
},
|
||||
/* .listen = */  { return 0; },
|
||||
/* .connect = */  { return 0; },
|
||||
/* .recvbuf = */  { return 0; },
|
||||
/* .recv = */
|
||||
 {
|
||||
// Always fail.
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
},
|
||||
/* .recvfrom = */
|
||||
 {
|
||||
assert(sock.value == 42);
|
||||
if (self->recvq.empty()) {
|
||||
self->push("\xff\xff");
|
||||
errno = EWOULDBLOCK;
|
||||
if (Fuzz_Data::FUZZ_DEBUG) {
|
||||
std::printf("%s: recvfrom: no data\n", self->name_);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
const auto [from, packet] = std::move(self->recvq.front());
|
||||
self->recvq.pop_front();
|
||||
const size_t recvlen = std::min(len, packet.size());
|
||||
std::copy(packet.begin(), packet.end(), buf);
|
||||
|
||||
addr->addr = sockaddr_storage{};
|
||||
// Dummy Addr
|
||||
addr->addr.ss_family = AF_INET;
|
||||
|
||||
// We want an AF_INET address with dummy values
|
||||
sockaddr_in *addr_in = reinterpret_cast<sockaddr_in *>(&addr->addr);
|
||||
addr_in->sin_port = from;
|
||||
addr_in->sin_addr.s_addr = htonl(0x7f000002); // 127.0.0.2
|
||||
addr->size = sizeof(struct sockaddr);
|
||||
|
||||
assert(recvlen > 0 && recvlen <= INT_MAX);
|
||||
self->push(uint8_t(recvlen >> 8));
|
||||
self->push(uint8_t(recvlen & 0xff));
|
||||
if (Fuzz_Data::FUZZ_DEBUG) {
|
||||
std::printf("%s: recvfrom: %zu (%02x, %02x)\n", self->name_, recvlen,
|
||||
self->recording().end()[-2], self->recording().end()[-1]);
|
||||
}
|
||||
self->push(buf, recvlen);
|
||||
return static_cast<int>(recvlen);
|
||||
},
|
||||
/* .send = */
|
||||
 {
|
||||
// Always succeed.
|
||||
return static_cast<int>(len);
|
||||
},
|
||||
/* .sendto = */
|
||||
 {
|
||||
assert(sock.value == 42);
|
||||
auto backend = self->global_.bound.find(get_port(addr));
|
||||
assert(backend != self->global_.bound.end());
|
||||
backend->second->receive(self->port, buf, len);
|
||||
return static_cast<int>(len);
|
||||
},
|
||||
/* .socket = */
|
||||
 { return Socket{42}; },
|
||||
/* .socket_nonblock = */  { return 0; },
|
||||
/* .getsockopt = */
|
||||
 {
|
||||
std::memset(optval, 0, *optlen);
|
||||
return 0;
|
||||
},
|
||||
/* .setsockopt = */
|
||||
 { return 0; },
|
||||
};
|
||||
|
||||
static constexpr Tox_Random_Funcs record_random_funcs = {
|
||||
/* .bytes_callback = */
|
||||
 {
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
bytes[i] = simple_rng(self->seed_) & 0xff;
|
||||
self->push(bytes[i]);
|
||||
}
|
||||
if (Fuzz_Data::FUZZ_DEBUG) {
|
||||
std::printf(
|
||||
"%s: rng: %02x..%02x[%zu]\n", self->name_, bytes[0], bytes[length - 1], length);
|
||||
}
|
||||
},
|
||||
/* .uniform_callback = */
|
||||
fuzz_random_funcs.uniform_callback,
|
||||
};
|
||||
|
||||
Record_System::Record_System(Global &global, uint64_t seed, const char *name)
|
||||
: System{
|
||||
std::make_unique<Tox_System>(),
|
||||
std::make_unique<Tox_Memory>(Tox_Memory{&record_memory_funcs, this}),
|
||||
std::make_unique<Network>(Network{&record_network_funcs, this}),
|
||||
std::make_unique<Random>(Random{&record_random_funcs, this}),
|
||||
}
|
||||
, global_(global)
|
||||
, seed_(seed)
|
||||
, name_(name)
|
||||
{
|
||||
sys->mono_time_callback = [](void *self) { return static_cast<Record_System *>(self)->clock; };
|
||||
sys->mono_time_user_data = this;
|
||||
sys->mem = mem.get();
|
||||
sys->ns = ns.get();
|
||||
sys->rng = rng.get();
|
||||
}
|
||||
|
||||
void Record_System::receive(uint16_t send_port, const uint8_t *buf, size_t len)
|
||||
{
|
||||
assert(port != 0);
|
||||
recvq.emplace_back(send_port, std::vector<uint8_t>{buf, buf + len});
|
||||
}
|
||||
@@ -1,407 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Copyright © 2021-2025 The TokTok team.
|
||||
*/
|
||||
|
||||
#ifndef C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H
|
||||
#define C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "../../toxcore/tox_private.h"
|
||||
|
||||
struct Fuzz_Data {
|
||||
static constexpr bool FUZZ_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)
|
||||
, base_(input_data)
|
||||
, size_(input_size)
|
||||
{
|
||||
}
|
||||
|
||||
Fuzz_Data &operator=(const Fuzz_Data &rhs) = delete;
|
||||
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 (FUZZ_DEBUG) {
|
||||
std::printf("consume@%zu(%s): bool %s\n", fd.pos(), func, val ? "true" : "false");
|
||||
}
|
||||
++fd.data_;
|
||||
--fd.size_;
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
operator T()
|
||||
{
|
||||
const uint8_t *bytes = fd.consume(func, sizeof(T));
|
||||
T val;
|
||||
std::memcpy(&val, bytes, sizeof(T));
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
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(const char *func, std::size_t count)
|
||||
{
|
||||
const uint8_t *val = data_;
|
||||
if (FUZZ_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;
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief Consumes 1 byte of the fuzzer input or returns if no data available.
|
||||
*
|
||||
* This advances the fuzzer input data by 1 byte and consumes that byte in the
|
||||
* declaration.
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* CONSUME1_OR_RETURN(const uint8_t, one_byte, input);
|
||||
* @endcode
|
||||
*/
|
||||
#define CONSUME1_OR_RETURN(TYPE, NAME, INPUT) \
|
||||
if (INPUT.size() < sizeof(TYPE)) { \
|
||||
return; \
|
||||
} \
|
||||
TYPE NAME = INPUT.consume1(__func__)
|
||||
|
||||
/** @brief Consumes 1 byte of the fuzzer input or returns a value if no data
|
||||
* available.
|
||||
*
|
||||
* This advances the fuzzer input data by 1 byte and consumes that byte in the
|
||||
* declaration.
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* CONSUME1_OR_RETURN_VAL(const uint8_t one_byte, input, nullptr);
|
||||
* @endcode
|
||||
*/
|
||||
#define CONSUME1_OR_RETURN_VAL(TYPE, NAME, INPUT, VAL) \
|
||||
if (INPUT.size() < sizeof(TYPE)) { \
|
||||
return VAL; \
|
||||
} \
|
||||
TYPE NAME = INPUT.consume1(__func__)
|
||||
|
||||
/** @brief Consumes SIZE bytes of the fuzzer input or returns if not enough data available.
|
||||
*
|
||||
* This advances the fuzzer input data by SIZE byte and consumes those bytes in
|
||||
* the declaration. If less than SIZE bytes are available in the fuzzer input,
|
||||
* this macro returns from the enclosing function.
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* CONSUME_OR_RETURN(const uint8_t *ten_bytes, input, 10);
|
||||
* @endcode
|
||||
*/
|
||||
#define CONSUME_OR_RETURN(DECL, INPUT, SIZE) \
|
||||
if (INPUT.size() < SIZE) { \
|
||||
return; \
|
||||
} \
|
||||
DECL = INPUT.consume(__func__, SIZE)
|
||||
|
||||
#define CONSUME_OR_RETURN_VAL(DECL, INPUT, SIZE, VAL) \
|
||||
if (INPUT.size() < SIZE) { \
|
||||
return VAL; \
|
||||
} \
|
||||
DECL = INPUT.consume(__func__, SIZE)
|
||||
|
||||
#define CONSUME_OR_ABORT(DECL, INPUT, SIZE) \
|
||||
if (INPUT.size() < SIZE) { \
|
||||
abort(); \
|
||||
} \
|
||||
DECL = INPUT.consume(__func__, SIZE)
|
||||
|
||||
using Fuzz_Target = void (*)(Fuzz_Data &input);
|
||||
|
||||
template <Fuzz_Target... Args>
|
||||
struct Fuzz_Target_Selector;
|
||||
|
||||
template <Fuzz_Target Arg, Fuzz_Target... Args>
|
||||
struct Fuzz_Target_Selector<Arg, Args...> {
|
||||
static void select(uint8_t selector, Fuzz_Data &input)
|
||||
{
|
||||
if (selector == sizeof...(Args)) {
|
||||
return Arg(input);
|
||||
}
|
||||
return Fuzz_Target_Selector<Args...>::select(selector, input);
|
||||
}
|
||||
};
|
||||
|
||||
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 <Fuzz_Target... Args>
|
||||
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_Target_Selector<Args...>::select(selector, input);
|
||||
}
|
||||
|
||||
struct Tox_Memory;
|
||||
struct Network;
|
||||
struct Tox_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<Tox_System> sys;
|
||||
std::unique_ptr<Tox_Memory> mem;
|
||||
std::unique_ptr<Network> ns;
|
||||
std::unique_ptr<Tox_Random> rng;
|
||||
|
||||
System(std::unique_ptr<Tox_System> sys, std::unique_ptr<Tox_Memory> mem,
|
||||
std::unique_ptr<Network> ns, std::unique_ptr<Tox_Random> rng);
|
||||
System(System &&);
|
||||
|
||||
// Not inline because sizeof of the above 2 structs is not known everywhere.
|
||||
~System();
|
||||
|
||||
/**
|
||||
* During bootstrap, move the time forward a decent amount, because friend
|
||||
* finding and bootstrapping takes significant (around 10 seconds) wall
|
||||
* clock time that should be advanced more quickly in the test.
|
||||
*/
|
||||
static constexpr uint8_t BOOTSTRAP_ITERATION_INTERVAL = 200;
|
||||
/**
|
||||
* Less than BOOTSTRAP_ITERATION_INTERVAL because otherwise we'll spam
|
||||
* onion announce packets.
|
||||
*/
|
||||
static constexpr uint8_t MESSAGE_ITERATION_INTERVAL = 20;
|
||||
/**
|
||||
* Move the clock forward at least 20ms so at least some amount of
|
||||
* time passes on each iteration.
|
||||
*/
|
||||
static constexpr uint8_t MIN_ITERATION_INTERVAL = 20;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Tox_System implementation that consumes fuzzer input to produce network
|
||||
* inputs and random numbers. Once it runs out of fuzzer input, network receive
|
||||
* functions return no more data and the random numbers are always zero.
|
||||
*/
|
||||
struct Fuzz_System : System {
|
||||
Fuzz_Data &data;
|
||||
|
||||
explicit Fuzz_System(Fuzz_Data &input);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Tox_System implementation that consumes no fuzzer input but still has a
|
||||
* working and deterministic RNG. Network receive functions always fail, send
|
||||
* always succeeds.
|
||||
*/
|
||||
struct Null_System : System {
|
||||
uint64_t seed = 4; // chosen by fair dice roll. guaranteed to be random.
|
||||
|
||||
Null_System();
|
||||
};
|
||||
|
||||
template <typename V>
|
||||
class int_map {
|
||||
public:
|
||||
struct iterator {
|
||||
std::pair<uint16_t, V> pair;
|
||||
|
||||
bool operator==(const iterator &rhs) const { return pair.first == rhs.pair.first; }
|
||||
bool operator!=(const iterator &rhs) const { return pair.first != rhs.pair.first; }
|
||||
|
||||
std::pair<uint16_t, V> operator*() const { return pair; }
|
||||
const std::pair<uint16_t, V> *operator->() const { return &pair; }
|
||||
};
|
||||
|
||||
int_map() = default;
|
||||
~int_map() = default;
|
||||
|
||||
iterator find(uint16_t key) const
|
||||
{
|
||||
if (!values[key]) {
|
||||
return end();
|
||||
}
|
||||
return {{key, values[key]}};
|
||||
}
|
||||
|
||||
iterator end() const { return {{static_cast<uint16_t>(values.size()), nullptr}}; }
|
||||
|
||||
void emplace(uint16_t key, V value) { values[key] = value; }
|
||||
|
||||
private:
|
||||
std::array<V, UINT16_MAX> values;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Tox_System implementation that records all I/O but does not actually
|
||||
* perform any real I/O. Everything inside this system is hermetic in-process
|
||||
* and fully deterministic.
|
||||
*
|
||||
* Note: take care not to initialise two systems with the same seed, since
|
||||
* that's the only thing distinguishing the system's behaviour. Two toxes
|
||||
* initialised with the same seed will be identical (same keys, etc.).
|
||||
*/
|
||||
struct Record_System : System {
|
||||
static constexpr bool FUZZ_DEBUG = Fuzz_Data::FUZZ_DEBUG;
|
||||
|
||||
/** @brief State shared between all tox instances. */
|
||||
struct Global {
|
||||
/** @brief Bound UDP ports and their system instance.
|
||||
*
|
||||
* This implements an in-process network where instances can send
|
||||
* packets to other instances by inserting them into the receiver's
|
||||
* recvq using the receive function.
|
||||
*
|
||||
* We need to keep track of ports associated with recv queues because
|
||||
* toxcore sends packets to itself sometimes when doing onion routing
|
||||
* with only 2 nodes in the network.
|
||||
*/
|
||||
int_map<Record_System *> bound;
|
||||
};
|
||||
|
||||
Global &global_;
|
||||
uint64_t seed_; //!< Current PRNG state.
|
||||
const char *name_; //!< Tox system name ("tox1"/"tox2") for logging.
|
||||
|
||||
std::deque<std::pair<uint16_t, std::vector<uint8_t>>> recvq;
|
||||
uint16_t port = 0; //!< Sending port for this system instance.
|
||||
|
||||
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 (FUZZ_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 (FUZZ_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 (FUZZ_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 <std::size_t N>
|
||||
void push(const char (&bytes)[N])
|
||||
{
|
||||
push(reinterpret_cast<const uint8_t *>(bytes), N - 1);
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> &recording() const { return recording_; }
|
||||
std::vector<uint8_t> take_recording() const { return std::move(recording_); }
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> recording_;
|
||||
};
|
||||
|
||||
/** @brief Enable debug logging.
|
||||
*
|
||||
* This should not be enabled in fuzzer code while fuzzing, as console I/O slows
|
||||
* everything down drastically. It's useful while developing the fuzzer and the
|
||||
* protodump program.
|
||||
*/
|
||||
extern const bool FUZZ_DEBUG;
|
||||
|
||||
inline constexpr char tox_log_level_name(Tox_Log_Level level)
|
||||
{
|
||||
switch (level) {
|
||||
case TOX_LOG_LEVEL_TRACE:
|
||||
return 'T';
|
||||
case TOX_LOG_LEVEL_DEBUG:
|
||||
return 'D';
|
||||
case TOX_LOG_LEVEL_INFO:
|
||||
return 'I';
|
||||
case TOX_LOG_LEVEL_WARNING:
|
||||
return 'W';
|
||||
case TOX_LOG_LEVEL_ERROR:
|
||||
return 'E';
|
||||
}
|
||||
|
||||
return '?';
|
||||
}
|
||||
|
||||
#endif // C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H
|
||||
@@ -1,17 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Copyright © 2022-2025 The TokTok team.
|
||||
*/
|
||||
|
||||
#ifndef C_TOXCORE_TESTING_FUZZING_FUZZ_TOX_H
|
||||
#define C_TOXCORE_TESTING_FUZZING_FUZZ_TOX_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "../../toxcore/network.h"
|
||||
|
||||
constexpr uint16_t SIZE_IP_PORT = SIZE_IP6 + sizeof(uint16_t);
|
||||
|
||||
template <typename T>
|
||||
using Ptr = std::unique_ptr<T, void (*)(T *)>;
|
||||
|
||||
#endif // C_TOXCORE_TESTING_FUZZING_FUZZ_TOX_H
|
||||
@@ -26,23 +26,41 @@
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "../../toxcore/tox.h"
|
||||
#include "../../toxcore/tox_dispatch.h"
|
||||
#include "../../toxcore/tox_events.h"
|
||||
#include "../../toxcore/tox_private.h"
|
||||
#include "fuzz_support.hh"
|
||||
#include "../support/public/simulated_environment.hh"
|
||||
|
||||
namespace {
|
||||
|
||||
/** @brief Number of messages to exchange between tox1 and tox2.
|
||||
*
|
||||
* The higher this number, the more room we give the fuzzer to mutate the
|
||||
* exchange into something more interesting. If it's too high, the fuzzer will
|
||||
* be slow.
|
||||
*/
|
||||
using tox::test::FakeClock;
|
||||
using tox::test::Packet;
|
||||
using tox::test::ScopedToxSystem;
|
||||
using tox::test::SimulatedEnvironment;
|
||||
|
||||
constexpr uint32_t MESSAGE_COUNT = 5;
|
||||
|
||||
class Recorder {
|
||||
public:
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
void push(bool val) { data.push_back(val); }
|
||||
void push(uint8_t val) { data.push_back(val); }
|
||||
void push(const uint8_t *bytes, size_t size) { data.insert(data.end(), bytes, bytes + size); }
|
||||
|
||||
// Format: 2 bytes length (big-endian), then data.
|
||||
void push_packet(const uint8_t *bytes, size_t size)
|
||||
{
|
||||
assert(size <= 65535);
|
||||
push(static_cast<uint8_t>(size >> 8));
|
||||
push(static_cast<uint8_t>(size & 0xFF));
|
||||
push(bytes, size);
|
||||
}
|
||||
};
|
||||
|
||||
struct State {
|
||||
Tox *tox;
|
||||
uint32_t done;
|
||||
@@ -91,7 +109,6 @@ void setup_callbacks(Tox_Dispatch *dispatch)
|
||||
tox_events_callback_friend_connection_status(
|
||||
dispatch, [](const Tox_Event_Friend_Connection_Status *event, void *user_data) {
|
||||
State *state = static_cast<State *>(user_data);
|
||||
// OK: friend came online.
|
||||
const uint32_t friend_number
|
||||
= tox_event_friend_connection_status_get_friend_number(event);
|
||||
assert(friend_number == 0);
|
||||
@@ -163,9 +180,7 @@ void setup_callbacks(Tox_Dispatch *dispatch)
|
||||
assert(!tox_event_friend_typing_get_typing(event));
|
||||
});
|
||||
tox_events_callback_self_connection_status(
|
||||
dispatch, [](const Tox_Event_Self_Connection_Status *event, void *user_data) {
|
||||
// OK: we got connected.
|
||||
});
|
||||
dispatch, [](const Tox_Event_Self_Connection_Status *event, void *user_data) {});
|
||||
}
|
||||
|
||||
void dump(std::vector<uint8_t> recording, const char *filename)
|
||||
@@ -177,69 +192,80 @@ void dump(std::vector<uint8_t> recording, const char *filename)
|
||||
|
||||
void RecordBootstrap(const char *init, const char *bootstrap)
|
||||
{
|
||||
auto global = std::make_unique<Record_System::Global>();
|
||||
SimulatedEnvironment env1;
|
||||
SimulatedEnvironment env2;
|
||||
|
||||
// Set deterministic seeds.
|
||||
std::minstd_rand rng1(4);
|
||||
env1.fake_random().set_entropy_source([&](uint8_t *out, size_t count) {
|
||||
std::uniform_int_distribution<uint16_t> dist(0, 255);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
out[i] = static_cast<uint8_t>(dist(rng1));
|
||||
});
|
||||
|
||||
std::minstd_rand rng2(5);
|
||||
env2.fake_random().set_entropy_source([&](uint8_t *out, size_t count) {
|
||||
std::uniform_int_distribution<uint16_t> dist(0, 255);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
out[i] = static_cast<uint8_t>(dist(rng2));
|
||||
});
|
||||
|
||||
Recorder recorder1;
|
||||
Recorder recorder2;
|
||||
|
||||
env1.fake_memory().set_observer([&](bool success) { recorder1.push(success); });
|
||||
|
||||
env1.fake_random().set_observer(
|
||||
[&](const uint8_t *data, size_t count) { recorder1.push(data, count); });
|
||||
|
||||
auto node1 = env1.create_node(33445);
|
||||
auto node2 = env2.create_node(33446);
|
||||
|
||||
// Record received packets.
|
||||
node1->endpoint->set_recv_observer([&](const std::vector<uint8_t> &data, const IP_Port &from) {
|
||||
recorder1.push_packet(data.data(), data.size());
|
||||
});
|
||||
|
||||
// Bridge the two simulated networks.
|
||||
env1.simulation().net().add_observer(
|
||||
[&](const Packet &p) { env2.simulation().net().send_packet(p); });
|
||||
|
||||
env2.simulation().net().add_observer(
|
||||
[&](const Packet &p) { env1.simulation().net().send_packet(p); });
|
||||
|
||||
Tox_Options *opts = tox_options_new(nullptr);
|
||||
assert(opts != nullptr);
|
||||
|
||||
tox_options_set_local_discovery_enabled(opts, false);
|
||||
|
||||
tox_options_set_log_callback(opts,
|
||||
[](Tox *tox, Tox_Log_Level level, const char *file, uint32_t line, const char *func,
|
||||
const char *message, void *user_data) {
|
||||
// Log to stdout.
|
||||
std::printf("[%s] %c %s:%d(%s): %s\n", static_cast<Record_System *>(user_data)->name_,
|
||||
tox_log_level_name(level), file, line, func, message);
|
||||
});
|
||||
Tox_Options_Testing opts_test1;
|
||||
opts_test1.operating_system = &node1->system;
|
||||
|
||||
Tox_Err_New error_new;
|
||||
Tox_Err_New_Testing error_new_testing;
|
||||
Tox_Options_Testing tox_options_testing;
|
||||
|
||||
auto sys1 = std::make_unique<Record_System>(*global, 4, "tox1"); // fair dice roll
|
||||
tox_options_set_log_user_data(opts, sys1.get());
|
||||
tox_options_testing.operating_system = sys1->sys.get();
|
||||
Tox *tox1 = tox_new_testing(opts, &error_new, &tox_options_testing, &error_new_testing);
|
||||
Tox *tox1 = tox_new_testing(opts, &error_new, &opts_test1, &error_new_testing);
|
||||
assert(tox1 != nullptr);
|
||||
assert(error_new == TOX_ERR_NEW_OK);
|
||||
assert(error_new_testing == TOX_ERR_NEW_TESTING_OK);
|
||||
std::array<uint8_t, TOX_ADDRESS_SIZE> address1;
|
||||
tox_self_get_address(tox1, address1.data());
|
||||
std::array<uint8_t, TOX_PUBLIC_KEY_SIZE> pk1;
|
||||
tox_self_get_public_key(tox1, pk1.data());
|
||||
std::array<uint8_t, TOX_PUBLIC_KEY_SIZE> dht_key1;
|
||||
tox_self_get_dht_id(tox1, dht_key1.data());
|
||||
|
||||
auto sys2 = std::make_unique<Record_System>(*global, 5, "tox2"); // unfair dice roll
|
||||
tox_options_set_log_user_data(opts, sys2.get());
|
||||
tox_options_testing.operating_system = sys2->sys.get();
|
||||
Tox *tox2 = tox_new_testing(opts, &error_new, &tox_options_testing, &error_new_testing);
|
||||
Tox_Options_Testing opts_test2;
|
||||
opts_test2.operating_system = &node2->system;
|
||||
|
||||
Tox *tox2 = tox_new_testing(opts, &error_new, &opts_test2, &error_new_testing);
|
||||
assert(tox2 != nullptr);
|
||||
assert(error_new == TOX_ERR_NEW_OK);
|
||||
assert(error_new_testing == TOX_ERR_NEW_TESTING_OK);
|
||||
std::array<uint8_t, TOX_ADDRESS_SIZE> address2;
|
||||
tox_self_get_address(tox2, address2.data());
|
||||
std::array<uint8_t, TOX_PUBLIC_KEY_SIZE> pk2;
|
||||
tox_self_get_public_key(tox2, pk2.data());
|
||||
std::array<uint8_t, TOX_PUBLIC_KEY_SIZE> dht_key2;
|
||||
tox_self_get_dht_id(tox2, dht_key2.data());
|
||||
|
||||
assert(address1 != address2);
|
||||
assert(pk1 != pk2);
|
||||
assert(dht_key1 != dht_key2);
|
||||
|
||||
tox_options_free(opts);
|
||||
|
||||
const uint16_t port = tox_self_get_udp_port(tox1, nullptr);
|
||||
std::array<uint8_t, TOX_ADDRESS_SIZE> address1;
|
||||
tox_self_get_address(tox1, address1.data());
|
||||
std::array<uint8_t, TOX_PUBLIC_KEY_SIZE> dht_key1;
|
||||
tox_self_get_dht_id(tox1, dht_key1.data());
|
||||
|
||||
const bool udp_success = tox_bootstrap(tox2, "127.0.0.2", port, dht_key1.data(), nullptr);
|
||||
// Bootstrap tox2 to tox1.
|
||||
const bool udp_success = tox_bootstrap(tox2, "127.0.0.1", 33445, dht_key1.data(), nullptr);
|
||||
assert(udp_success);
|
||||
|
||||
tox_events_init(tox1);
|
||||
tox_events_init(tox2);
|
||||
|
||||
Tox_Dispatch *dispatch = tox_dispatch_new(nullptr);
|
||||
assert(dispatch != nullptr);
|
||||
setup_callbacks(dispatch);
|
||||
|
||||
State state1 = {tox1, 0};
|
||||
@@ -250,35 +276,26 @@ void RecordBootstrap(const char *init, const char *bootstrap)
|
||||
Tox_Events *events;
|
||||
|
||||
events = tox_events_iterate(tox1, true, &error_iterate);
|
||||
assert(tox_events_equal(sys1->sys.get(), events, events));
|
||||
tox_dispatch_invoke(dispatch, events, &state1);
|
||||
tox_events_free(events);
|
||||
|
||||
events = tox_events_iterate(tox2, true, &error_iterate);
|
||||
assert(tox_events_equal(sys2->sys.get(), events, events));
|
||||
tox_dispatch_invoke(dispatch, events, &state2);
|
||||
tox_events_free(events);
|
||||
|
||||
// Move the clock forward a decent amount so all the time-based checks
|
||||
// trigger more quickly.
|
||||
sys1->clock += clock_increment;
|
||||
sys2->clock += clock_increment;
|
||||
// Record the clock increment.
|
||||
env1.fake_clock().advance(clock_increment);
|
||||
recorder1.push(clock_increment);
|
||||
|
||||
if (Fuzz_Data::FUZZ_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);
|
||||
env2.fake_clock().advance(clock_increment);
|
||||
|
||||
env1.simulation().net().process_events(env1.fake_clock().current_time_ms());
|
||||
env2.simulation().net().process_events(env2.fake_clock().current_time_ms());
|
||||
};
|
||||
|
||||
while (tox_self_get_connection_status(tox1) == TOX_CONNECTION_NONE
|
||||
|| tox_self_get_connection_status(tox2) == TOX_CONNECTION_NONE) {
|
||||
if (Fuzz_Data::FUZZ_DEBUG) {
|
||||
std::printf("tox1: %d, tox2: %d\n", tox_self_get_connection_status(tox1),
|
||||
tox_self_get_connection_status(tox2));
|
||||
}
|
||||
iterate(System::BOOTSTRAP_ITERATION_INTERVAL);
|
||||
iterate(200);
|
||||
}
|
||||
|
||||
std::printf("toxes are online\n");
|
||||
@@ -289,27 +306,18 @@ void RecordBootstrap(const char *init, const char *bootstrap)
|
||||
|
||||
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 (Fuzz_Data::FUZZ_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),
|
||||
tox_friend_get_connection_status(tox2, 0, nullptr));
|
||||
}
|
||||
iterate(System::BOOTSTRAP_ITERATION_INTERVAL);
|
||||
iterate(200);
|
||||
}
|
||||
|
||||
std::printf("tox clients connected\n");
|
||||
|
||||
dump(sys1->take_recording(), init);
|
||||
dump(recorder1.data, init);
|
||||
|
||||
// Clear the recorder.
|
||||
recorder1.data.clear();
|
||||
|
||||
while (state1.done < MESSAGE_COUNT && state2.done < MESSAGE_COUNT) {
|
||||
if (Fuzz_Data::FUZZ_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),
|
||||
tox_friend_get_connection_status(tox2, 0, nullptr));
|
||||
}
|
||||
iterate(System::MESSAGE_ITERATION_INTERVAL);
|
||||
iterate(20);
|
||||
}
|
||||
|
||||
std::printf("test complete\n");
|
||||
@@ -318,7 +326,7 @@ void RecordBootstrap(const char *init, const char *bootstrap)
|
||||
tox_kill(tox2);
|
||||
tox_kill(tox1);
|
||||
|
||||
dump(sys1->recording(), bootstrap);
|
||||
dump(recorder1.data, bootstrap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
#include "../../toxcore/crypto_core.h"
|
||||
#include "../../toxcore/tox.h"
|
||||
#include "../../toxcore/tox_dispatch.h"
|
||||
#include "../../toxcore/tox_events.h"
|
||||
#include "../../toxcore/tox_private.h"
|
||||
#include "fuzz_support.hh"
|
||||
#include "fuzz_tox.hh"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool PROTODUMP_DEBUG = Fuzz_Data::FUZZ_DEBUG;
|
||||
|
||||
void setup_callbacks(Tox_Dispatch *dispatch)
|
||||
{
|
||||
tox_events_callback_conference_connected(
|
||||
dispatch, [](const Tox_Event_Conference_Connected *event, void *user_data) {
|
||||
assert(event == nullptr);
|
||||
});
|
||||
tox_events_callback_conference_connected(
|
||||
dispatch, [](const Tox_Event_Conference_Connected *event, void *user_data) {
|
||||
assert(event == nullptr);
|
||||
});
|
||||
tox_events_callback_conference_invite(
|
||||
dispatch, [](const Tox_Event_Conference_Invite *event, void *user_data) {
|
||||
assert(event == nullptr);
|
||||
});
|
||||
tox_events_callback_conference_message(
|
||||
dispatch, [](const Tox_Event_Conference_Message *event, void *user_data) {
|
||||
assert(event == nullptr);
|
||||
});
|
||||
tox_events_callback_conference_peer_list_changed(
|
||||
dispatch, [](const Tox_Event_Conference_Peer_List_Changed *event, void *user_data) {
|
||||
assert(event == nullptr);
|
||||
});
|
||||
tox_events_callback_conference_peer_name(
|
||||
dispatch, [](const Tox_Event_Conference_Peer_Name *event, void *user_data) {
|
||||
assert(event == nullptr);
|
||||
});
|
||||
tox_events_callback_conference_title(dispatch,
|
||||
[](const Tox_Event_Conference_Title *event, void *user_data) { assert(event == nullptr); });
|
||||
tox_events_callback_file_chunk_request(
|
||||
dispatch, [](const Tox_Event_File_Chunk_Request *event, void *user_data) {
|
||||
assert(event == nullptr);
|
||||
});
|
||||
tox_events_callback_file_recv(dispatch,
|
||||
[](const Tox_Event_File_Recv *event, void *user_data) { assert(event == nullptr); });
|
||||
tox_events_callback_file_recv_chunk(dispatch,
|
||||
[](const Tox_Event_File_Recv_Chunk *event, void *user_data) { assert(event == nullptr); });
|
||||
tox_events_callback_file_recv_control(
|
||||
dispatch, [](const Tox_Event_File_Recv_Control *event, void *user_data) {
|
||||
assert(event == nullptr);
|
||||
});
|
||||
tox_events_callback_friend_connection_status(
|
||||
dispatch, [](const Tox_Event_Friend_Connection_Status *event, void *user_data) {
|
||||
Tox *tox = static_cast<Tox *>(user_data);
|
||||
// OK: friend came online.
|
||||
const uint32_t friend_number
|
||||
= tox_event_friend_connection_status_get_friend_number(event);
|
||||
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, [](const Tox_Event_Friend_Lossless_Packet *event, void *user_data) {
|
||||
assert(event == nullptr);
|
||||
});
|
||||
tox_events_callback_friend_lossy_packet(
|
||||
dispatch, [](const Tox_Event_Friend_Lossy_Packet *event, void *user_data) {
|
||||
assert(event == nullptr);
|
||||
});
|
||||
tox_events_callback_friend_message(
|
||||
dispatch, [](const Tox_Event_Friend_Message *event, void *user_data) {
|
||||
Tox *tox = static_cast<Tox *>(user_data);
|
||||
const uint32_t friend_number = tox_event_friend_message_get_friend_number(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);
|
||||
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, [](const Tox_Event_Friend_Name *event, void *user_data) {
|
||||
const uint32_t friend_number = tox_event_friend_name_get_friend_number(event);
|
||||
assert(friend_number == 0);
|
||||
});
|
||||
tox_events_callback_friend_read_receipt(
|
||||
dispatch, [](const Tox_Event_Friend_Read_Receipt *event, void *user_data) {
|
||||
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<uint32_t *>(user_data);
|
||||
*done = std::max(*done, message_id);
|
||||
});
|
||||
tox_events_callback_friend_request(
|
||||
dispatch, [](const Tox_Event_Friend_Request *event, void *user_data) {
|
||||
Tox *tox = static_cast<Tox *>(user_data);
|
||||
Tox_Err_Friend_Add err;
|
||||
tox_friend_add_norequest(tox, tox_event_friend_request_get_public_key(event), &err);
|
||||
});
|
||||
tox_events_callback_friend_status(
|
||||
dispatch, [](const Tox_Event_Friend_Status *event, void *user_data) {
|
||||
const uint32_t friend_number = tox_event_friend_status_get_friend_number(event);
|
||||
assert(friend_number == 0);
|
||||
});
|
||||
tox_events_callback_friend_status_message(
|
||||
dispatch, [](const Tox_Event_Friend_Status_Message *event, void *user_data) {
|
||||
const uint32_t friend_number = tox_event_friend_status_message_get_friend_number(event);
|
||||
assert(friend_number == 0);
|
||||
});
|
||||
tox_events_callback_friend_typing(
|
||||
dispatch, [](const Tox_Event_Friend_Typing *event, void *user_data) {
|
||||
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, [](const Tox_Event_Self_Connection_Status *event, void *user_data) {
|
||||
// OK: we got connected.
|
||||
});
|
||||
}
|
||||
|
||||
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<Tox_Options> opts(tox_options_new(nullptr), tox_options_free);
|
||||
assert(opts != nullptr);
|
||||
tox_options_set_local_discovery_enabled(opts.get(), false);
|
||||
|
||||
Tox_Options_Testing tox_options_testing;
|
||||
tox_options_testing.operating_system = sys.sys.get();
|
||||
|
||||
tox_options_set_log_callback(opts.get(),
|
||||
[](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 (PROTODUMP_DEBUG) {
|
||||
std::printf("[tox1] %c %s:%d(%s): %s\n", tox_log_level_name(level), file, line,
|
||||
func, message);
|
||||
}
|
||||
});
|
||||
|
||||
Tox_Err_New error_new;
|
||||
Tox_Err_New_Testing error_new_testing;
|
||||
Tox *tox = tox_new_testing(opts.get(), &error_new, &tox_options_testing, &error_new_testing);
|
||||
|
||||
if (tox == nullptr) {
|
||||
// It might fail, because some I/O happens in tox_new, and the fuzzer
|
||||
// might do things that make that I/O fail.
|
||||
return;
|
||||
}
|
||||
|
||||
assert(error_new == TOX_ERR_NEW_OK);
|
||||
assert(error_new_testing == TOX_ERR_NEW_TESTING_OK);
|
||||
|
||||
tox_events_init(tox);
|
||||
|
||||
Tox_Dispatch *dispatch = tox_dispatch_new(nullptr);
|
||||
assert(dispatch != nullptr);
|
||||
setup_callbacks(dispatch);
|
||||
|
||||
while (!input.empty()) {
|
||||
Tox_Err_Events_Iterate error_iterate;
|
||||
Tox_Events *events = tox_events_iterate(tox, true, &error_iterate);
|
||||
tox_events_equal(tox_get_system(tox), events, events); // TODO(iphydf): assert?
|
||||
tox_dispatch_invoke(dispatch, events, tox);
|
||||
tox_events_free(events);
|
||||
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 (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);
|
||||
tox_kill(tox);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
Fuzz_Data input{data, size};
|
||||
TestEndToEnd(input);
|
||||
return 0; // Non-zero return values are reserved for future use.
|
||||
}
|
||||
@@ -4,10 +4,14 @@
|
||||
|
||||
#include "../../toxcore/tox.h"
|
||||
#include "../../toxcore/tox_private.h"
|
||||
#include "fuzz_support.hh"
|
||||
#include "../support/public/fuzz_data.hh"
|
||||
#include "../support/public/simulated_environment.hh"
|
||||
|
||||
namespace {
|
||||
|
||||
using tox::test::Fuzz_Data;
|
||||
using tox::test::SimulatedEnvironment;
|
||||
|
||||
void TestSaveDataLoading(Fuzz_Data &input)
|
||||
{
|
||||
Tox_Err_Options_New error_options;
|
||||
@@ -27,10 +31,12 @@ void TestSaveDataLoading(Fuzz_Data &input)
|
||||
tox_options_set_savedata_type(tox_options, TOX_SAVEDATA_TYPE_TOX_SAVE);
|
||||
|
||||
Tox_Options_Testing tox_options_testing;
|
||||
Null_System sys;
|
||||
tox_options_testing.operating_system = sys.sys.get();
|
||||
SimulatedEnvironment env;
|
||||
auto node = env.create_node(33445);
|
||||
tox_options_testing.operating_system = &node->system;
|
||||
|
||||
Tox *tox = tox_new_testing(tox_options, nullptr, &tox_options_testing, nullptr);
|
||||
Tox_Err_New_Testing err_testing;
|
||||
Tox *tox = tox_new_testing(tox_options, nullptr, &tox_options_testing, &err_testing);
|
||||
tox_options_free(tox_options);
|
||||
if (tox == nullptr) {
|
||||
// Tox save was invalid, we're finished here
|
||||
|
||||
Reference in New Issue
Block a user