Delay rehashing until we're not processing events

Fixes crash introduced by 0ab6dbbc65. It's
probably a regression since it defeats a system designed to stop this
from happening, but I didn't dig through the history.

rehash() closes listeners. If we happen to get a single epoll() result
that wants to first rehash and then accept a connection, the epoll info
will point to a freed rb_fde_t. Other selectors should have similar
problems, but we didn't investigate that.

rb_fde_ts are normally batched up and freed outside the event
processing, but as of the above commit close_listeners() screws that up
by closing pending FDs immediately in order to create new listeners.

I think it might be a bit better to revert this behaviour and simply not
close listeners if we are going to open new ones over them, but have
opted for the smallest reasonable change I can think of.

Helped-by: Eric Mertens <emertens@gmail.com>
This commit is contained in:
Ed Kellett 2023-11-05 22:05:41 +00:00 committed by Stephen Bennett
parent 460e793274
commit 59ea3c6753
4 changed files with 53 additions and 9 deletions

View file

@ -642,16 +642,18 @@ attach_conf(struct Client *client_p, struct ConfItem *aconf)
return (0);
}
/*
* rehash
*
* Actual REHASH service routine. Called with sig == 0 if it has been called
* as a result of an operator issuing this command, else assume it has been
* called as a result of the server receiving a HUP signal.
*/
bool
rehash(bool sig)
struct rehash_data {
bool sig;
};
static void
service_rehash(void *data_)
{
struct rehash_data *data = data_;
bool sig = data->sig;
free(data);
rb_dlink_node *n;
hook_data_rehash hdata = { sig };
@ -684,6 +686,21 @@ rehash(bool sig)
privilegeset_cleanup_rehash();
call_hook(h_rehash, &hdata);
}
/*
* rehash
*
* Called with sig == 0 if it has been called as a result of an operator
* issuing this command, else assume it has been called as a result of the
* server receiving a HUP signal.
*/
bool
rehash(bool sig)
{
struct rehash_data *data = rb_malloc(sizeof *data);
data->sig = sig;
rb_defer(service_rehash, data);
return false;
}

View file

@ -159,6 +159,7 @@ int rb_ignore_errno(int);
void rb_setselect(rb_fde_t *, unsigned int type, PF * handler, void *client_data);
void rb_init_netio(void);
int rb_select(unsigned long);
void rb_defer(void (*)(void *), void *);
int rb_fd_ssl(rb_fde_t *F);
int rb_get_fd(rb_fde_t *F);
const char *rb_get_ssl_strerror(rb_fde_t *F);

View file

@ -51,6 +51,14 @@ static rb_bh *fd_heap;
static rb_dlink_list timeout_list;
static rb_dlink_list closed_list;
struct defer
{
rb_dlink_node node;
void (*fn)(void *);
void *data;
};
static rb_dlink_list defer_list;
static struct ev_entry *rb_timeout_ev;
@ -2015,10 +2023,27 @@ rb_setselect(rb_fde_t *F, unsigned int type, PF * handler, void *client_data)
setselect_handler(F, type, handler, client_data);
}
void
rb_defer(void (*fn)(void *), void *data)
{
struct defer *defer = rb_malloc(sizeof *defer);
defer->fn = fn;
defer->data = data;
rb_dlinkAdd(defer, &defer->node, &defer_list);
}
int
rb_select(unsigned long timeout)
{
int ret = select_handler(timeout);
rb_dlink_node *ptr, *next;
RB_DLINK_FOREACH_SAFE(ptr, next, defer_list.head)
{
struct defer *defer = ptr->data;
defer->fn(defer->data);
rb_dlinkDelete(ptr, &defer_list);
rb_free(defer);
}
rb_close_pending_fds();
return ret;
}

View file

@ -27,6 +27,7 @@ rb_ctime
rb_current_time
rb_current_time_tv
rb_date
rb_defer
rb_destroy_patricia
rb_dictionary_add
rb_dictionary_create