]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] Fix critical TCP fuzzy protocol bugs
authorVsevolod Stakhov <vsevolod@rspamd.com>
Wed, 22 Oct 2025 09:30:00 +0000 (10:30 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Wed, 22 Oct 2025 10:17:48 +0000 (11:17 +0100)
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.

src/fuzzy_storage.c
src/plugins/fuzzy_check.c

index efd28dd47c54014d652da208becef8c0949ba407..67f2a1edbc1e85699be923b19e6bd9594d16f40c 100644 (file)
@@ -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));
index be27a0c9490a5e7d5b461dbff69abdce37d4ed54..2c10b412e6ef8a4f1ec8774e4b9a504f8b0fdfff 100644 (file)
@@ -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);