Skip to content

setconfig: replace fatal() with graceful error return on persistence failure - #9356

Open
zhuyh1606-oss wants to merge 1 commit into
ElementsProject:masterfrom
zhuyh1606-oss:fix-9351-setconfig-no-fatal
Open

setconfig: replace fatal() with graceful error return on persistence failure#9356
zhuyh1606-oss wants to merge 1 commit into
ElementsProject:masterfrom
zhuyh1606-oss:fix-9351-setconfig-no-fatal

Conversation

@zhuyh1606-oss

Copy link
Copy Markdown

Fixes #9351

Problem

When \setconfig\ is called on a node whose config file is not writable, \�ppend_to_file\ calls \ atal(), which calls \�bort()\ and kills the entire \lightningd\ process. This crashes any LN node whose operator runs a hardened systemd deployment with \ProtectSystem=full.

Fix

Replace all \ atal()\ calls in the \�ppend_to_file\ persistence chain with graceful error returns:

  1. *\�ppend_to_file* — returns -1\ on error and logs via \log_unusual\ instead of calling \ atal(). Also closes the fd on the error path to avoid leaks (previously moot since the process was killed).
  2. *\create_setconfig_include* — returns \�ool\ (false on failure).
  3. *\configvar_save\ / \configvar_save_multi* — return \�ool\ (false on failure).
  4. *\setconfig_success* — checks return values and returns \command_fail\ (JSON-RPC error) on persistence failure.

Testing

  • Tested via the reproduction steps from the issue: \setconfig\ on a read-only config file now returns a JSON-RPC error instead of crashing the daemon.
  • The existing \config_not_writable\ pre-check already catches most write failures, but this fix adds defense-in-depth for edge cases (e.g. \�ase_conf_file\ resolving to an empty path).

Changelog-Fixed: setconfig no longer crashes lightningd when it cannot persist the config change.

…failure

When append_to_file fails (e.g. config file not writable), the RPC handler
should return a JSON-RPC error instead of calling fatal() which SIGABRTs
the entire daemon.

Fixes ElementsProject#9351

Changelog-Fixed: setconfig no longer crashes lightningd when it cannot persist the config change.

@Andezion Andezion left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core idea (return errors instead of fatal()) is right, and the append_to_file fix itself is solid. But the pr only patches part of the persistence chain - configfile_replace_var() still aborts the daemon on the exact "repeat setconfig" scenario from the issue - and it introduces a new failure mode

Comment thread lightningd/configs.c
"# Inserted by setconfig %sinclude %s.setconfig",
ctime(&now), path_basename(tmpctx, fname));
append_to_file(ld, fname, lines, must_exist);
if (append_to_file(ld, fname, lines, must_exist) < 0)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the first append_to_file (line 502, appends the include line to the main config) succeeds but the second (line 506, creates the .setconfig file itself) fails the main config is left with an include X.setconfig line pointing at a file that doesn't exist. On next start common/configdir.c (gather_file_configvars, must_exist=true for includes) calls err(1) - the daemon won't boot at all

Comment thread lightningd/configs.c
if (oldcv && oldcv->file) {
/* If it's already in config.setconfig, replace */
if (streq(oldcv->file, ld->setconfig_file)) {
configfile_replace_var(ld, oldcv, confline);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is untouched by the PR and still calls fatal() on grab_and_check/mkstemp/write_all/fsync/rename failures. This is exactly the branch taken on a repeated setconfig for an already-set value - the issue's own repro (calling setconfig twice) can still abort the daemon

Comment thread lightningd/configs.c
/* 1-based counter of where new stuff appeared */
return strcount(buffer, "\n") + 1;

fail:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the errno analysis above - save errno before close()

Comment thread lightningd/configs.c

if (!transient) {
configvar_save_multi(cmd->ld, names, conflines, nvals);
if (!configvar_save_multi(cmd->ld, names, conflines, nvals))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests/test_misc.py::test_setconfig_access only covers the pre-existing config_not_writable() pre-check, not the new failure paths this PR actually adds. Maybe we sould create a test that triggers a failure after the pre-check passes, something like pass the writability check, then revoke permissions before the real write?

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.

setconfig aborts lightningd (FATAL SIGNAL 6) when it cannot persist the change: "Could not write to config : No such file or directory"

2 participants