Skip to content

test: Add a TCP server fuzz test. #2866

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
20 changes: 12 additions & 8 deletions testing/fuzzing/fuzz_support.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ static int recv_common(Fuzz_Data &input, uint8_t *buf, size_t buf_len)
if (fuzz_len == 0xffff) {
errno = EWOULDBLOCK;
if (Fuzz_Data::FUZZ_DEBUG) {
std::printf("recvfrom: no data for tox1\n");
std::fprintf(stderr, "recvfrom: no data for tox1 (errno=%d)\n", errno);
}
return -1;
}

if (Fuzz_Data::FUZZ_DEBUG) {
std::printf(
"recvfrom: %zu (%02x, %02x) for tox1\n", fuzz_len, input.data()[-2], input.data()[-1]);
std::fprintf(
stderr, "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()));

Expand Down Expand Up @@ -120,10 +120,14 @@ static constexpr Network_Funcs fuzz_network_funcs = {
/* .listen = */ ![](Fuzz_System *self, Socket sock, int backlog) { return 0; },
/* .connect = */ ![](Fuzz_System *self, Socket sock, const Network_Addr *addr) { return 0; },
/* .recvbuf = */
![](Fuzz_System *self, Socket sock) {
![](Fuzz_System *self, Socket sock, uint16_t length) {
assert(sock.value == 42 || sock.value == 1337);
const size_t count = random_u16(self->rng.get());
return static_cast<int>(std::min(count, self->data.size()));
const size_t count = std::max(length, random_u16(self->rng.get()));
const int bufsize = static_cast<int>(std::min(count, self->data.size()));
if (Fuzz_Data::FUZZ_DEBUG) {
std::printf("recvbuf: %d (length=%u, count=%zu, data->size=%zu)\n", bufsize, length, count, self->data.size());
}
return bufsize;
},
/* .recv = */
![](Fuzz_System *self, Socket sock, uint8_t *buf, size_t len) {
Expand Down Expand Up @@ -261,7 +265,7 @@ static constexpr Network_Funcs null_network_funcs = {
/* .bind = */ ![](Null_System *self, Socket sock, const Network_Addr *addr) { return 0; },
/* .listen = */ ![](Null_System *self, Socket sock, int backlog) { return 0; },
/* .connect = */ ![](Null_System *self, Socket sock, const Network_Addr *addr) { return 0; },
/* .recvbuf = */ ![](Null_System *self, Socket sock) { return 0; },
/* .recvbuf = */ ![](Null_System *self, Socket sock, uint16_t length) { return 0; },
/* .recv = */
![](Null_System *self, Socket sock, uint8_t *buf, size_t len) {
// Always fail.
Expand Down Expand Up @@ -378,7 +382,7 @@ static constexpr Network_Funcs record_network_funcs = {
},
/* .listen = */ ![](Record_System *self, Socket sock, int backlog) { return 0; },
/* .connect = */ ![](Record_System *self, Socket sock, const Network_Addr *addr) { return 0; },
/* .recvbuf = */ ![](Record_System *self, Socket sock) { return 0; },
/* .recvbuf = */ ![](Record_System *self, Socket sock, uint32_t length) { return 0; },
/* .recv = */
![](Record_System *self, Socket sock, uint8_t *buf, size_t len) {
// Always fail.
Expand Down
14 changes: 13 additions & 1 deletion toxcore/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,19 @@ cc_library(
],
)

cc_fuzz_test(
name = "TCP_server_fuzz_test",
size = "small",
testonly = True,
srcs = ["TCP_server_fuzz_test.cc"],
# corpus = ["//tools/toktok-fuzzer/corpus:TCP_server_fuzz_test"],
deps = [
":TCP_server",
"//c-toxcore/testing/fuzzing:fuzz_support",
"//c-toxcore/testing/fuzzing:fuzz_tox",
],
)

cc_library(
name = "TCP_client",
srcs = ["TCP_client.c"],
Expand Down Expand Up @@ -779,7 +792,6 @@ cc_fuzz_test(
deps = [
":DHT",
":TCP_client",
":mem_test_util",
":net_crypto",
":network",
"//c-toxcore/testing/fuzzing:fuzz_support",
Expand Down
2 changes: 1 addition & 1 deletion toxcore/TCP_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ static int proxy_http_read_connection_response(const Logger *logger, const TCP_C

if (strstr((const char *)data, success) != nullptr) {
// drain all data
uint16_t data_left = net_socket_data_recv_buffer(tcp_conn->con.ns, tcp_conn->con.sock);
uint16_t data_left = net_socket_data_recv_buffer(tcp_conn->con.ns, tcp_conn->con.sock, 0);

while (data_left > 0) {
uint8_t temp_data[16];
Expand Down
20 changes: 12 additions & 8 deletions toxcore/TCP_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,21 +207,25 @@ int write_packet_tcp_secure_connection(const Logger *logger, TCP_Connection *con
int read_tcp_packet(
const Logger *logger, const Memory *mem, const Network *ns, Socket sock, uint8_t *data, uint16_t length, const IP_Port *ip_port)
{
const uint16_t count = net_socket_data_recv_buffer(ns, sock);
const uint16_t count = net_socket_data_recv_buffer(ns, sock, length);

if (count == 0) {
// This is a very common case. Don't log here to avoid spamming the logs.
return -1;
}

if (count < length) {
if (count != 0) {
// Only log when there are some bytes available, as empty buffer
// is a very common case and this spams our logs.
LOGGER_TRACE(logger, "recv buffer has %d bytes, but requested %d bytes", count, length);
}
LOGGER_TRACE(logger, "recv buffer has %d bytes, but requested %d bytes", count, length);
return -1;
}

LOGGER_INFO(logger, "recv buffer has %d bytes", count);

const int len = net_recv(ns, logger, sock, data, length, ip_port);

if (len != length) {
LOGGER_ERROR(logger, "FAIL recv packet");
Net_Strerror error_str;
LOGGER_ERROR(logger, "FAIL recv packet: %d != %d (%s)", len, length, net_strerror(net_error(), &error_str));
return -1;
}

Expand All @@ -238,7 +242,7 @@ int read_tcp_packet(
non_null()
static uint16_t read_tcp_length(const Logger *logger, const Network *ns, Socket sock, const IP_Port *ip_port)
{
const uint16_t count = net_socket_data_recv_buffer(ns, sock);
const uint16_t count = net_socket_data_recv_buffer(ns, sock, sizeof(uint16_t));

if (count >= sizeof(uint16_t)) {
uint8_t length_buf[sizeof(uint16_t)];
Expand Down
4 changes: 4 additions & 0 deletions toxcore/TCP_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
#include <sys/ioctl.h>
#endif /* !WIN32 */

#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(TCP_SERVER_USE_EPOLL)
#undef TCP_SERVER_USE_EPOLL
#endif

#ifdef TCP_SERVER_USE_EPOLL
#include <sys/epoll.h>
#include <unistd.h>
Expand Down
8 changes: 8 additions & 0 deletions toxcore/TCP_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
#include "network.h"
#include "onion.h"

#ifdef __cplusplus
extern "C" {
#endif

#define MAX_INCOMING_CONNECTIONS 256

#define TCP_MAX_BACKLOG MAX_INCOMING_CONNECTIONS
Expand Down Expand Up @@ -60,4 +64,8 @@ void kill_tcp_server(TCP_Server *tcp_server);
nullable(1)
const Net_Profile *tcp_server_get_net_profile(const TCP_Server *tcp_server);

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

#endif /* C_TOXCORE_TOXCORE_TCP_SERVER_H */
95 changes: 95 additions & 0 deletions toxcore/TCP_server_fuzz_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include "TCP_server.h"

#include <stdio.h>

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

#include "../testing/fuzzing/fuzz_support.hh"
#include "../testing/fuzzing/fuzz_tox.hh"
#include "c-toxcore/toxcore/logger.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);
const uint8_t iterations = *iterations_packed;

return {{ipp, iterations}};
}

void TestTcpServer(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 network
Null_System null_sys;
Fuzz_System sys(input);

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

#if 1
logger_callback_log(
logger.get(),
[](void *context, Logger_Level level, const char *file, uint32_t line, const char *func,
const char *message, void *userdata) {
if (level <= LOGGER_LEVEL_DEBUG) {
return;
}
fprintf(stderr, "[%s] %s\n", logger_level_to_string(level), message);
},
nullptr, nullptr);
#endif

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 uint8_t secret_key[CRYPTO_SECRET_KEY_SIZE] = {0};
const Ptr<TCP_Server> tcp_server(
new_tcp_server(logger.get(), null_sys.mem.get(), sys.rng.get(), sys.ns.get(), false, 1,
&ipp.port, secret_key, nullptr, nullptr),
kill_tcp_server);
if (tcp_server == nullptr) {
abort();
}

for (uint8_t i = 0; i < iterations; ++i) {
do_tcp_server(tcp_server.get(), mono_time.get());
// "Sleep"
sys.clock += System::BOOTSTRAP_ITERATION_INTERVAL;

if (input.empty()) {
break;
}
}
}

} // 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<TestTcpServer>(data, size);
return 0;
}
18 changes: 18 additions & 0 deletions toxcore/logger.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@
#include "ccompat.h"
#include "mem.h"

const char *logger_level_to_string(Logger_Level level)
{
switch (level) {
case LOGGER_LEVEL_TRACE:
return "LOGGER_LEVEL_TRACE";
case LOGGER_LEVEL_DEBUG:
return "LOGGER_LEVEL_DEBUG";
case LOGGER_LEVEL_INFO:
return "LOGGER_LEVEL_INFO";
case LOGGER_LEVEL_WARNING:
return "LOGGER_LEVEL_WARNING";
case LOGGER_LEVEL_ERROR:
return "LOGGER_LEVEL_ERROR";
}

return "<invalid Logger_Level>";
}

struct Logger {
const Memory *mem;

Expand Down
2 changes: 2 additions & 0 deletions toxcore/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ typedef enum Logger_Level {
LOGGER_LEVEL_ERROR,
} Logger_Level;

const char *logger_level_to_string(Logger_Level level);

typedef struct Logger Logger;

typedef void logger_cb(void *context, Logger_Level level, const char *file, uint32_t line,
Expand Down
8 changes: 5 additions & 3 deletions toxcore/network.c
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ static int sys_connect(void *obj, Socket sock, const Network_Addr *addr)
}

non_null()
static int sys_recvbuf(void *obj, Socket sock)
static int sys_recvbuf(void *obj, Socket sock, uint16_t length)
{
#ifdef OS_WIN32
u_long count = 0;
Expand Down Expand Up @@ -933,7 +933,9 @@ int net_recv(const Network *ns, const Logger *log,
Socket sock, uint8_t *buf, size_t len, const IP_Port *ip_port)
{
const int res = ns->funcs->recv(ns->obj, sock, buf, len);
fprintf(stderr, "net_recv1: %d %d\n", res, errno);
loglogdata(log, "=>T", buf, len, ip_port, res);
fprintf(stderr, "net_recv2: %d %d\n", res, errno);
return res;
}

Expand Down Expand Up @@ -2256,9 +2258,9 @@ Socket net_socket(const Network *ns, Family domain, int type, int protocol)
return ns->funcs->socket(ns->obj, platform_domain, platform_type, platform_prot);
}

uint16_t net_socket_data_recv_buffer(const Network *ns, Socket sock)
uint16_t net_socket_data_recv_buffer(const Network *ns, Socket sock, uint16_t length)
{
const int count = ns->funcs->recvbuf(ns->obj, sock);
const int count = ns->funcs->recvbuf(ns->obj, sock, length);
return (uint16_t)max_s32(0, min_s32(count, UINT16_MAX));
}

Expand Down
12 changes: 8 additions & 4 deletions toxcore/network.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ typedef Socket net_accept_cb(void *obj, Socket sock);
typedef int net_bind_cb(void *obj, Socket sock, const Network_Addr *addr);
typedef int net_listen_cb(void *obj, Socket sock, int backlog);
typedef int net_connect_cb(void *obj, Socket sock, const Network_Addr *addr);
typedef int net_recvbuf_cb(void *obj, Socket sock);
typedef int net_recvbuf_cb(void *obj, Socket sock, uint16_t length);
typedef int net_recv_cb(void *obj, Socket sock, uint8_t *buf, size_t len);
typedef int net_recvfrom_cb(void *obj, Socket sock, uint8_t *buf, size_t len, Network_Addr *addr);
typedef int net_send_cb(void *obj, Socket sock, const uint8_t *buf, size_t len);
Expand Down Expand Up @@ -272,11 +272,15 @@ non_null()
Socket net_accept(const Network *ns, Socket sock);

/**
* return the size of data in the tcp recv buffer.
* return 0 on failure.
* @param ns System network interface.
* @param sock Socket to check the recv buffer on.
* @param length Length hint: how much does the caller expect to find?
* This does nothing except in fuzzing mode where it helps guide packet sizes.
*
* @return the size of data in the tcp recv buffer or 0 on failure.
*/
non_null()
uint16_t net_socket_data_recv_buffer(const Network *ns, Socket sock);
uint16_t net_socket_data_recv_buffer(const Network *ns, Socket sock, uint16_t length);

/** Convert values between host and network byte order. */
uint32_t net_htonl(uint32_t hostlong);
Expand Down
Loading