Squashed 'external/toxcore/c-toxcore/' content from commit 67badf69

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: 67badf69416a74e74f6d7eb51dd96f37282b8455
This commit is contained in:
2023-07-25 11:53:09 +02:00
commit 227425b90e
467 changed files with 116591 additions and 0 deletions

101
testing/fuzzing/BUILD.bazel Normal file
View File

@ -0,0 +1,101 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test")
load("@rules_fuzzing//fuzzing/private:binary.bzl", "fuzzing_binary") # buildifier: disable=bzl-visibility
package(features = ["layering_check"])
cc_library(
name = "fuzz_support",
srcs = [
"func_conversion.h",
"fuzz_support.cc",
],
hdrs = ["fuzz_support.h"],
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.h"],
visibility = ["//c-toxcore:__subpackages__"],
deps = [":fuzz_support"],
)
cc_fuzz_test(
name = "bootstrap_fuzz_test",
srcs = ["bootstrap_harness.cc"],
copts = ["-UNDEBUG"],
corpus = ["//tools/toktok-fuzzer/corpus:bootstrap_fuzzer"],
deps = [
":fuzz_support",
":fuzz_tox",
"//c-toxcore/toxcore:tox",
"//c-toxcore/toxcore:tox_dispatch",
"//c-toxcore/toxcore:tox_events",
],
)
cc_fuzz_test(
name = "e2e_fuzz_test",
srcs = ["e2e_fuzz_test.cc"],
copts = ["-UNDEBUG"],
corpus = ["//tools/toktok-fuzzer/corpus:e2e_fuzz_test"],
data = ["//tools/toktok-fuzzer/init:e2e_fuzz_test.dat"],
deps = [
":fuzz_support",
":fuzz_tox",
"//c-toxcore/toxcore:tox",
"//c-toxcore/toxcore:tox_dispatch",
"//c-toxcore/toxcore:tox_events",
],
)
cc_fuzz_test(
name = "toxsave_fuzz_test",
srcs = ["toxsave_harness.cc"],
copts = ["-UNDEBUG"],
corpus = ["//tools/toktok-fuzzer/corpus:toxsave_fuzzer"],
deps = [
":fuzz_support",
"//c-toxcore/toxcore:tox",
],
)
cc_binary(
name = "protodump",
srcs = ["protodump.cc"],
copts = ["-UNDEBUG"],
deps = [
":fuzz_support",
"//c-toxcore/toxcore:tox",
"//c-toxcore/toxcore:tox_dispatch",
"//c-toxcore/toxcore:tox_events",
"//c-toxcore/toxcore:util",
],
)
fuzzing_binary(
name = "protodump_bin",
testonly = True,
binary = ":protodump",
engine = "@rules_fuzzing//fuzzing:cc_engine",
tags = ["manual"],
)
cc_fuzz_test(
name = "protodump_reduce",
srcs = ["protodump_reduce.cc"],
copts = ["-UNDEBUG"],
deps = [
":fuzz_support",
":fuzz_tox",
"//c-toxcore/toxcore:tox",
"//c-toxcore/toxcore:tox_dispatch",
"//c-toxcore/toxcore:tox_events",
],
)

View File

@ -0,0 +1,26 @@
# For coverage tests
target_compile_definitions(toxcore_static PUBLIC "FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION")
# Override network and random functions
add_library(fuzz_support func_conversion.h fuzz_support.cc fuzz_support.h)
set(LIBFUZZER_LINKER_FLAGS)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(LIBFUZZER_LINKER_FLAGS "-fsanitize=fuzzer")
else()
message(SEND_ERROR "Compiler must be Clang to build fuzz targets")
endif()
# Fuzzes the toxsave API
add_executable(toxsave_fuzzer toxsave_harness.cc)
target_link_libraries(toxsave_fuzzer toxcore_static fuzz_support ${LIBFUZZER_LINKER_FLAGS})
# Fuzzes the bootstrap process
add_executable(bootstrap_fuzzer bootstrap_harness.cc)
target_link_libraries(bootstrap_fuzzer toxcore_static fuzz_support ${LIBFUZZER_LINKER_FLAGS})
add_executable(DHT_fuzz_test ../../toxcore/DHT_fuzz_test.cc)
target_link_libraries(DHT_fuzz_test toxcore_static fuzz_support ${LIBFUZZER_LINKER_FLAGS})
add_executable(tox_events_fuzz_test ../../toxcore/tox_events_fuzz_test.cc)
target_link_libraries(tox_events_fuzz_test toxcore_static fuzz_support ${LIBFUZZER_LINKER_FLAGS})

View File

@ -0,0 +1,192 @@
#include <cassert>
#include <cstdio>
#include "../../toxcore/tox.h"
#include "../../toxcore/tox_dispatch.h"
#include "../../toxcore/tox_events.h"
#include "fuzz_support.h"
#include "fuzz_tox.h"
namespace {
void setup_callbacks(Tox_Dispatch *dispatch)
{
tox_events_callback_conference_connected(
dispatch, [](Tox *tox, const Tox_Event_Conference_Connected *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_connected(
dispatch, [](Tox *tox, const Tox_Event_Conference_Connected *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_invite(
dispatch, [](Tox *tox, const Tox_Event_Conference_Invite *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_message(
dispatch, [](Tox *tox, const Tox_Event_Conference_Message *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_peer_list_changed(dispatch,
[](Tox *tox, const Tox_Event_Conference_Peer_List_Changed *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_peer_name(
dispatch, [](Tox *tox, const Tox_Event_Conference_Peer_Name *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_title(
dispatch, [](Tox *tox, const Tox_Event_Conference_Title *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_file_chunk_request(
dispatch, [](Tox *tox, const Tox_Event_File_Chunk_Request *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_file_recv(
dispatch, [](Tox *tox, const Tox_Event_File_Recv *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_file_recv_chunk(
dispatch, [](Tox *tox, const Tox_Event_File_Recv_Chunk *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_file_recv_control(
dispatch, [](Tox *tox, const Tox_Event_File_Recv_Control *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_friend_connection_status(
dispatch, [](Tox *tox, const Tox_Event_Friend_Connection_Status *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_friend_lossless_packet(
dispatch, [](Tox *tox, const Tox_Event_Friend_Lossless_Packet *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_friend_lossy_packet(
dispatch, [](Tox *tox, const Tox_Event_Friend_Lossy_Packet *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_friend_message(
dispatch, [](Tox *tox, const Tox_Event_Friend_Message *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_friend_name(
dispatch, [](Tox *tox, const Tox_Event_Friend_Name *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_friend_read_receipt(
dispatch, [](Tox *tox, const Tox_Event_Friend_Read_Receipt *event, void *user_data) {
assert(event == nullptr);
});
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) {
assert(event == nullptr);
});
tox_events_callback_friend_status_message(
dispatch, [](Tox *tox, const Tox_Event_Friend_Status_Message *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_friend_typing(
dispatch, [](Tox *tox, const Tox_Event_Friend_Typing *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_self_connection_status(
dispatch, [](Tox *tox, const Tox_Event_Self_Connection_Status *event, void *user_data) {
assert(event == nullptr);
});
}
void TestBootstrap(Fuzz_Data &input)
{
Fuzz_System sys(input);
Ptr<Tox_Options> opts(tox_options_new(nullptr), tox_options_free);
assert(opts != nullptr);
tox_options_set_operating_system(opts.get(), 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 (DEBUG) {
std::printf("[tox1] %c %s:%d(%s): %s\n", tox_log_level_name(level), file, line,
func, message);
}
});
CONSUME1_OR_RETURN(const uint8_t proxy_type, input);
if (proxy_type == 0) {
tox_options_set_proxy_type(opts.get(), TOX_PROXY_TYPE_NONE);
} else if (proxy_type == 1) {
tox_options_set_proxy_type(opts.get(), TOX_PROXY_TYPE_SOCKS5);
tox_options_set_proxy_host(opts.get(), "127.0.0.1");
tox_options_set_proxy_port(opts.get(), 8080);
} else if (proxy_type == 2) {
tox_options_set_proxy_type(opts.get(), TOX_PROXY_TYPE_HTTP);
tox_options_set_proxy_host(opts.get(), "127.0.0.1");
tox_options_set_proxy_port(opts.get(), 8080);
}
CONSUME1_OR_RETURN(const uint8_t tcp_relay_enabled, input);
if (tcp_relay_enabled >= (UINT8_MAX / 2)) {
tox_options_set_tcp_port(opts.get(), 33445);
}
Tox_Err_New error_new;
Tox *tox = tox_new(opts.get(), &error_new);
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);
uint8_t pub_key[TOX_PUBLIC_KEY_SIZE] = {0};
const bool udp_success = tox_bootstrap(tox, "127.0.0.2", 33446, pub_key, nullptr);
assert(udp_success);
const bool tcp_success = tox_add_tcp_relay(tox, "127.0.0.2", 33446, pub_key, nullptr);
assert(tcp_success);
tox_events_init(tox);
Tox_Dispatch *dispatch = tox_dispatch_new(nullptr);
assert(dispatch != nullptr);
setup_callbacks(dispatch);
while (input.size > 0) {
Tox_Err_Events_Iterate error_iterate;
Tox_Events *events = tox_events_iterate(tox, true, &error_iterate);
assert(tox_events_equal(events, events));
tox_dispatch_invoke(dispatch, events, tox, nullptr);
tox_events_free(events);
// Move the clock forward a decent amount so all the time-based checks
// trigger more quickly.
sys.clock += 200;
}
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};
TestBootstrap(input);
return 0; // Non-zero return values are reserved for future use.
}

View File

@ -0,0 +1,213 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cassert>
#include <cstdio>
#include <fstream>
#include <vector>
#include "../../toxcore/tox.h"
#include "../../toxcore/tox_dispatch.h"
#include "../../toxcore/tox_events.h"
#include "fuzz_support.h"
#include "fuzz_tox.h"
namespace {
void setup_callbacks(Tox_Dispatch *dispatch)
{
tox_events_callback_conference_connected(
dispatch, [](Tox *tox, const Tox_Event_Conference_Connected *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_connected(
dispatch, [](Tox *tox, const Tox_Event_Conference_Connected *event, void *user_data) {
assert(event == nullptr);
});
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);
});
tox_events_callback_conference_message(
dispatch, [](Tox *tox, const Tox_Event_Conference_Message *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_peer_list_changed(dispatch,
[](Tox *tox, const Tox_Event_Conference_Peer_List_Changed *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_peer_name(
dispatch, [](Tox *tox, const Tox_Event_Conference_Peer_Name *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_title(
dispatch, [](Tox *tox, const Tox_Event_Conference_Title *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_file_chunk_request(
dispatch, [](Tox *tox, const Tox_Event_File_Chunk_Request *event, void *user_data) {
assert(event == nullptr);
});
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);
});
tox_events_callback_file_recv_chunk(
dispatch, [](Tox *tox, const Tox_Event_File_Recv_Chunk *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_file_recv_control(
dispatch, [](Tox *tox, const Tox_Event_File_Recv_Control *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_friend_connection_status(
dispatch, [](Tox *tox, const Tox_Event_Friend_Connection_Status *event, void *user_data) {
// OK: friend came online.
const uint32_t friend_number
= tox_event_friend_connection_status_get_friend_number(event);
assert(friend_number != UINT32_MAX);
});
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);
});
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);
});
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);
const uint32_t message_length = tox_event_friend_message_get_message_length(event);
const uint8_t *message = tox_event_friend_message_get_message(event);
tox_friend_send_message(tox, friend_number, type, message, message_length, nullptr);
});
tox_events_callback_friend_name(
dispatch, [](Tox *tox, const Tox_Event_Friend_Name *event, void *user_data) {
// OK: friend name received.
});
tox_events_callback_friend_read_receipt(
dispatch, [](Tox *tox, const Tox_Event_Friend_Read_Receipt *event, void *user_data) {
// OK: message has been received.
});
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.
});
tox_events_callback_friend_status_message(
dispatch, [](Tox *tox, const Tox_Event_Friend_Status_Message *event, void *user_data) {
// OK: friend status message received.
});
tox_events_callback_friend_typing(
dispatch, [](Tox *tox, const Tox_Event_Friend_Typing *event, void *user_data) {
// OK: friend may be typing.
});
tox_events_callback_self_connection_status(
dispatch, [](Tox *tox, const Tox_Event_Self_Connection_Status *event, void *user_data) {
// OK: we got connected.
});
}
void TestEndToEnd(Fuzz_Data &input)
{
Fuzz_System sys(input);
Ptr<Tox_Options> opts(tox_options_new(nullptr), tox_options_free);
assert(opts != nullptr);
tox_options_set_operating_system(opts.get(), sys.sys.get());
tox_options_set_local_discovery_enabled(opts.get(), false);
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 (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 *tox = tox_new(opts.get(), &error_new);
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);
tox_events_init(tox);
Tox_Dispatch *dispatch = tox_dispatch_new(nullptr);
assert(dispatch != nullptr);
setup_callbacks(dispatch);
while (input.size > 0) {
Tox_Err_Events_Iterate error_iterate;
Tox_Events *events = tox_events_iterate(tox, true, &error_iterate);
assert(tox_events_equal(events, events));
tox_dispatch_invoke(dispatch, events, tox, nullptr);
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()));
}
tox_dispatch_free(dispatch);
tox_kill(tox);
}
const std::vector<uint8_t> startup_data = [] {
constexpr char startup_file[] = "tools/toktok-fuzzer/init/e2e_fuzz_test.dat";
struct stat statbuf;
const int res = stat(startup_file, &statbuf);
assert(res == 0);
const int fd = open(startup_file, O_RDONLY);
assert(fd > 0);
std::vector<uint8_t> data(statbuf.st_size);
const ssize_t read_count = read(fd, data.data(), data.size());
assert(read_count > 0 && read_count == statbuf.st_size);
return data;
}();
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
std::vector<uint8_t> full_data(startup_data.size() + size);
std::copy(startup_data.begin(), startup_data.end(), full_data.begin());
std::copy(data, data + size, full_data.begin() + startup_data.size());
Fuzz_Data input{full_data.data(), full_data.size()};
TestEndToEnd(input);
return 0; // Non-zero return values are reserved for future use.
}

View File

@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 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

View File

@ -0,0 +1,368 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2021-2022 The TokTok team.
*/
#include "fuzz_support.h"
#include <arpa/inet.h>
#include <sys/socket.h>
#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_private.h"
#include "func_conversion.h"
const bool DEBUG = false;
// TODO(iphydf): Put this somewhere shared.
struct Network_Addr {
struct sockaddr_storage addr;
size_t size;
};
System::~System() { }
static int recv_common(Fuzz_Data &input, uint8_t *buf, size_t buf_len)
{
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;
if (fuzz_len == 0xffff) {
errno = EWOULDBLOCK;
if (DEBUG) {
std::printf("recvfrom: no data for tox1\n");
}
return -1;
}
if (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));
std::copy(input.data, input.data + res, buf);
input.data += res;
input.size -= res;
return res;
}
static constexpr Network_Funcs fuzz_network_funcs = {
/* .close = */ ![](Fuzz_System *self, int sock) { return 0; },
/* .accept = */ ![](Fuzz_System *self, int sock) { return 1337; },
/* .bind = */ ![](Fuzz_System *self, int sock, const Network_Addr *addr) { return 0; },
/* .listen = */ ![](Fuzz_System *self, int sock, int backlog) { return 0; },
/* .recvbuf = */
![](Fuzz_System *self, int sock) {
assert(sock == 42 || sock == 1337);
const size_t count = random_u16(self->rng.get());
return static_cast<int>(std::min(count, self->data.size));
},
/* .recv = */
![](Fuzz_System *self, int sock, uint8_t *buf, size_t len) {
assert(sock == 42 || sock == 1337);
// Receive data from the fuzzer.
return recv_common(self->data, buf, len);
},
/* .recvfrom = */
![](Fuzz_System *self, int sock, uint8_t *buf, size_t len, Network_Addr *addr) {
assert(sock == 42 || sock == 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 = */
![](Fuzz_System *self, int sock, const uint8_t *buf, size_t len) {
assert(sock == 42 || sock == 1337);
// Always succeed.
return static_cast<int>(len);
},
/* .sendto = */
![](Fuzz_System *self, int sock, const uint8_t *buf, size_t len, const Network_Addr *addr) {
assert(sock == 42 || sock == 1337);
// Always succeed.
return static_cast<int>(len);
},
/* .socket = */ ![](Fuzz_System *self, int domain, int type, int proto) { return 42; },
/* .socket_nonblock = */ ![](Fuzz_System *self, int sock, bool nonblock) { return 0; },
/* .getsockopt = */
![](Fuzz_System *self, int sock, int level, int optname, void *optval, size_t *optlen) {
std::memset(optval, 0, *optlen);
return 0;
},
/* .setsockopt = */
![](Fuzz_System *self, int sock, int level, int optname, const void *optval, size_t optlen) {
return 0;
},
};
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);
// 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);
}
},
/* .random_uniform = */
![](Fuzz_System *self, uint32_t upper_bound) {
uint32_t randnum = 0;
if (upper_bound > 0) {
self->rng->funcs->random_bytes(
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<Network>(Network{&fuzz_network_funcs, this}),
std::make_unique<Random>(Random{&fuzz_random_funcs, this}),
}
, data(input)
{
sys->mono_time_callback = ![](Fuzz_System *self) { return self->clock; };
sys->mono_time_user_data = this;
sys->ns = ns.get();
sys->rng = rng.get();
}
static constexpr Network_Funcs null_network_funcs = {
/* .close = */ ![](Null_System *self, int sock) { return 0; },
/* .accept = */ ![](Null_System *self, int sock) { return 1337; },
/* .bind = */ ![](Null_System *self, int sock, const Network_Addr *addr) { return 0; },
/* .listen = */ ![](Null_System *self, int sock, int backlog) { return 0; },
/* .recvbuf = */ ![](Null_System *self, int sock) { return 0; },
/* .recv = */
![](Null_System *self, int sock, uint8_t *buf, size_t len) {
// Always fail.
errno = ENOMEM;
return -1;
},
/* .recvfrom = */
![](Null_System *self, int sock, uint8_t *buf, size_t len, Network_Addr *addr) {
// Always fail.
errno = ENOMEM;
return -1;
},
/* .send = */
![](Null_System *self, int sock, const uint8_t *buf, size_t len) {
// Always succeed.
return static_cast<int>(len);
},
/* .sendto = */
![](Null_System *self, int sock, const uint8_t *buf, size_t len, const Network_Addr *addr) {
// Always succeed.
return static_cast<int>(len);
},
/* .socket = */ ![](Null_System *self, int domain, int type, int proto) { return 42; },
/* .socket_nonblock = */ ![](Null_System *self, int sock, bool nonblock) { return 0; },
/* .getsockopt = */
![](Null_System *self, int sock, int level, int optname, void *optval, size_t *optlen) {
std::memset(optval, 0, *optlen);
return 0;
},
/* .setsockopt = */
![](Null_System *self, int sock, int level, int optname, const void *optval, size_t optlen) {
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 Random_Funcs null_random_funcs = {
/* .random_bytes = */
![](Null_System *self, uint8_t *bytes, size_t length) {
for (size_t i = 0; i < length; ++i) {
bytes[i] = simple_rng(self->seed) & 0xff;
}
},
/* .random_uniform = */
![](Null_System *self, uint32_t upper_bound) {
return static_cast<uint32_t>(simple_rng(self->seed)) % upper_bound;
},
};
Null_System::Null_System()
: System{
std::make_unique<Tox_System>(),
std::make_unique<Network>(Network{&null_network_funcs, this}),
std::make_unique<Random>(Random{&null_random_funcs, this}),
}
{
sys->mono_time_callback = ![](Fuzz_System *self) { return self->clock; };
sys->mono_time_user_data = this;
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 Network_Funcs record_network_funcs = {
/* .close = */ ![](Record_System *self, int sock) { return 0; },
/* .accept = */ ![](Record_System *self, int sock) { return 2; },
/* .bind = */
![](Record_System *self, int sock, const Network_Addr *addr) {
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 = */ ![](Record_System *self, int sock, int backlog) { return 0; },
/* .recvbuf = */ ![](Record_System *self, int sock) { return 0; },
/* .recv = */
![](Record_System *self, int sock, uint8_t *buf, size_t len) {
// Always fail.
errno = ENOMEM;
return -1;
},
/* .recvfrom = */
![](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);
errno = EWOULDBLOCK;
if (DEBUG) {
std::printf("recvfrom: no data for %s\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->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->recording.insert(self->recording.end(), buf, buf + recvlen);
return static_cast<int>(recvlen);
},
/* .send = */
![](Record_System *self, int sock, const uint8_t *buf, size_t len) {
// Always succeed.
return static_cast<int>(len);
},
/* .sendto = */
![](Record_System *self, int sock, const uint8_t *buf, size_t len, const Network_Addr *addr) {
assert(sock == 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 = */ ![](Record_System *self, int domain, int type, int proto) { return 42; },
/* .socket_nonblock = */ ![](Record_System *self, int sock, bool nonblock) { return 0; },
/* .getsockopt = */
![](Record_System *self, int sock, int level, int optname, void *optval, size_t *optlen) {
std::memset(optval, 0, *optlen);
return 0;
},
/* .setsockopt = */
![](Record_System *self, int sock, int level, int optname, const void *optval, size_t optlen) {
return 0;
},
};
static constexpr Random_Funcs record_random_funcs = {
/* .random_bytes = */
![](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]);
}
if (DEBUG) {
std::printf(
"rng: %02x..%02x[%zu] -> %s\n", bytes[0], bytes[length - 1], length, self->name_);
}
},
/* .random_uniform = */
fuzz_random_funcs.random_uniform,
};
Record_System::Record_System(Global &global, uint64_t seed, const char *name)
: System{
std::make_unique<Tox_System>(),
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 = ![](Fuzz_System *self) { return self->clock; };
sys->mono_time_user_data = this;
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});
}

View File

@ -0,0 +1,230 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2021-2022 The TokTok team.
*/
#ifndef C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H
#define C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H
#include <cstdint>
#include <cstdlib>
#include <deque>
#include <memory>
#include <vector>
#include <unordered_map>
#include <utility>
#include "../../toxcore/tox.h"
struct Fuzz_Data {
const uint8_t *data;
std::size_t size;
Fuzz_Data(const uint8_t *input_data, std::size_t input_size)
: data(input_data), size(input_size)
{}
Fuzz_Data &operator=(const Fuzz_Data &rhs) = delete;
Fuzz_Data(const Fuzz_Data &rhs) = delete;
uint8_t consume1()
{
const uint8_t val = data[0];
++data;
--size;
return val;
}
const uint8_t *consume(std::size_t count)
{
const uint8_t *val = data;
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(DECL, INPUT) \
if (INPUT.size < 1) { \
return; \
} \
DECL = INPUT.consume1()
/** @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(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.
}
template <typename Arg, typename... Args>
void fuzz_select_target(uint8_t selector, Fuzz_Data &input, Arg &&fn, Args &&... args)
{
if (selector == sizeof...(Args)) {
return fn(input);
}
return fuzz_select_target(selector - 1, input, std::forward<Args>(args)...);
}
template <typename... Args>
void fuzz_select_target(const uint8_t *data, std::size_t size, Args &&... args)
{
Fuzz_Data input{data, size};
CONSUME1_OR_RETURN(uint8_t selector, input);
return fuzz_select_target(selector, input, std::forward<Args>(args)...);
}
struct Network;
struct Random;
struct System {
std::unique_ptr<Tox_System> sys;
std::unique_ptr<Network> ns;
std::unique_ptr<Random> rng;
// 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
* 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();
};
/**
* 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 {
/** @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.
*/
std::unordered_map<uint16_t, 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.
std::vector<uint8_t> recording;
explicit Record_System(Global &global, uint64_t seed, const char *name);
/** @brief Deposit a network packet in this instance's recvq.
*/
void receive(uint16_t send_port, const uint8_t *buf, size_t len);
};
/** @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 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

View File

@ -0,0 +1,95 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#ifndef C_TOXCORE_TESTING_FUZZING_FUZZ_TOX_H
#define C_TOXCORE_TESTING_FUZZING_FUZZ_TOX_H
#include <cassert>
#include <memory>
#include "../../toxcore/DHT.h"
#include "../../toxcore/logger.h"
#include "../../toxcore/network.h"
#include "fuzz_support.h"
constexpr uint16_t SIZE_IP_PORT = SIZE_IP6 + sizeof(uint16_t);
template <typename T>
using Ptr = std::unique_ptr<T, void (*)(T *)>;
/** @brief Construct any Tox resource using fuzzer input data.
*
* Constructs (or fails by returning) a valid object of type T and passes it to
* a function specified on the rhs of `>>`. Takes care of cleaning up the
* resource after the specified function returns.
*
* Some `with` instances require additional inputs such as the `Fuzz_Data`
* reference or a logger.
*/
template <typename T>
struct with;
/** @brief Construct a Logger without logging callback.
*/
template <>
struct with<Logger> {
template <typename F>
void operator>>(F &&f)
{
Ptr<Logger> logger(logger_new(), logger_kill);
assert(logger != nullptr);
f(std::move(logger));
}
};
/** @brief Construct an IP_Port by unpacking fuzzer input with `unpack_ip_port`.
*/
template <>
struct with<IP_Port> {
Fuzz_Data &input_;
template <typename F>
void operator>>(F &&f)
{
CONSUME_OR_RETURN(const uint8_t *ipp_packed, input_, SIZE_IP_PORT);
IP_Port ipp;
unpack_ip_port(&ipp, ipp_packed, SIZE_IP6, true);
f(ipp);
}
};
/** @brief Construct a Networking_Core object using the Network vtable passed.
*
* Use `with<Logger>{} >> with<Networking_Core>{input, ns} >> ...` to construct
* a logger and pass it to the Networking_Core constructor function.
*/
template <>
struct with<Networking_Core> {
Fuzz_Data &input_;
const Network *ns_;
Ptr<Logger> logger_{nullptr, logger_kill};
friend with operator>>(with<Logger> f, with self)
{
f >> [&self](Ptr<Logger> logger) { self.logger_ = std::move(logger); };
return self;
}
template <typename F>
void operator>>(F &&f)
{
with<IP_Port>{input_} >> [&f, this](const IP_Port &ipp) {
Ptr<Networking_Core> net(
new_networking_ex(logger_.get(), ns_, &ipp.ip, ipp.port, ipp.port + 100, nullptr),
kill_networking);
if (net == nullptr) {
return;
}
f(std::move(net));
};
}
};
#endif // C_TOXCORE_TESTING_FUZZING_FUZZ_TOX_H

View File

@ -0,0 +1,315 @@
/** @file
* @brief Generates a valid input for e2e_fuzz_test.
*
* This bootstraps 2 toxes tox1 and tox2, adds tox1 as tox2's friend, waits for
* the friend request, then tox1 adds tox2 in response, waits for the friend to
* come online, sends a 2-message exchange, and waits for the read receipt.
*
* All random inputs and network traffic is recorded and dumped to files at the
* end. This can then be fed to e2e_fuzz_test for replay (only of tox1) and
* further code path exploration using fuzzer mutations.
*
* We write 2 files: an init file that contains all the inputs needed to reach
* the "friend online" state, and a smaller file containing things to mutate
* once the tox instance is in that state. This allows for more specific
* exploration of code paths starting from "friend is online". DHT and onion
* packet parsing is explored more in bootstrap_fuzz_test.
*
* Usage:
*
* bazel build //c-toxcore/testing/fuzzing:protodump_bin && \
* bazel-bin/c-toxcore/testing/fuzzing/protodump_bin
*/
#include <cassert>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <memory>
#include "../../toxcore/tox.h"
#include "../../toxcore/tox_dispatch.h"
#include "../../toxcore/tox_events.h"
#include "../../toxcore/tox_private.h"
#include "../../toxcore/tox_struct.h"
#include "../../toxcore/util.h"
#include "fuzz_support.h"
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.
*/
constexpr uint32_t MESSAGE_COUNT = 5;
void setup_callbacks(Tox_Dispatch *dispatch)
{
tox_events_callback_conference_connected(
dispatch, [](Tox *tox, const Tox_Event_Conference_Connected *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_connected(
dispatch, [](Tox *tox, const Tox_Event_Conference_Connected *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_invite(
dispatch, [](Tox *tox, const Tox_Event_Conference_Invite *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_message(
dispatch, [](Tox *tox, const Tox_Event_Conference_Message *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_peer_list_changed(dispatch,
[](Tox *tox, const Tox_Event_Conference_Peer_List_Changed *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_peer_name(
dispatch, [](Tox *tox, const Tox_Event_Conference_Peer_Name *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_title(
dispatch, [](Tox *tox, const Tox_Event_Conference_Title *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_file_chunk_request(
dispatch, [](Tox *tox, const Tox_Event_File_Chunk_Request *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_file_recv(
dispatch, [](Tox *tox, const Tox_Event_File_Recv *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_file_recv_chunk(
dispatch, [](Tox *tox, const Tox_Event_File_Recv_Chunk *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_file_recv_control(
dispatch, [](Tox *tox, const Tox_Event_File_Recv_Control *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_friend_connection_status(
dispatch, [](Tox *tox, const Tox_Event_Friend_Connection_Status *event, void *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, [](Tox *tox, const Tox_Event_Friend_Lossless_Packet *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_friend_lossy_packet(
dispatch, [](Tox *tox, const Tox_Event_Friend_Lossy_Packet *event, void *user_data) {
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);
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, [](Tox *tox, 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, [](Tox *tox, 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, [](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) {
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) {
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) {
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) {
// OK: we got connected.
});
}
void dump(std::vector<uint8_t> &recording, const char *filename)
{
std::printf("%zu bytes: %s\n", recording.size(), filename);
std::ofstream(filename, std::ios::binary)
.write(reinterpret_cast<const char *>(recording.data()), recording.size());
recording.clear();
}
void RecordBootstrap()
{
Record_System::Global global;
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_Err_New error_new;
Record_System sys1(global, 4, "tox1"); // fair dice roll
tox_options_set_log_user_data(opts, &sys1);
tox_options_set_operating_system(opts, sys1.sys.get());
Tox *tox1 = tox_new(opts, &error_new);
assert(tox1 != nullptr);
assert(error_new == TOX_ERR_NEW_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());
Record_System sys2(global, 5, "tox2"); // unfair dice roll
tox_options_set_log_user_data(opts, &sys2);
tox_options_set_operating_system(opts, sys2.sys.get());
Tox *tox2 = tox_new(opts, &error_new);
assert(tox2 != nullptr);
assert(error_new == TOX_ERR_NEW_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);
const bool udp_success = tox_bootstrap(tox2, "127.0.0.2", port, 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);
uint32_t done1 = 0;
uint32_t done2 = 0;
const auto iterate = [&](uint8_t clock_increment) {
Tox_Err_Events_Iterate error_iterate;
Tox_Events *events;
events = tox_events_iterate(tox1, true, &error_iterate);
assert(tox_events_equal(events, events));
tox_dispatch_invoke(dispatch, events, tox1, &done1);
tox_events_free(events);
events = tox_events_iterate(tox2, true, &error_iterate);
assert(tox_events_equal(events, events));
tox_dispatch_invoke(dispatch, events, tox2, &done2);
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;
sys1.recording.push_back(clock_increment);
sys2.recording.push_back(clock_increment);
};
while (tox_self_get_connection_status(tox1) == TOX_CONNECTION_NONE
|| tox_self_get_connection_status(tox2) == TOX_CONNECTION_NONE) {
if (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);
}
std::printf("toxes are online\n");
const uint8_t msg = 'A';
const uint32_t friend_number = tox_friend_add(tox2, address1.data(), &msg, 1, nullptr);
assert(friend_number == 0);
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) {
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);
}
std::printf("tox clients connected\n");
dump(sys1.recording, "tools/toktok-fuzzer/init/e2e_fuzz_test.dat");
while (done1 < MESSAGE_COUNT && done2 < MESSAGE_COUNT) {
if (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);
}
std::printf("test complete\n");
tox_dispatch_free(dispatch);
tox_kill(tox2);
tox_kill(tox1);
dump(sys1.recording, "tools/toktok-fuzzer/corpus/e2e_fuzz_test/bootstrap.dat");
}
}
int main(void) { RecordBootstrap(); }

View File

@ -0,0 +1,200 @@
#include <cassert>
#include <cstdio>
#include "../../toxcore/tox.h"
#include "../../toxcore/tox_dispatch.h"
#include "../../toxcore/tox_events.h"
#include "fuzz_support.h"
#include "fuzz_tox.h"
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;
void setup_callbacks(Tox_Dispatch *dispatch)
{
tox_events_callback_conference_connected(
dispatch, [](Tox *tox, const Tox_Event_Conference_Connected *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_connected(
dispatch, [](Tox *tox, const Tox_Event_Conference_Connected *event, void *user_data) {
assert(event == nullptr);
});
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);
});
tox_events_callback_conference_message(
dispatch, [](Tox *tox, const Tox_Event_Conference_Message *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_peer_list_changed(dispatch,
[](Tox *tox, const Tox_Event_Conference_Peer_List_Changed *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_peer_name(
dispatch, [](Tox *tox, const Tox_Event_Conference_Peer_Name *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_conference_title(
dispatch, [](Tox *tox, const Tox_Event_Conference_Title *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_file_chunk_request(
dispatch, [](Tox *tox, const Tox_Event_File_Chunk_Request *event, void *user_data) {
assert(event == nullptr);
});
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);
});
tox_events_callback_file_recv_chunk(
dispatch, [](Tox *tox, const Tox_Event_File_Recv_Chunk *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_file_recv_control(
dispatch, [](Tox *tox, const Tox_Event_File_Recv_Control *event, void *user_data) {
assert(event == nullptr);
});
tox_events_callback_friend_connection_status(
dispatch, [](Tox *tox, const Tox_Event_Friend_Connection_Status *event, void *user_data) {
// OK: friend came online.
const uint32_t friend_number
= tox_event_friend_connection_status_get_friend_number(event);
assert(friend_number != UINT32_MAX);
});
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);
});
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);
});
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);
const uint32_t message_length = tox_event_friend_message_get_message_length(event);
const uint8_t *message = tox_event_friend_message_get_message(event);
tox_friend_send_message(tox, friend_number, type, message, message_length, nullptr);
});
tox_events_callback_friend_name(
dispatch, [](Tox *tox, const Tox_Event_Friend_Name *event, void *user_data) {
// OK: friend name received.
});
tox_events_callback_friend_read_receipt(
dispatch, [](Tox *tox, const Tox_Event_Friend_Read_Receipt *event, void *user_data) {
// OK: message has been received.
});
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.
});
tox_events_callback_friend_status_message(
dispatch, [](Tox *tox, const Tox_Event_Friend_Status_Message *event, void *user_data) {
// OK: friend status message received.
});
tox_events_callback_friend_typing(
dispatch, [](Tox *tox, const Tox_Event_Friend_Typing *event, void *user_data) {
// OK: friend may be typing.
});
tox_events_callback_self_connection_status(
dispatch, [](Tox *tox, const Tox_Event_Self_Connection_Status *event, void *user_data) {
// OK: we got connected.
});
}
void TestEndToEnd(Fuzz_Data &input)
{
Fuzz_System sys(input);
Ptr<Tox_Options> opts(tox_options_new(nullptr), tox_options_free);
assert(opts != nullptr);
tox_options_set_operating_system(opts.get(), sys.sys.get());
tox_options_set_local_discovery_enabled(opts.get(), false);
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 (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 *tox = tox_new(opts.get(), &error_new);
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);
tox_events_init(tox);
Tox_Dispatch *dispatch = tox_dispatch_new(nullptr);
assert(dispatch != nullptr);
setup_callbacks(dispatch);
while (input.size > 0) {
Tox_Err_Events_Iterate error_iterate;
Tox_Events *events = tox_events_iterate(tox, true, &error_iterate);
assert(tox_events_equal(events, events));
tox_dispatch_invoke(dispatch, events, tox, nullptr);
tox_events_free(events);
sys.clock += std::max(System::MIN_ITERATION_INTERVAL, random_u08(sys.rng.get()));
}
if (REDUCE_PROTODUMP) {
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));
}
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.
}

View File

@ -0,0 +1,52 @@
#include <cassert>
#include <cstdint>
#include <vector>
#include "../../toxcore/tox.h"
#include "../../toxcore/tox_private.h"
#include "fuzz_support.h"
namespace {
void TestSaveDataLoading(Fuzz_Data &input)
{
Tox_Err_Options_New error_options;
struct Tox_Options *tox_options = tox_options_new(&error_options);
assert(tox_options != nullptr);
assert(error_options == TOX_ERR_OPTIONS_NEW_OK);
const size_t savedata_size = input.size;
CONSUME_OR_RETURN(const uint8_t *savedata, input, savedata_size);
Null_System sys;
tox_options_set_operating_system(tox_options, sys.sys.get());
// pass test data to Tox
tox_options_set_savedata_data(tox_options, savedata, savedata_size);
tox_options_set_savedata_type(tox_options, TOX_SAVEDATA_TYPE_TOX_SAVE);
Tox *tox = tox_new(tox_options, nullptr);
tox_options_free(tox_options);
if (tox == nullptr) {
// Tox save was invalid, we're finished here
return;
}
// verify that the file can be saved again
std::vector<uint8_t> new_savedata(tox_get_savedata_size(tox));
tox_get_savedata(tox, new_savedata.data());
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};
TestSaveDataLoading(input);
return 0; // Non-zero return values are reserved for future use.
}