From ff643366fc916865a827fa38fc73c80ff827bc20 Mon Sep 17 00:00:00 2001 From: MrLenin <909621+MrLenin@users.noreply.github.com> Date: Thu, 5 Feb 2026 20:22:32 -0500 Subject: [PATCH] fix: Off-by-one buffer errors in ircd_snprintf and ircd_strncpy calls ircd_snprintf(buf, size, ...) writes at most size-1 characters plus null. ircd_strncpy(dest, src, len) copies len-1 characters plus null. When a buffer is declared as buf[SIZE+1] to hold SIZE characters plus the null terminator, the copy function must be called with SIZE+1 as the length parameter, not SIZE. Fixed locations: - channel.c: find_ban() tmphost buffer for hidden host matching - m_userip.c: userip_formatter() iphost buffer (5 instances) - s_user.c: hide_hostmask() newhost buffer for account-based hiding - s_user.c: set_user_mode() account parsing with timestamped accounts The account parsing bug caused usernames like "ibutsu:1234567890" to be truncated to "ibuts" when received via P10 NICK burst with timestamp. Co-Authored-By: Claude Opus 4.5 --- ircd/channel.c | 2 +- ircd/m_userip.c | 10 +++++----- ircd/s_user.c | 10 ++++++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ircd/channel.c b/ircd/channel.c index 31e4cee0..194c09c6 100644 --- a/ircd/channel.c +++ b/ircd/channel.c @@ -424,7 +424,7 @@ struct Ban *find_ban(struct Client *cptr, struct Ban *banlist, int extbantype, i if (IsAccount(cptr) && ((feature_int(FEAT_HOST_HIDING_STYLE) == 1) || (feature_int(FEAT_HOST_HIDING_STYLE) == 3))) { - ircd_snprintf(0, tmphost, HOSTLEN, "%s.%s", + ircd_snprintf(0, tmphost, HOSTLEN + 1, "%s.%s", cli_user(cptr)->account, (feature_bool(FEAT_OPERHOST_HIDING) && IsAnOper(cptr) ? feature_str(FEAT_HIDDEN_OPERHOST) : feature_str(FEAT_HIDDEN_HOST))); diff --git a/ircd/m_userip.c b/ircd/m_userip.c index 98bb8066..95b65cf7 100644 --- a/ircd/m_userip.c +++ b/ircd/m_userip.c @@ -100,16 +100,16 @@ static void userip_formatter(struct Client* cptr, struct Client *sptr, struct Ms assert(IsUser(cptr)); if ((sptr == cptr) || IsAnOper(sptr) || !IsHiddenHost(cptr)) - ircd_snprintf(0, iphost, HOSTLEN, "%s", ircd_ntoa(&cli_ip(cptr))); + ircd_snprintf(0, iphost, HOSTLEN + 1, "%s", ircd_ntoa(&cli_ip(cptr))); else if (IsCloakIP(cptr)) - ircd_snprintf(0, iphost, HOSTLEN, "%s", cli_user(cptr)->cloakip); + ircd_snprintf(0, iphost, HOSTLEN + 1, "%s", cli_user(cptr)->cloakip); else if (IsFakeHost(cptr) || IsSetHost(cptr)) - ircd_snprintf(0, iphost, HOSTLEN, "%s", feature_str(FEAT_HIDDEN_IP)); + ircd_snprintf(0, iphost, HOSTLEN + 1, "%s", feature_str(FEAT_HIDDEN_IP)); else if (((feature_int(FEAT_HOST_HIDING_STYLE) == 1) || (feature_int(FEAT_HOST_HIDING_STYLE) == 3)) && IsAccount(cptr)) - ircd_snprintf(0, iphost, HOSTLEN, "%s", feature_str(FEAT_HIDDEN_IP)); + ircd_snprintf(0, iphost, HOSTLEN + 1, "%s", feature_str(FEAT_HIDDEN_IP)); else - ircd_snprintf(0, iphost, HOSTLEN, "%s", ircd_ntoa(&cli_ip(cptr))); + ircd_snprintf(0, iphost, HOSTLEN + 1, "%s", ircd_ntoa(&cli_ip(cptr))); msgq_append(0, mb, "%s%s=%c%s@%s", cli_name(cptr), SeeOper(sptr,cptr) ? "*" : "", diff --git a/ircd/s_user.c b/ircd/s_user.c index bc9513ca..b4afb9e4 100644 --- a/ircd/s_user.c +++ b/ircd/s_user.c @@ -1161,10 +1161,10 @@ hide_hostmask(struct Client *cptr) } else if ((feature_int(FEAT_HOST_HIDING_STYLE) == 1) || ((feature_int(FEAT_HOST_HIDING_STYLE) == 3) && IsAccount(cptr))) { if (IsAnOper(cptr) && !IsHideOper(cptr) && feature_bool(FEAT_OPERHOST_HIDING)) - ircd_snprintf(0, newhost, HOSTLEN, "%s.%s", + ircd_snprintf(0, newhost, HOSTLEN + 1, "%s.%s", cli_user(cptr)->account, feature_str(FEAT_HIDDEN_OPERHOST)); else - ircd_snprintf(0, newhost, HOSTLEN, "%s.%s", + ircd_snprintf(0, newhost, HOSTLEN + 1, "%s.%s", cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST)); } else if (IsCloakHost(cptr) && ((feature_int(FEAT_HOST_HIDING_STYLE) == 2) || (feature_int(FEAT_HOST_HIDING_STYLE) == 3))) { @@ -1753,10 +1753,12 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, * will cause servers to update correctly. */ if (!FlagHas(&setflags, FLAG_ACCOUNT) && IsAccount(acptr)) { - int len = ACCOUNTLEN; + int len = ACCOUNTLEN + 1; char *ts; if ((ts = strchr(account, ':'))) { - len = (ts++) - account; + len = (ts++) - account + 1; /* +1: ircd_strncpy copies len-1 chars */ + if (len > ACCOUNTLEN + 1) + len = ACCOUNTLEN + 1; cli_user(acptr)->acc_create = atoi(ts); Debug((DEBUG_DEBUG, "Received timestamped account in user mode; " "account \"%s\", timestamp %Tu", account,