Skip to content

Commit 4626c2e

Browse files
committed
test: Add a Net_Crypto fuzz test.
1 parent b4a0e61 commit 4626c2e

File tree

11 files changed

+214
-28
lines changed

11 files changed

+214
-28
lines changed

testing/fuzzing/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ fuzz_test(DHT ../../toxcore)
2626
fuzz_test(forwarding ../../toxcore)
2727
fuzz_test(group_announce ../../toxcore)
2828
fuzz_test(group_moderation ../../toxcore)
29+
fuzz_test(net_crypto ../../toxcore)
2930
fuzz_test(tox_events ../../toxcore)

testing/fuzzing/bootstrap_fuzz_test.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ void TestBootstrap(Fuzz_Data &input)
161161
assert(dispatch != nullptr);
162162
setup_callbacks(dispatch);
163163

164+
size_t input_size = input.size();
164165
while (!input.empty()) {
165166
Tox_Err_Events_Iterate error_iterate;
166167
Tox_Events *events = tox_events_iterate(tox, true, &error_iterate);
@@ -170,6 +171,11 @@ void TestBootstrap(Fuzz_Data &input)
170171
// Move the clock forward a decent amount so all the time-based checks
171172
// trigger more quickly.
172173
sys.clock += 200;
174+
175+
// If no input was consumed, something went wrong.
176+
assert(input_size != input.size());
177+
178+
input_size = input.size();
173179
}
174180

175181
tox_dispatch_free(dispatch);

testing/fuzzing/e2e_fuzz_test.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
#include <cassert>
77
#include <cstdio>
8-
#include <fstream>
98
#include <vector>
109

1110
#include "../../toxcore/crypto_core.h"

testing/fuzzing/fuzz_support.cc

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,39 @@ static constexpr Network_Funcs fuzz_network_funcs = {
175175
static constexpr Random_Funcs fuzz_random_funcs = {
176176
/* .random_bytes = */
177177
![](Fuzz_System *self, uint8_t *bytes, size_t length) {
178-
// Amount of data is limited
179-
const size_t bytes_read = std::min(length, self->data.size());
180-
// Initialize everything to make MSAN and others happy
181-
std::memset(bytes, 0, length);
182-
CONSUME_OR_ABORT(const uint8_t *data, self->data, bytes_read);
183-
std::copy(data, data + bytes_read, bytes);
178+
// Initialize the buffer with zeros in case there's no randomness left.
179+
std::fill_n(bytes, length, 0);
180+
181+
// For integers, we copy bytes directly, because we want to control the
182+
// exact values.
183+
if (length == sizeof(uint8_t) || length == sizeof(uint16_t) || length == sizeof(uint32_t)
184+
|| length == sizeof(uint64_t)) {
185+
CONSUME_OR_RETURN(const uint8_t *data, self->data, length);
186+
std::copy(data, data + length, bytes);
187+
if (Fuzz_Data::FUZZ_DEBUG) {
188+
if (length == 1) {
189+
std::printf("rng: %d (0x%02x)\n", bytes[0], bytes[0]);
190+
} else {
191+
std::printf("rng: %02x..%02x[%zu]\n", bytes[0], bytes[length - 1], length);
192+
}
193+
}
194+
return;
195+
}
196+
197+
// For nonces and keys, we fill the buffer with the same 1-2 bytes
198+
// repeated. We only need these to be different enough to not often be
199+
// the same.
200+
assert(length == 24 || length == 32);
201+
// We must cover the case of having only 1 byte left in the input. In
202+
// that case, we will use the same byte for all the bytes in the output.
203+
const size_t chunk_size = std::max(self->data.size(), static_cast<std::size_t>(2));
204+
CONSUME_OR_RETURN(const uint8_t *chunk, self->data, chunk_size);
205+
if (chunk_size == 2) {
206+
std::fill_n(bytes, length / 2, chunk[0]);
207+
std::fill_n(bytes + length / 2, length / 2, chunk[1]);
208+
} else {
209+
std::fill_n(bytes, length, chunk[0]);
210+
}
184211
if (Fuzz_Data::FUZZ_DEBUG) {
185212
if (length == 1) {
186213
std::printf("rng: %d (0x%02x)\n", bytes[0], bytes[0]);

testing/fuzzing/fuzz_support.hh

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55
#ifndef C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H
66
#define C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H
77

8+
#include <array>
9+
#include <cassert>
810
#include <cstdint>
911
#include <cstdio>
1012
#include <cstdlib>
1113
#include <cstring>
1214
#include <deque>
1315
#include <memory>
14-
#include <unordered_map>
1516
#include <utility>
1617
#include <vector>
1718

18-
#include "../../toxcore/tox.h"
1919
#include "../../toxcore/tox_private.h"
2020

2121
struct Fuzz_Data {
@@ -256,6 +256,38 @@ struct Null_System : System {
256256
Null_System();
257257
};
258258

259+
template <typename V>
260+
class int_map {
261+
public:
262+
struct iterator {
263+
std::pair<uint16_t, V> pair;
264+
265+
bool operator==(const iterator &rhs) const { return pair.first == rhs.pair.first; }
266+
bool operator!=(const iterator &rhs) const { return pair.first != rhs.pair.first; }
267+
268+
std::pair<uint16_t, V> operator*() const { return pair; }
269+
const std::pair<uint16_t, V> *operator->() const { return &pair; }
270+
};
271+
272+
int_map() = default;
273+
~int_map() = default;
274+
275+
iterator find(uint16_t key) const
276+
{
277+
if (!values[key]) {
278+
return end();
279+
}
280+
return {{key, values[key]}};
281+
}
282+
283+
iterator end() const { return {{static_cast<uint16_t>(values.size()), nullptr}}; }
284+
285+
void emplace(uint16_t key, V value) { values[key] = value; }
286+
287+
private:
288+
std::array<V, UINT16_MAX> values;
289+
};
290+
259291
/**
260292
* A Tox_System implementation that records all I/O but does not actually
261293
* perform any real I/O. Everything inside this system is hermetic in-process
@@ -280,7 +312,7 @@ struct Record_System : System {
280312
* toxcore sends packets to itself sometimes when doing onion routing
281313
* with only 2 nodes in the network.
282314
*/
283-
std::unordered_map<uint16_t, Record_System *> bound;
315+
int_map<Record_System *> bound;
284316
};
285317

286318
Global &global_;

testing/fuzzing/protodump.cc

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@
3131
#include "../../toxcore/tox_dispatch.h"
3232
#include "../../toxcore/tox_events.h"
3333
#include "../../toxcore/tox_private.h"
34-
#include "../../toxcore/tox_struct.h"
35-
#include "../../toxcore/util.h"
3634
#include "fuzz_support.hh"
3735

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

180178
void RecordBootstrap(const char *init, const char *bootstrap)
181179
{
182-
Record_System::Global global;
180+
auto global = std::make_unique<Record_System::Global>();
183181

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

201-
Record_System sys1(global, 4, "tox1"); // fair dice roll
202-
tox_options_set_log_user_data(opts, &sys1);
203-
tox_options_testing.operating_system = sys1.sys.get();
199+
auto sys1 = std::make_unique<Record_System>(*global, 4, "tox1"); // fair dice roll
200+
tox_options_set_log_user_data(opts, sys1.get());
201+
tox_options_testing.operating_system = sys1->sys.get();
204202
Tox *tox1 = tox_new_testing(opts, &error_new, &tox_options_testing, &error_new_testing);
205203
assert(tox1 != nullptr);
206204
assert(error_new == TOX_ERR_NEW_OK);
@@ -212,9 +210,9 @@ void RecordBootstrap(const char *init, const char *bootstrap)
212210
std::array<uint8_t, TOX_PUBLIC_KEY_SIZE> dht_key1;
213211
tox_self_get_dht_id(tox1, dht_key1.data());
214212

215-
Record_System sys2(global, 5, "tox2"); // unfair dice roll
216-
tox_options_set_log_user_data(opts, &sys2);
217-
tox_options_testing.operating_system = sys2.sys.get();
213+
auto sys2 = std::make_unique<Record_System>(*global, 5, "tox2"); // unfair dice roll
214+
tox_options_set_log_user_data(opts, sys2.get());
215+
tox_options_testing.operating_system = sys2->sys.get();
218216
Tox *tox2 = tox_new_testing(opts, &error_new, &tox_options_testing, &error_new_testing);
219217
assert(tox2 != nullptr);
220218
assert(error_new == TOX_ERR_NEW_OK);
@@ -252,26 +250,26 @@ void RecordBootstrap(const char *init, const char *bootstrap)
252250
Tox_Events *events;
253251

254252
events = tox_events_iterate(tox1, true, &error_iterate);
255-
assert(tox_events_equal(sys1.sys.get(), events, events));
253+
assert(tox_events_equal(sys1->sys.get(), events, events));
256254
tox_dispatch_invoke(dispatch, events, &state1);
257255
tox_events_free(events);
258256

259257
events = tox_events_iterate(tox2, true, &error_iterate);
260-
assert(tox_events_equal(sys2.sys.get(), events, events));
258+
assert(tox_events_equal(sys2->sys.get(), events, events));
261259
tox_dispatch_invoke(dispatch, events, &state2);
262260
tox_events_free(events);
263261

264262
// Move the clock forward a decent amount so all the time-based checks
265263
// trigger more quickly.
266-
sys1.clock += clock_increment;
267-
sys2.clock += clock_increment;
264+
sys1->clock += clock_increment;
265+
sys2->clock += clock_increment;
268266

269267
if (Fuzz_Data::FUZZ_DEBUG) {
270268
printf("tox1: rng: %d (for clock)\n", clock_increment);
271269
printf("tox2: rng: %d (for clock)\n", clock_increment);
272270
}
273-
sys1.push(clock_increment);
274-
sys2.push(clock_increment);
271+
sys1->push(clock_increment);
272+
sys2->push(clock_increment);
275273
};
276274

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

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

305-
dump(sys1.take_recording(), init);
303+
dump(sys1->take_recording(), init);
306304

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

323-
dump(sys1.recording(), bootstrap);
321+
dump(sys1->recording(), bootstrap);
324322
}
325323

326324
}

testing/fuzzing/rebuild_protodump

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#!/bin/sh
22

3-
set -eux
3+
set -eux -o pipefail
4+
5+
WORKSPACE_ROOT=$(bazel info workspace)
6+
7+
cd "$WORKSPACE_ROOT"
48

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

toxcore/BUILD.bazel

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,23 @@ cc_library(
770770
],
771771
)
772772

773+
cc_fuzz_test(
774+
name = "net_crypto_fuzz_test",
775+
size = "small",
776+
testonly = True,
777+
srcs = ["net_crypto_fuzz_test.cc"],
778+
corpus = ["//tools/toktok-fuzzer/corpus:net_crypto_fuzz_test"],
779+
deps = [
780+
":DHT",
781+
":TCP_client",
782+
":mem_test_util",
783+
":net_crypto",
784+
":network",
785+
"//c-toxcore/testing/fuzzing:fuzz_support",
786+
"//c-toxcore/testing/fuzzing:fuzz_tox",
787+
],
788+
)
789+
773790
cc_library(
774791
name = "onion_announce",
775792
srcs = ["onion_announce.c"],

toxcore/net_crypto.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
#include "net_profile.h"
2121
#include "network.h"
2222

23+
#ifdef __cplusplus
24+
extern "C" {
25+
#endif
26+
2327
/*** Crypto payloads. */
2428

2529
/*** Ranges. */
@@ -422,4 +426,8 @@ void kill_net_crypto(Net_Crypto *c);
422426
non_null()
423427
const Net_Profile *nc_get_tcp_client_net_profile(const Net_Crypto *c);
424428

429+
#ifdef __cplusplus
430+
} /* extern "C" */
431+
#endif
432+
425433
#endif /* C_TOXCORE_TOXCORE_NET_CRYPTO_H */

toxcore/net_crypto_fuzz_test.cc

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#include "net_crypto.h"
2+
3+
#include <cassert>
4+
#include <cstring>
5+
#include <functional>
6+
#include <memory>
7+
#include <optional>
8+
9+
#include "../testing/fuzzing/fuzz_support.hh"
10+
#include "../testing/fuzzing/fuzz_tox.hh"
11+
#include "DHT.h"
12+
#include "TCP_client.h"
13+
#include "network.h"
14+
15+
namespace {
16+
17+
std::optional<std::tuple<IP_Port, uint8_t>> prepare(Fuzz_Data &input)
18+
{
19+
IP_Port ipp;
20+
ip_init(&ipp.ip, true);
21+
ipp.port = 33445;
22+
23+
CONSUME_OR_RETURN_VAL(const uint8_t *iterations_packed, input, 1, std::nullopt);
24+
uint8_t iterations = *iterations_packed;
25+
26+
return {{ipp, iterations}};
27+
}
28+
29+
void TestNetCrypto(Fuzz_Data &input)
30+
{
31+
const auto prep = prepare(input);
32+
if (!prep.has_value()) {
33+
return;
34+
}
35+
const auto [ipp, iterations] = prep.value();
36+
37+
// rest of the fuzz data is input for malloc and network
38+
Fuzz_System sys(input);
39+
40+
const Ptr<Logger> logger(logger_new(sys.mem.get()), logger_kill);
41+
if (logger == nullptr) {
42+
return;
43+
}
44+
45+
const Ptr<Networking_Core> net(new_networking_ex(logger.get(), sys.mem.get(), sys.ns.get(),
46+
&ipp.ip, ipp.port, ipp.port + 100, nullptr),
47+
kill_networking);
48+
if (net == nullptr) {
49+
return;
50+
}
51+
52+
const std::unique_ptr<Mono_Time, std::function<void(Mono_Time *)>> mono_time(
53+
mono_time_new(
54+
sys.mem.get(), [](void *user_data) { return *static_cast<uint64_t *>(user_data); },
55+
&sys.clock),
56+
[mem = sys.mem.get()](Mono_Time *ptr) { mono_time_free(mem, ptr); });
57+
if (mono_time == nullptr) {
58+
return;
59+
}
60+
61+
const Ptr<DHT> dht(new_dht(logger.get(), sys.mem.get(), sys.rng.get(), sys.ns.get(),
62+
mono_time.get(), net.get(), false, false),
63+
kill_dht);
64+
if (dht == nullptr) {
65+
return;
66+
}
67+
68+
const TCP_Proxy_Info proxy_info = {0};
69+
70+
const Ptr<Net_Crypto> net_crypto(new_net_crypto(logger.get(), sys.mem.get(), sys.rng.get(),
71+
sys.ns.get(), mono_time.get(), dht.get(), &proxy_info),
72+
kill_net_crypto);
73+
if (net_crypto == nullptr) {
74+
return;
75+
}
76+
77+
for (uint8_t i = 0; i < iterations; ++i) {
78+
networking_poll(net.get(), nullptr);
79+
do_dht(dht.get());
80+
do_net_crypto(net_crypto.get(), nullptr);
81+
// "Sleep"
82+
sys.clock += System::BOOTSTRAP_ITERATION_INTERVAL;
83+
}
84+
}
85+
86+
} // namespace
87+
88+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
89+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
90+
{
91+
fuzz_select_target<TestNetCrypto>(data, size);
92+
return 0;
93+
}

0 commit comments

Comments
 (0)