From: Vsevolod Stakhov Date: Wed, 22 Oct 2025 09:30:00 +0000 (+0100) Subject: [Fix] Fix critical TCP fuzzy protocol bugs X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5f75fd7ee3bffda01d1a17346f9acea5350710bb;p=thirdparty%2Frspamd.git [Fix] Fix critical TCP fuzzy protocol bugs This commit fixes three critical bugs in the TCP fuzzy implementation: 1. Heap-use-after-free in connection retry (fuzzy_check.c:782) - Removed redundant FUZZY_TCP_RELEASE() after g_ptr_array_remove() - The array's free function already handles unreferencing - This was causing double-free when retrying failed connections 2. TCP frame write calculation error (fuzzy_check.c:1088-1094) - Fixed data write length calculation that included 2-byte size header - Was writing 2 extra garbage bytes after payload - Server rejected frames with "invalid frame length" errors - Now correctly separates header and payload byte accounting 3. Server frame length validation (fuzzy_storage.c:2683) - Changed limit from sizeof(struct) to FUZZY_TCP_BUFFER_LENGTH (8192) - Commands with extensions exceed struct size but are valid - Added check for zero-length frames - Allows proper handling of variable-length fuzzy commands These fixes enable TCP fuzzy protocol to work correctly with parallel message processing and commands with extensions/shingles. --- diff --git a/src/fuzzy_storage.c b/src/fuzzy_storage.c index efd28dd47c..67f2a1edbc 100644 --- a/src/fuzzy_storage.c +++ b/src/fuzzy_storage.c @@ -2680,7 +2680,7 @@ rspamd_fuzzy_tcp_io(EV_P_ ev_io *w, int revents) /* Now we have full length */ frame_len = session->cur_frame_state & 0x3FFF; - if (frame_len > sizeof(struct rspamd_fuzzy_encrypted_shingle_cmd)) { + if (frame_len > FUZZY_TCP_BUFFER_LENGTH || frame_len == 0) { msg_err("invalid frame length %d from %s, closing connection", (int) frame_len, rspamd_inet_address_to_string(session->common.addr)); diff --git a/src/plugins/fuzzy_check.c b/src/plugins/fuzzy_check.c index be27a0c949..2c10b412e6 100644 --- a/src/plugins/fuzzy_check.c +++ b/src/plugins/fuzzy_check.c @@ -778,8 +778,8 @@ fuzzy_tcp_get_or_create_connection(struct fuzzy_rule *rule, time_since_failure, rspamd_upstream_name(upstream), rule->tcp_retry_delay); + /* g_ptr_array_remove automatically calls fuzzy_tcp_connection_unref via free_func */ g_ptr_array_remove(rule->tcp_connections, conn); - FUZZY_TCP_RELEASE(conn); /* Release reference held by array */ conn = NULL; } } @@ -1087,8 +1087,10 @@ fuzzy_tcp_write_handler(struct fuzzy_tcp_connection *conn) } else { /* Writing data */ - write_ptr = buf->data + (buf->bytes_written - sizeof(buf->size_hdr)); - remaining = buf->total_len - buf->bytes_written; + gsize data_offset = buf->bytes_written - sizeof(buf->size_hdr); + gsize data_len = buf->total_len - sizeof(buf->size_hdr); + write_ptr = buf->data + data_offset; + remaining = data_len - data_offset; } r = write(conn->fd, write_ptr, remaining);