Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Add a Net_Crypto fuzz test. #2863

Merged
merged 1 commit into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions testing/fuzzing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ fuzz_test(DHT ../../toxcore)
fuzz_test(forwarding ../../toxcore)
fuzz_test(group_announce ../../toxcore)
fuzz_test(group_moderation ../../toxcore)
fuzz_test(net_crypto ../../toxcore)
fuzz_test(tox_events ../../toxcore)
6 changes: 6 additions & 0 deletions testing/fuzzing/bootstrap_fuzz_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ void TestBootstrap(Fuzz_Data &input)
assert(dispatch != nullptr);
setup_callbacks(dispatch);

size_t input_size = input.size();
while (!input.empty()) {
Tox_Err_Events_Iterate error_iterate;
Tox_Events *events = tox_events_iterate(tox, true, &error_iterate);
Expand All @@ -170,6 +171,11 @@ void TestBootstrap(Fuzz_Data &input)
// 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());

input_size = input.size();
}

tox_dispatch_free(dispatch);
Expand Down
1 change: 0 additions & 1 deletion testing/fuzzing/e2e_fuzz_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

#include <cassert>
#include <cstdio>
#include <fstream>
#include <vector>

#include "../../toxcore/crypto_core.h"
Expand Down
39 changes: 33 additions & 6 deletions testing/fuzzing/fuzz_support.cc
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,39 @@ static constexpr Network_Funcs fuzz_network_funcs = {
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);
CONSUME_OR_ABORT(const uint8_t *data, self->data, bytes_read);
std::copy(data, data + bytes_read, bytes);
// 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]);
Expand Down
38 changes: 35 additions & 3 deletions testing/fuzzing/fuzz_support.hh
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
#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 <unordered_map>
#include <utility>
#include <vector>

#include "../../toxcore/tox.h"
#include "../../toxcore/tox_private.h"

struct Fuzz_Data {
Expand Down Expand Up @@ -256,6 +256,38 @@ struct Null_System : System {
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
Expand All @@ -280,7 +312,7 @@ struct Record_System : System {
* 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;
int_map<Record_System *> bound;
};

Global &global_;
Expand Down
32 changes: 15 additions & 17 deletions testing/fuzzing/protodump.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@
#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.hh"

namespace {
Expand Down Expand Up @@ -179,7 +177,7 @@ void dump(std::vector<uint8_t> recording, const char *filename)

void RecordBootstrap(const char *init, const char *bootstrap)
{
Record_System::Global global;
auto global = std::make_unique<Record_System::Global>();

Tox_Options *opts = tox_options_new(nullptr);
assert(opts != nullptr);
Expand All @@ -198,9 +196,9 @@ void RecordBootstrap(const char *init, const char *bootstrap)
Tox_Err_New_Testing error_new_testing;
Tox_Options_Testing tox_options_testing;

Record_System sys1(global, 4, "tox1"); // fair dice roll
tox_options_set_log_user_data(opts, &sys1);
tox_options_testing.operating_system = sys1.sys.get();
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);
assert(tox1 != nullptr);
assert(error_new == TOX_ERR_NEW_OK);
Expand All @@ -212,9 +210,9 @@ void RecordBootstrap(const char *init, const char *bootstrap)
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_testing.operating_system = sys2.sys.get();
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);
assert(tox2 != nullptr);
assert(error_new == TOX_ERR_NEW_OK);
Expand Down Expand Up @@ -252,26 +250,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));
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));
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;
sys1->clock += clock_increment;
sys2->clock += 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);
sys1->push(clock_increment);
sys2->push(clock_increment);
};

while (tox_self_get_connection_status(tox1) == TOX_CONNECTION_NONE
Expand Down Expand Up @@ -302,7 +300,7 @@ void RecordBootstrap(const char *init, const char *bootstrap)

std::printf("tox clients connected\n");

dump(sys1.take_recording(), init);
dump(sys1->take_recording(), init);

while (state1.done < MESSAGE_COUNT && state2.done < MESSAGE_COUNT) {
if (Fuzz_Data::FUZZ_DEBUG) {
Expand All @@ -320,7 +318,7 @@ void RecordBootstrap(const char *init, const char *bootstrap)
tox_kill(tox2);
tox_kill(tox1);

dump(sys1.recording(), bootstrap);
dump(sys1->recording(), bootstrap);
}

}
Expand Down
6 changes: 5 additions & 1 deletion testing/fuzzing/rebuild_protodump
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#!/bin/sh

set -eux
set -eux -o pipefail

WORKSPACE_ROOT=$(bazel info workspace)

cd "$WORKSPACE_ROOT"

bazel test --config=asan-libfuzzer //c-toxcore/testing/fuzzing:protodump_reduce_test

Expand Down
17 changes: 17 additions & 0 deletions toxcore/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,23 @@ cc_library(
],
)

cc_fuzz_test(
name = "net_crypto_fuzz_test",
size = "small",
testonly = True,
srcs = ["net_crypto_fuzz_test.cc"],
corpus = ["//tools/toktok-fuzzer/corpus:net_crypto_fuzz_test"],
deps = [
":DHT",
":TCP_client",
":mem_test_util",
":net_crypto",
":network",
"//c-toxcore/testing/fuzzing:fuzz_support",
"//c-toxcore/testing/fuzzing:fuzz_tox",
],
)

cc_library(
name = "onion_announce",
srcs = ["onion_announce.c"],
Expand Down
8 changes: 8 additions & 0 deletions toxcore/net_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
#include "net_profile.h"
#include "network.h"

#ifdef __cplusplus
extern "C" {
#endif

/*** Crypto payloads. */

/*** Ranges. */
Expand Down Expand Up @@ -422,4 +426,8 @@ void kill_net_crypto(Net_Crypto *c);
non_null()
const Net_Profile *nc_get_tcp_client_net_profile(const Net_Crypto *c);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* C_TOXCORE_TOXCORE_NET_CRYPTO_H */
93 changes: 93 additions & 0 deletions toxcore/net_crypto_fuzz_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "net_crypto.h"

#include <cassert>
#include <cstring>
#include <functional>
#include <memory>
#include <optional>

#include "../testing/fuzzing/fuzz_support.hh"
#include "../testing/fuzzing/fuzz_tox.hh"
#include "DHT.h"
#include "TCP_client.h"
#include "network.h"

namespace {

std::optional<std::tuple<IP_Port, uint8_t>> prepare(Fuzz_Data &input)
{
IP_Port ipp;
ip_init(&ipp.ip, true);
ipp.port = 33445;

CONSUME_OR_RETURN_VAL(const uint8_t *iterations_packed, input, 1, std::nullopt);
uint8_t iterations = *iterations_packed;

return {{ipp, iterations}};
}

void TestNetCrypto(Fuzz_Data &input)
{
const auto prep = prepare(input);
if (!prep.has_value()) {
return;
}
const auto [ipp, iterations] = prep.value();

// rest of the fuzz data is input for malloc and network
Fuzz_System sys(input);

const Ptr<Logger> logger(logger_new(sys.mem.get()), logger_kill);
if (logger == nullptr) {
return;
}

const Ptr<Networking_Core> net(new_networking_ex(logger.get(), sys.mem.get(), sys.ns.get(),
&ipp.ip, ipp.port, ipp.port + 100, nullptr),
kill_networking);
if (net == nullptr) {
return;
}

const std::unique_ptr<Mono_Time, std::function<void(Mono_Time *)>> mono_time(
mono_time_new(
sys.mem.get(), [](void *user_data) { return *static_cast<uint64_t *>(user_data); },
&sys.clock),
[mem = sys.mem.get()](Mono_Time *ptr) { mono_time_free(mem, ptr); });
if (mono_time == nullptr) {
return;
}

const Ptr<DHT> dht(new_dht(logger.get(), sys.mem.get(), sys.rng.get(), sys.ns.get(),
mono_time.get(), net.get(), false, false),
kill_dht);
if (dht == nullptr) {
return;
}

const TCP_Proxy_Info proxy_info = {0};

const Ptr<Net_Crypto> net_crypto(new_net_crypto(logger.get(), sys.mem.get(), sys.rng.get(),
sys.ns.get(), mono_time.get(), dht.get(), &proxy_info),
kill_net_crypto);
if (net_crypto == nullptr) {
return;
}

for (uint8_t i = 0; i < iterations; ++i) {
networking_poll(net.get(), nullptr);
do_dht(dht.get());
do_net_crypto(net_crypto.get(), nullptr);
// "Sleep"
sys.clock += System::BOOTSTRAP_ITERATION_INTERVAL;
}
}

} // namespace

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
fuzz_select_target<TestNetCrypto>(data, size);
return 0;
}
Loading
Loading