Remove queueing of wildcard CSETs and fix use-after-free with queued CSETs

A queued CSET is one that is set before the matching channel is joined.  The CSetList is allocated and linked to
the cset_queue list.

The use-after-free bug was caused because the CSetList wasn't removed from the cset_queue after it was attached
to a joined channel.  If the channel is parted, it eventually ages out of the Whowas-Chan-List and the CSetList
is freed, but there is now a dangling pointer to it in the cset_queue.  This is fixed by removing the CSetList
from the cset_queue when it is attached to a channel, so the net result of /CSET #chan ; /JOIN #chan is the same
as doing the reverse.

Queued wildcard CSETs never worked properly - the queued CSET with the wildcard name would end up attached to any
subsequent matching channel that was joined.  Channel names with wildcards in them are legal anyway, so the best
medium-term solution (pending a full redesign of the whole CSET command) is to just enforce that queued CSETs are
always literal channel names, not wildcards.
This commit is contained in:
Kevin Easton 2019-08-03 01:09:21 +10:00
parent f714e3e951
commit ddfbec0b4b
2 changed files with 11 additions and 32 deletions

View File

@ -1,5 +1,8 @@
[Changes 1.2.2]
* Remove queueing of wildcard CSETs and fix use-after-free with queued
CSETs. (caf)
* Bump MAXPARA (maximum protocol message arguments) to 20 as per EPIC5. (caf)
* Correctly handle a last argument prefixed with : for any protocol message,

View File

@ -325,34 +325,15 @@ static void set_cset_var_value(CSetList *tmp, int var_index, char *value)
CSetList *check_cset_queue(char *channel, int add)
{
CSetList *c = NULL;
int found = 0;
if (!strchr(channel, '*') && !(c = (CSetList *)find_in_list((List **)&cset_queue, channel, 0)))
CSetList *c = (CSetList *)find_in_list((List **)&cset_queue, channel, 0);
if (!c && add)
{
if (!add)
{
for (c = cset_queue; c; c = c->next)
if (!my_stricmp(c->channel, channel) || wild_match(c->channel, channel))
return c;
return NULL;
}
c = create_csets_for_channel(channel);
add_to_list((List **)&cset_queue, (List *)c);
found++;
}
if (c)
return c;
if (add && !found)
{
for (c = cset_queue; c; c = c->next)
if (!my_stricmp(c->channel, channel))
return c;
c = create_csets_for_channel(channel);
c->next = cset_queue;
cset_queue = c;
return c;
}
return NULL;
return c;
}
static inline void cset_variable_range(char *channel, int cnt, int var_index, char *args)
@ -541,14 +522,9 @@ CSetList *create_csets_for_channel(char *channel)
ircpanic("Variable [%d] (%s) is out of order.", i, cset_array[i].name);
#endif
if (check_cset_queue(channel, 0))
{
if ((tmp = (CSetList *)find_in_list((List **)&cset_queue, channel, 0)))
return tmp;
for (tmp = cset_queue; tmp; tmp = tmp->next)
if (!my_stricmp(tmp->channel, channel) || wild_match(tmp->channel, channel))
return tmp;
}
if ((tmp = (CSetList *)remove_from_list((List **)&cset_queue, channel)))
return tmp;
tmp = (CSetList *) new_malloc(sizeof(CSetList));
/* use default settings. */
tmp->set_aop = get_int_var(AOP_VAR);