Skip to content

core: do not crash setting the log level before the process table exists#4121

Open
Lt-Flash wants to merge 1 commit into
OpenSIPS:masterfrom
Lt-Flash:fix/log-level-before-fork
Open

core: do not crash setting the log level before the process table exists#4121
Lt-Flash wants to merge 1 commit into
OpenSIPS:masterfrom
Lt-Flash:fix/log-level-before-fork

Conversation

@Lt-Flash

Copy link
Copy Markdown

What

set_proc_log_level() and reset_proc_log_level() segfault when called from a module's mod_init().

Root cause

init_modules() runs several steps before init_multi_proc_support(), so the process table is still NULL while every module's mod_init() executes — and the setter writes through it unconditionally:

void __set_proc_log_level(int proc_idx, int level)
{
	pt[proc_idx].log_level = level;      /* pt == NULL during mod_init() */
}

reset_proc_log_level() fails the same way one level down: default_log_level is a static pointer that init_log_level() has not filled in yet, so it dereferences NULL.

Why it is worth guarding

Temporarily lowering the log level around a call that is expected to fail is the natural way for a module to keep a benign startup error out of its output — and the API gives no hint that it is unusable at exactly the point most modules would reach for it. The failure is a hard crash at startup, not a diagnostic.

The fix

Guards each affected setter. Where the intent can still be honoured it is, rather than silently dropping the request:

  • __set_proc_log_level()log_level already points at the static holder before init_log_level() runs, and there is only one process at that stage, so the level is applied through it.
  • reset_proc_log_level() — restores the value logging started with (log_level_holder).
  • __set_proc_default_log_level() — updates that same holder.
  • set_global_log_level() — additionally guards log_level_global, which init_log_level() allocates. Before that, counted_max_processes is 0 so its loop is a no-op and the shared value does not exist, so it applies the level to the current process instead.
  • suppress_proc_log_event() / reset_proc_log_event() — return early; the event consumer they guard is not running yet either.

No behaviour changes once the process table exists — every guard is a NULL check on state that is non-NULL from init_multi_proc_support() onward.

Testing

A/B tested with a module calling set_proc_log_level() followed by reset_proc_log_level() from its mod_init(): the same module segfaults on an unpatched core and starts normally on a patched one.

set_proc_log_level() and reset_proc_log_level() segfault when called
from a module's mod_init(). init_modules() runs several steps before
init_multi_proc_support(), so pt is still NULL, and __set_proc_log_level()
writes pt[proc_idx].log_level unconditionally. reset_proc_log_level()
fails the same way one level down: default_log_level is a static pointer
that init_log_level() has not filled in yet, so it dereferences NULL.

This is easy to hit. Temporarily lowering the log level around a call
that is expected to fail is the natural way for a module to keep an
expected error out of its startup output, and the API gives no hint that
it is unusable at the point most modules would reach for it.

Guards each of the affected setters. Where the intent can still be
honoured it is, rather than silently dropping the request: log_level
already points at the static holder before init_log_level() runs, and
there is only one process at that stage, so __set_proc_log_level() sets
the level through it and reset_proc_log_level() restores the value
logging started with. __set_proc_default_log_level() updates that same
holder. set_global_log_level() additionally guards log_level_global,
which init_log_level() allocates - before that, counted_max_processes is
0 so its loop is a no-op and the shared value does not exist yet, so it
applies the level to the current process instead.
suppress_proc_log_event() and reset_proc_log_event() return early: the
event consumer they guard is not running yet either.

Verified by calling set_proc_log_level() followed by
reset_proc_log_level() from a module mod_init(): the same module
segfaults on an unpatched core and starts normally on a patched one.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant