2.6 KiB
weechat-0001: irc_nick_search() O(n) called per-channel in AWAY/NICK/QUIT/KILL handlers
Target: weechat/weechat
Severity: HIGH
CWE: CWE-407 (Inefficient Algorithmic Complexity)
File: src/plugins/irc/irc-protocol.c (multiple handlers)
Status: PATCHED
Description
irc_nick_search() performs an O(n) linear scan of the channel->nicks linked
list (irc-nick.c:830–848). It is called inside an outer loop over all channels
the server knows about in four hot IRC protocol handlers:
| Handler | File:Line | Pattern |
|---|---|---|
AWAY |
irc-protocol.c:648–651 | for each channel: irc_nick_search(nick) |
NICK |
irc-protocol.c:2295–2366 | for each channel: irc_nick_search(old_nick) |
QUIT |
irc-protocol.c:3397–3408 | for each channel: irc_nick_search(nick) |
KILL |
irc-protocol.c:2051–2055 | for each channel: irc_nick_search(nick) × 2 |
All four are O(C × N) where C = channels on server, N = nicks per channel.
Root cause
// irc-nick.c:830 — O(n) linked-list walk
struct t_irc_nick *
irc_nick_search(struct t_irc_server *server, struct t_irc_channel *channel,
const char *nickname) {
for (ptr_nick = channel->nicks; ptr_nick; ptr_nick = ptr_nick->next_nick) {
if (irc_server_strcasecmp(server, ptr_nick->name, nickname) == 0)
return ptr_nick;
}
return NULL;
}
channel->nicks is a plain doubly-linked list with no hash index.
The handlers iterate all channels, calling irc_nick_search for each:
// irc-protocol.c:648 — AWAY handler — O(C × N)
for (ptr_channel = ctxt->server->channels; ptr_channel; ptr_channel = ptr_channel->next_channel) {
ptr_nick = irc_nick_search(ctxt->server, ptr_channel, ctxt->nick); // O(N)
...
}
On a busy server with 200 channels of 500 nicks each, a single AWAY message triggers 100,000 strcmp operations.
Fix
Add a GHashTable* nicks_hashtable (nick_name → t_irc_nick*) to
t_irc_channel. Maintain it in sync with channel->nicks on add/remove.
Replace irc_nick_search with a hash lookup.
// O(1) lookup
struct t_irc_nick *
irc_nick_search(struct t_irc_server *server, struct t_irc_channel *channel,
const char *nickname) {
if (channel->nicks_hashtable)
return weechat_hashtable_get(channel->nicks_hashtable, lowercase(nickname));
// fallback for empty/initializing channel
...
}
Ops/ns numbers (Java benchmark)
See defects/weechat/unit/WeechatTest.java.
At C=200 channels, N=500 nicks: slow ~100,000 ops, fast ~200 ops → ~500× speedup.