When a remote MODRESTART command is received, it will pass through the

ENCAP module. The ms_encap function is responsible for dispatching the
command handler and then the modules will eventually be reloaded.

However, if the ENCAP module is reloaded to a different address, the
stack now contains the address of a function that no longer exists.

Also, in this version of the IRCd, the module restarting functionality
was located in a function that is itself located in a module, so things
will also go badly if that module is reloaded to a different address,
too.

Return immediately from the command handler and have the event loop
call the function responsible for reloading the modules instead.

c.f. release/3.5 commit db05a36210

Reported-by: mniip (Freenode)
This commit is contained in:
Aaron Jones 2016-12-28 22:08:14 +00:00
parent 6002ccec6b
commit 41390bfe5f
No known key found for this signature in database
GPG key ID: EC6F86EE9CD840B5
2 changed files with 48 additions and 30 deletions

View file

@ -668,3 +668,38 @@ load_a_module(const char *path, bool warn, int origin, bool core)
rb_free(mod_displayname); rb_free(mod_displayname);
return true; return true;
} }
void
modules_do_restart(void *unused)
{
unsigned int modnum = 0;
rb_dlink_node *ptr, *nptr;
RB_DLINK_FOREACH_SAFE(ptr, nptr, module_list.head)
{
struct module *mod = ptr->data;
if(!unload_one_module(mod->name, false))
{
ilog(L_MAIN, "Module Restart: %s was not unloaded %s",
mod->name,
mod->core? "(core module)" : "");
if(!mod->core)
sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
"Module Restart: %s failed to unload",
mod->name);
continue;
}
modnum++;
}
load_all_modules(false);
load_core_modules(false);
rehash(false);
sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
"Module Restart: %u modules unloaded, %lu modules loaded",
modnum, rb_dlink_list_length(&module_list));
ilog(L_MAIN, "Module Restart: %u modules unloaded, %lu modules loaded", modnum, rb_dlink_list_length(&module_list));
}

View file

@ -54,6 +54,8 @@ static void do_modreload(struct Client *, const char *);
static void do_modlist(struct Client *, const char *); static void do_modlist(struct Client *, const char *);
static void do_modrestart(struct Client *); static void do_modrestart(struct Client *);
extern void modules_do_restart(void *); /* end of ircd/modules.c */
struct Message modload_msgtab = { struct Message modload_msgtab = {
"MODLOAD", 0, 0, 0, 0, "MODLOAD", 0, 0, 0, 0,
{mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_modload, 2}, {mo_modload, 2}} {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_modload, 2}, {mo_modload, 2}}
@ -341,38 +343,19 @@ do_modreload(struct Client *source_p, const char *module)
static void static void
do_modrestart(struct Client *source_p) do_modrestart(struct Client *source_p)
{ {
unsigned int modnum = 0;
rb_dlink_node *ptr, *nptr;
sendto_one_notice(source_p, ":Reloading all modules"); sendto_one_notice(source_p, ":Reloading all modules");
RB_DLINK_FOREACH_SAFE(ptr, nptr, module_list.head) /*
{ * If a remote MODRESTART is received, m_encap.so will be reloaded,
struct module *mod = ptr->data; * but ms_encap is in the call stack (it indirectly calls this
if(!unload_one_module(mod->name, false)) * function). Also, this function is itself in a module.
{ *
ilog(L_MAIN, "Module Restart: %s was not unloaded %s", * This will go horribly wrong if either module is reloaded to a
mod->name, * different address.
mod->core? "(core module)" : ""); *
* So, defer the restart to the event loop and return now.
if(!mod->core) */
sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, rb_event_addonce("modules_do_restart", modules_do_restart, NULL, 1);
"Module Restart: %s failed to unload",
mod->name);
continue;
}
modnum++;
}
load_all_modules(false);
load_core_modules(false);
rehash(false);
sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
"Module Restart: %u modules unloaded, %lu modules loaded",
modnum, rb_dlink_list_length(&module_list));
ilog(L_MAIN, "Module Restart: %u modules unloaded, %lu modules loaded", modnum, rb_dlink_list_length(&module_list));
} }
static void static void