ctcp: cap CTCP body length used in dynamic signal name (IRC path)

The body of a CTCP message received over IRC (PRIVMSG/NOTICE) is
concatenated into a dynamic signal name and emitted via signal_emit
in ctcp_msg/ctcp_reply (ctcp.c) and the DCC sub-dispatch
ctcp_msg_dcc/ctcp_reply_dcc (dcc.c). The first whitespace-separated
token becomes the CTCP command and is interned as a signal name; the
remainder is delivered to any matching handler, including loaded
perl scripts.

signal_emit interns unknown signal names permanently in the global
signal-name registry (module_get_uniq_id_str g_strdup's the name into
two hash tables that are freed only at exit). With no length or count
cap on the IRC-CTCP path, a remote user sending many distinct CTCP
command names grows the registry without bound, exhausting memory and
crashing the victim's irssi.
This commit is contained in:
Acts1631 2026-07-05 21:01:21 -04:00
commit 68131a09be
2 changed files with 24 additions and 0 deletions

View file

@ -234,6 +234,15 @@ static void ctcp_msg(IRC_SERVER_REC *server, const char *data,
if (ignore_check(SERVER(server), nick, addr, target, data, MSGLEVEL_CTCPS))
return;
/* Cap the CTCP body length that is concatenated into the signal name.
* The body comes from a remote user over IRC (PRIVMSG) and is passed
* as the second argument to any matching signal handler, including
* loaded perl scripts. A modest cap limits the attack surface for the
* dynamic-signal-name dispatch and aligns with the convention used for
* CTCP-over-DCC-chat (dcc-chat.c). */
if (strlen(data) > 256)
return;
str = g_strconcat("ctcp msg ", data, NULL);
args = strchr(str+9, ' ');
if (args != NULL) *args++ = '\0'; else args = "";
@ -254,6 +263,11 @@ static void ctcp_reply(IRC_SERVER_REC *server, const char *data,
if (ignore_check(SERVER(server), nick, addr, target, data, MSGLEVEL_CTCPS))
return;
/* Cap the CTCP body length concatenated into the signal name; see
* ctcp_msg() for the rationale. */
if (strlen(data) > 256)
return;
str = g_strconcat("ctcp reply ", data, NULL);
args = strchr(str+11, ' ');
if (args != NULL) *args++ = '\0'; else args = "";

View file

@ -364,6 +364,11 @@ static void ctcp_msg_dcc(IRC_SERVER_REC *server, const char *data,
if (ignore_check(SERVER(server), nick, addr, target, data, MSGLEVEL_DCC))
return;
/* Cap the CTCP body length concatenated into the signal name; see
* ctcp_msg() in ctcp.c for the rationale. */
if (strlen(data) > 256)
return;
str = g_strconcat("ctcp msg dcc ", data, NULL);
args = strchr(str+13, ' ');
if (args != NULL) *args++ = '\0'; else args = "";
@ -386,6 +391,11 @@ static void ctcp_reply_dcc(IRC_SERVER_REC *server, const char *data,
if (ignore_check(SERVER(server), nick, addr, target, data, MSGLEVEL_DCC))
return;
/* Cap the CTCP body length concatenated into the signal name; see
* ctcp_msg() in ctcp.c for the rationale. */
if (strlen(data) > 256)
return;
str = g_strconcat("ctcp reply dcc ", data, NULL);
args = strchr(str+15, ' ');
if (args != NULL) *args++ = '\0'; else args = "";