diff --git a/testing/fuzzing/fuzz_support.cc b/testing/fuzzing/fuzz_support.cc index 904d542494..1110d3afaa 100644 --- a/testing/fuzzing/fuzz_support.cc +++ b/testing/fuzzing/fuzz_support.cc @@ -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())); @@ -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(std::min(count, self->data.size())); + const size_t count = std::max(length, random_u16(self->rng.get())); + const int bufsize = static_cast(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) { @@ -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. @@ -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. diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel index 1f2549fac7..abb1a3b5a3 100644 --- a/toxcore/BUILD.bazel +++ b/toxcore/BUILD.bazel @@ -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"], @@ -779,7 +792,6 @@ cc_fuzz_test( deps = [ ":DHT", ":TCP_client", - ":mem_test_util", ":net_crypto", ":network", "//c-toxcore/testing/fuzzing:fuzz_support", diff --git a/toxcore/TCP_client.c b/toxcore/TCP_client.c index 1982412cda..7e5417ef4e 100644 --- a/toxcore/TCP_client.c +++ b/toxcore/TCP_client.c @@ -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]; diff --git a/toxcore/TCP_common.c b/toxcore/TCP_common.c index 1a8a5d9731..5a15dfa7a0 100644 --- a/toxcore/TCP_common.c +++ b/toxcore/TCP_common.c @@ -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; } @@ -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)]; diff --git a/toxcore/TCP_server.c b/toxcore/TCP_server.c index 288cd94c48..8e797a8d14 100644 --- a/toxcore/TCP_server.c +++ b/toxcore/TCP_server.c @@ -13,6 +13,10 @@ #include #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 #include diff --git a/toxcore/TCP_server.h b/toxcore/TCP_server.h index bd4c98b0cd..273ee7fa07 100644 --- a/toxcore/TCP_server.h +++ b/toxcore/TCP_server.h @@ -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 @@ -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 */ diff --git a/toxcore/TCP_server_fuzz_test.cc b/toxcore/TCP_server_fuzz_test.cc new file mode 100644 index 0000000000..e733f73f03 --- /dev/null +++ b/toxcore/TCP_server_fuzz_test.cc @@ -0,0 +1,95 @@ +#include "TCP_server.h" + +#include + +#include +#include +#include +#include +#include + +#include "../testing/fuzzing/fuzz_support.hh" +#include "../testing/fuzzing/fuzz_tox.hh" +#include "c-toxcore/toxcore/logger.h" + +namespace { + +std::optional> 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_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( + mono_time_new( + sys.mem.get(), [](void *user_data) { return *static_cast(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( + 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(data, size); + return 0; +} diff --git a/toxcore/logger.c b/toxcore/logger.c index b97ef8e184..e36ef31f5c 100644 --- a/toxcore/logger.c +++ b/toxcore/logger.c @@ -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 ""; +} + struct Logger { const Memory *mem; diff --git a/toxcore/logger.h b/toxcore/logger.h index 893c21fd10..35fea1dd93 100644 --- a/toxcore/logger.h +++ b/toxcore/logger.h @@ -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, diff --git a/toxcore/network.c b/toxcore/network.c index 17d49ed162..8723cbb81c 100644 --- a/toxcore/network.c +++ b/toxcore/network.c @@ -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; @@ -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; } @@ -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)); } diff --git a/toxcore/network.h b/toxcore/network.h index a13c98bf6f..1f9483a75f 100644 --- a/toxcore/network.h +++ b/toxcore/network.h @@ -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); @@ -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);