test: Add fuzzer support functions for internal toxcore objects.

These help creating fuzzer fixtures with non-trivially constructed
objects and takes care of cleaning them up afterwards so the fuzzer code
can focus on the system under test.
This commit is contained in:
iphydf 2022-04-02 12:11:46 +00:00
parent c71b1218f8
commit dec1399776
No known key found for this signature in database
GPG Key ID: 3855DBA2D74403C9
6 changed files with 138 additions and 22 deletions

View File

@ -15,6 +15,13 @@ cc_library(
visibility = ["//c-toxcore:__subpackages__"], visibility = ["//c-toxcore:__subpackages__"],
) )
cc_library(
name = "fuzz_tox",
hdrs = ["fuzz_tox.h"],
visibility = ["//c-toxcore:__subpackages__"],
deps = [":fuzz_support"],
)
cc_fuzz_test( cc_fuzz_test(
name = "bootstrap_fuzz_test", name = "bootstrap_fuzz_test",
srcs = ["bootstrap_harness.cc"], srcs = ["bootstrap_harness.cc"],

View File

@ -1 +1,5 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2021-2022 The TokTok team.
*/
#include "fuzz_support.h" #include "fuzz_support.h"

View File

@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2021 The TokTok team. * Copyright © 2021-2022 The TokTok team.
*/ */
#ifndef C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H #ifndef C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H
@ -7,19 +7,29 @@
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <utility>
struct Fuzz_Data { struct Fuzz_Data {
const uint8_t *data; const uint8_t *data;
std::size_t size; std::size_t size;
uint8_t consume1() { 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]; const uint8_t val = data[0];
++data; ++data;
--size; --size;
return val; return val;
} }
const uint8_t *consume(std::size_t count) { const uint8_t *consume(std::size_t count)
{
const uint8_t *val = data; const uint8_t *val = data;
data += count; data += count;
size -= count; size -= count;
@ -38,9 +48,9 @@ struct Fuzz_Data {
* @endcode * @endcode
*/ */
#define CONSUME1_OR_RETURN(DECL, INPUT) \ #define CONSUME1_OR_RETURN(DECL, INPUT) \
if (INPUT.size < 1) { \ if (INPUT.size < 1) { \
return; \ return; \
} \ } \
DECL = INPUT.consume1() DECL = INPUT.consume1()
/** @brief Consumes SIZE bytes of the fuzzer input or returns if not enough data available. /** @brief Consumes SIZE bytes of the fuzzer input or returns if not enough data available.
@ -55,33 +65,33 @@ struct Fuzz_Data {
* @endcode * @endcode
*/ */
#define CONSUME_OR_RETURN(DECL, INPUT, SIZE) \ #define CONSUME_OR_RETURN(DECL, INPUT, SIZE) \
if (INPUT.size < SIZE) { \ if (INPUT.size < SIZE) { \
return; \ return; \
} \ } \
DECL = INPUT.consume(SIZE) DECL = INPUT.consume(SIZE)
inline void fuzz_select_target(uint8_t selector, Fuzz_Data input) inline void fuzz_select_target(uint8_t selector, Fuzz_Data &input)
{ {
// The selector selected no function, so we do nothing and rely on the // The selector selected no function, so we do nothing and rely on the
// fuzzer to come up with a better selector. // fuzzer to come up with a better selector.
} }
template<typename Arg, typename ...Args> template <typename Arg, typename... Args>
void fuzz_select_target(uint8_t selector, Fuzz_Data input, Arg fn, Args ...args) void fuzz_select_target(uint8_t selector, Fuzz_Data &input, Arg &&fn, Args &&... args)
{ {
if (selector == sizeof...(Args)) { if (selector == sizeof...(Args)) {
return fn(input); return fn(input);
} }
return fuzz_select_target(selector - 1, input, args...); return fuzz_select_target(selector - 1, input, std::forward<Args>(args)...);
} }
template<typename ...Args> template <typename... Args>
void fuzz_select_target(const uint8_t *data, std::size_t size, Args ...args) void fuzz_select_target(const uint8_t *data, std::size_t size, Args &&... args)
{ {
Fuzz_Data input{data, size}; Fuzz_Data input{data, size};
CONSUME1_OR_RETURN(uint8_t selector, input); CONSUME1_OR_RETURN(uint8_t selector, input);
return fuzz_select_target(selector, input, args...); return fuzz_select_target(selector, input, std::forward<Args>(args)...);
} }
#endif // C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H #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

@ -7,7 +7,7 @@
namespace { namespace {
void TestHandleRequest(Fuzz_Data input) void TestHandleRequest(Fuzz_Data &input)
{ {
CONSUME_OR_RETURN(const uint8_t *self_public_key, input, CRYPTO_PUBLIC_KEY_SIZE); CONSUME_OR_RETURN(const uint8_t *self_public_key, input, CRYPTO_PUBLIC_KEY_SIZE);
CONSUME_OR_RETURN(const uint8_t *self_secret_key, input, CRYPTO_SECRET_KEY_SIZE); CONSUME_OR_RETURN(const uint8_t *self_secret_key, input, CRYPTO_SECRET_KEY_SIZE);
@ -19,7 +19,7 @@ void TestHandleRequest(Fuzz_Data input)
self_public_key, self_secret_key, public_key, request, &request_id, input.data, input.size); self_public_key, self_secret_key, public_key, request, &request_id, input.data, input.size);
} }
void TestUnpackNodes(Fuzz_Data input) void TestUnpackNodes(Fuzz_Data &input)
{ {
CONSUME1_OR_RETURN(const bool tcp_enabled, input); CONSUME1_OR_RETURN(const bool tcp_enabled, input);

View File

@ -4,7 +4,7 @@
namespace { namespace {
void TestModListUnpack(Fuzz_Data input) void TestModListUnpack(Fuzz_Data &input)
{ {
CONSUME1_OR_RETURN(const uint16_t num_mods, input); CONSUME1_OR_RETURN(const uint16_t num_mods, input);
Moderation mods{}; Moderation mods{};
@ -12,7 +12,7 @@ void TestModListUnpack(Fuzz_Data input)
mod_list_cleanup(&mods); mod_list_cleanup(&mods);
} }
void TestSanctionsListUnpack(Fuzz_Data input) void TestSanctionsListUnpack(Fuzz_Data &input)
{ {
Mod_Sanction sanctions[10]; Mod_Sanction sanctions[10];
Mod_Sanction_Creds creds; Mod_Sanction_Creds creds;
@ -20,7 +20,7 @@ void TestSanctionsListUnpack(Fuzz_Data input)
sanctions_list_unpack(sanctions, &creds, 10, input.data, input.size, &processed_data_len); sanctions_list_unpack(sanctions, &creds, 10, input.data, input.size, &processed_data_len);
} }
void TestSanctionCredsUnpack(Fuzz_Data input) void TestSanctionCredsUnpack(Fuzz_Data &input)
{ {
CONSUME_OR_RETURN(const uint8_t *data, input, MOD_SANCTIONS_CREDS_SIZE); CONSUME_OR_RETURN(const uint8_t *data, input, MOD_SANCTIONS_CREDS_SIZE);
Mod_Sanction_Creds creds; Mod_Sanction_Creds creds;