Skip to content

lightningd: don't free the --conf filename when setconfig can't write it - #9711

Open
nepet wants to merge 1 commit into
ElementsProject:masterfrom
nepet:setconfig-conf-uaf
Open

nepet wants to merge 1 commit into
ElementsProject:masterfrom
nepet:setconfig-conf-uaf

Conversation

@nepet

@nepet nepet commented Nov 7, 2026

Copy link
Copy Markdown
Member

Checklist

Before submitting the PR, ensure the following tasks are completed. If an item is not applicable to your PR, please mark it as checked:

  • The changelog has been updated in the relevant commit(s) according to the guidelines.
  • Tests have been added or modified to reflect the changes.
  • Documentation has been reviewed and updated as needed.
  • Related issues have been listed and linked, including any that this PR closes.
  • Important All PRs must consider how to reverse any persistent changes for tools/lightning-downgrade

Fixes #9351.

With an explicit --conf in a directory lightningd can't write, the first setconfig fails with "Cannot write to config file", as it should. But config_not_writable() hands the file name to the command with tal_steal(), and with --conf that name is ld->config_filename itself, so freeing the failed command frees it. The next setconfig reads the freed name, passes the writability check against the wrong directory, and lightningd aborts in append_to_file().

This returns a copy instead, like the other branch of config_not_writable() already does. Introduced in 5e79cd4.

The test starts a node with --conf in a read-only directory and calls setconfig twice: both calls must fail with "Cannot write to config file", and the node must still be running.

This doesn't conflict with #9356, which makes the persistence path return errors instead of calling fatal(). That PR doesn't fix this bug on its own: with it, the second call still reads freed memory.

With an explicit --conf in a directory lightningd can't write, setconfig
fails with "Cannot write to config file", as it should. But
config_not_writable() hands the filename to the command with tal_steal(),
and with --conf that filename is ld->config_filename itself, so freeing
the failed command frees it.

The next setconfig then reads the freed name, passes the writability
check against the wrong directory, and lightningd aborts in
append_to_file() ("Could not write to config : No such file or
directory").

Return a copy instead, as the other branch of config_not_writable()
already does.

Introduced in 5e79cd4 ("lightningd: check for writability before
allowing setconfig.").

Fixes: ElementsProject#9351
Changelog-Fixed: lightningd: a `setconfig` after one that failed to write an explicit `--conf` file no longer crashes the node.
Comment thread lightningd/configs.c
/* Copy: with --conf, fname is ld->config_filename itself. */
if (!dir_writable(fname))
return tal_steal(ctx, fname);
return tal_strdup(ctx, fname);

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.

maybe?

Suggested change
return tal_strdup(ctx, fname);
return tal_strdup(ctx, ld->config_filename);

Then config_not_writable() could call base_conf_file(ctx, ld, NULL) and return fname directly, with no steal and no copy. The other caller, create_setconfig_include(), passes tmpctx, so it is not affected. This is optional!!

Comment thread tests/test_misc.py
l1.start()

os.chmod(confdir, 0o550)
for val in (20000, 30000):

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.

as i see check goes through the same config_not_writable(cmd, cmd, cv) call (lines from 710 to 758), and the check command is freed too. So before this fix, lightning-cli check setconfig . . . with --conf would also free config_filename. so a harmless dry run could set up the crash??

maybe
l1.rpc.check("setconfig", config="min-capacity-sat", val=10000) and expect the same error, like test_setconfig_access does

Comment thread tests/test_misc.py
l1.daemon.opts['conf'] = conffile
l1.start()

os.chmod(confdir, 0o550)

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 an assertion fails, os.chmod(confdir, 0o750) never runs?

This branch has not been deployed

No deployments
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