From 1211510ded6935a1174eacd6a7c20c1c8db059a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Mon, 1 Jun 2026 21:53:03 +0200 Subject: [PATCH] irc: limit size of data received from the server to prevent memory exhaustion A malicious or compromised IRC server could send data with no end-of-line (or a flood of "005" messages), making WeeChat accumulate it in a buffer that grew without limit, until all memory was exhausted. The unterminated received message and the accumulated "005" (ISUPPORT) data are now bounded by IRC_SERVER_RECV_MSG_MAX_LENGTH and IRC_SERVER_ISUPPORT_MAX_LENGTH: extra data is ignored once the limit is reached. --- CHANGELOG.md | 1 + src/plugins/irc/irc-protocol.c | 27 ++++++++---- src/plugins/irc/irc-server.c | 8 ++++ src/plugins/irc/irc-server.h | 9 ++++ tests/unit/plugins/irc/test-irc-protocol.cpp | 38 +++++++++++++++++ tests/unit/plugins/irc/test-irc-server.cpp | 43 ++++++++++++++++++++ 6 files changed, 117 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bde8a6559..8c951fdb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ SPDX-License-Identifier: GPL-3.0-or-later - core: fix option weechat.look.color_real_white not applied when color is "white" on 16+ colors terminals ([#1742](https://github.com/weechat/weechat/issues/1742)) - irc: fix tag in message with list of names when joining a channel - fset: remove error displayed in core buffer when clicking with the mouse below the last option displayed +- irc: limit size of data received from the server to prevent memory exhaustion - relay: limit size of decompressed websocket frame with permessage-deflate to prevent memory exhaustion ([GHSA-v2v4-45wm-5cr3](https://github.com/weechat/weechat/security/advisories/GHSA-v2v4-45wm-5cr3)) - relay: fix timing attack on password authentication ([GHSA-vhv8-g2r9-cwcc](https://github.com/weechat/weechat/security/advisories/GHSA-vhv8-g2r9-cwcc)) - api, relay: fix timing attack on TOTP validation ([GHSA-vhv8-g2r9-cwcc](https://github.com/weechat/weechat/security/advisories/GHSA-vhv8-g2r9-cwcc)) diff --git a/src/plugins/irc/irc-protocol.c b/src/plugins/irc/irc-protocol.c index d9bb4acdc..698b5d476 100644 --- a/src/plugins/irc/irc-protocol.c +++ b/src/plugins/irc/irc-protocol.c @@ -4164,16 +4164,25 @@ IRC_PROTOCOL_CALLBACK(005) if (ctxt->server->isupport) { length_isupport = strlen (ctxt->server->isupport); - isupport2 = realloc (ctxt->server->isupport, - length_isupport + /* existing */ - 1 + /* space */ - length + /* new */ - 1); - if (isupport2) + /* + * limit the size of the accumulated ISUPPORT data: once the + * maximum is reached, ignore the extra data (protection against a + * server flooding "005" messages, which would consume all the + * memory) + */ + if (length_isupport + 1 + length < IRC_SERVER_ISUPPORT_MAX_LENGTH) { - ctxt->server->isupport = isupport2; - strcat (ctxt->server->isupport, " "); - strcat (ctxt->server->isupport, str_info); + isupport2 = realloc (ctxt->server->isupport, + length_isupport + /* existing */ + 1 + /* space */ + length + /* new */ + 1); + if (isupport2) + { + ctxt->server->isupport = isupport2; + strcat (ctxt->server->isupport, " "); + strcat (ctxt->server->isupport, str_info); + } } } else diff --git a/src/plugins/irc/irc-server.c b/src/plugins/irc/irc-server.c index 9cace67bc..844c1939a 100644 --- a/src/plugins/irc/irc-server.c +++ b/src/plugins/irc/irc-server.c @@ -3409,6 +3409,14 @@ irc_server_msgq_add_unterminated (struct t_irc_server *server, if (server->unterminated_message) { + /* + * limit the size of the unterminated message: once the maximum is + * reached, ignore the extra data (protection against a server sending + * a very long line without end-of-line, which would consume all the + * memory) + */ + if (strlen (server->unterminated_message) >= IRC_SERVER_RECV_MSG_MAX_LENGTH) + return; unterminated_message2 = realloc (server->unterminated_message, (strlen (server->unterminated_message) + diff --git a/src/plugins/irc/irc-server.h b/src/plugins/irc/irc-server.h index 611752062..9693c51a1 100644 --- a/src/plugins/irc/irc-server.h +++ b/src/plugins/irc/irc-server.h @@ -144,6 +144,15 @@ enum t_irc_server_option #define IRC_SERVER_MULTILINE_DEFAULT_MAX_BYTES 4096 #define IRC_SERVER_MULTILINE_DEFAULT_MAX_LINES 24 +/* + * maximum length of an unterminated message (a received line without + * end-of-line) and of the accumulated "005" (ISUPPORT) data; these limits + * protect against a server sending a huge amount of data without end-of-line + * (or a flood of "005" messages), which would consume all the memory + */ +#define IRC_SERVER_RECV_MSG_MAX_LENGTH (64 * 1024) +#define IRC_SERVER_ISUPPORT_MAX_LENGTH (64 * 1024) + /* casemapping (string comparisons for nicks/channels) */ enum t_irc_server_casemapping { diff --git a/tests/unit/plugins/irc/test-irc-protocol.cpp b/tests/unit/plugins/irc/test-irc-protocol.cpp index 01855ebdd..3721a0c73 100644 --- a/tests/unit/plugins/irc/test-irc-protocol.cpp +++ b/tests/unit/plugins/irc/test-irc-protocol.cpp @@ -3866,6 +3866,44 @@ TEST(IrcProtocolWithServer, 005_full) STRCMP_EQUAL(IRC_MSG_005 " " IRC_MSG_005, ptr_server->isupport); } +/* + * Test functions: + * irc_protocol_cb_005 (accumulated ISUPPORT is bounded) + */ + +TEST(IrcProtocolWithServer, 005_limit) +{ + char str_msg[4096], str_value[3500]; + size_t length1, length2; + int i; + + SRV_INIT; + + memset (str_value, 'X', sizeof (str_value) - 1); + str_value[sizeof (str_value) - 1] = '\0'; + snprintf (str_msg, sizeof (str_msg), + ":server 005 alice TEST=%s :are supported", str_value); + + /* flood the server with "005" messages */ + for (i = 0; i < 100; i++) + { + server_recv (str_msg); + } + CHECK(ptr_server->isupport); + length1 = strlen (ptr_server->isupport); + + /* the accumulated ISUPPORT data must be bounded */ + CHECK(length1 <= IRC_SERVER_ISUPPORT_MAX_LENGTH + sizeof (str_value)); + + /* receiving more "005" messages must not grow it any further */ + for (i = 0; i < 100; i++) + { + server_recv (str_msg); + } + length2 = strlen (ptr_server->isupport); + LONGS_EQUAL(length1, length2); +} + /* * Test functions: * irc_protocol_cb_005 (infos from server, multiple messages) diff --git a/tests/unit/plugins/irc/test-irc-server.cpp b/tests/unit/plugins/irc/test-irc-server.cpp index a6cec5cd8..446e9b46c 100644 --- a/tests/unit/plugins/irc/test-irc-server.cpp +++ b/tests/unit/plugins/irc/test-irc-server.cpp @@ -65,6 +65,49 @@ TEST(IrcServer, Valid) irc_server_free (server); } +/* + * Test functions: + * irc_server_msgq_add_unterminated (via irc_server_msgq_add_buffer) + * + * Check that data received without any end-of-line does not grow the + * unterminated message buffer without limit. + */ + +TEST(IrcServer, MsgqAddBufferLimit) +{ + struct t_irc_server *server; + char chunk[4097]; + int i; + size_t length1, length2; + + server = irc_server_alloc ("server_msgq"); + CHECK(server); + + memset (chunk, 'a', sizeof (chunk) - 1); + chunk[sizeof (chunk) - 1] = '\0'; + + /* feed a lot of data with no end-of-line */ + for (i = 0; i < 100; i++) + { + irc_server_msgq_add_buffer (server, chunk); + } + CHECK(server->unterminated_message); + length1 = strlen (server->unterminated_message); + + /* the buffer must be bounded (not ~400 KB) */ + CHECK(length1 <= IRC_SERVER_RECV_MSG_MAX_LENGTH + sizeof (chunk)); + + /* feeding more data must not grow the buffer any further */ + for (i = 0; i < 100; i++) + { + irc_server_msgq_add_buffer (server, chunk); + } + length2 = strlen (server->unterminated_message); + LONGS_EQUAL(length1, length2); + + irc_server_free (server); +} + /* * Test functions: * irc_server_search