lightningd: don't allocate huge log entries on the stack
What changed, and why it matters
This fix prevents a crash in Core Lightning's logging code. Previously, when the program wrote a log message, it reserved space on the stack based on the message's length. Because some logged text comes from outside sources (for example, unauthenticated web requests to the clnrest plugin), an attacker could send a very large request and cause the program to overflow its stack, crashing the node. The patch now uses a small fixed stack buffer for ordinary messages and switches to a heap allocation only when a message is unusually large.
Apply this patch promptly. It is a targeted, low-risk fix. If running a node exposed to clnrest or other interfaces that log external input, treat this as a denial-of-service issue that can be triggered remotely without authentication. Consider reviewing other VLA or stack-allocation patterns in logging and plugin code for similar issues.
Security signals we found
Stack-based variable-length allocation sized by attacker-influenced input
Remote unauthenticated input can reach the vulnerable logging path (clnrest request parameters)
Denial-of-service crash (SIGSEGV) demonstrated with a ~10 MB request
Changelog explicitly labels this as a crash fix for logging very large messages
Reported-by line credits an external security researcher/finding
Evidence from the diff
In lightningd/log.c, log_to_files() previously declared a variable-length array (VLA) on the stack sized by the full formatted log entry length. An attacker-controlled string (e.g., clnrest logging unauthenticated HTTP request parameters on rune failure) could therefore trigger a stack overflow and SIGSEGV. The patch replaces the VLA with a 1 kB fixed stack buffer and falls back to a tal_arr heap allocation when the computed buf_len exceeds sizeof(sbuf). snprintf and the length assertion are updated to use buf_len instead of sizeof(buf).
Changed components
lightningd/log.clog_to_files()clnrest plugin (attack vector for unauthenticated large request logging)Inspect captured patch +14 / −5
### lightningd/log.c
@@ -394,15 +394,24 @@ static void log_to_files(const char *log_prefix,
{
char tstamp[sizeof("YYYY-mm-ddTHH:MM:SS.nnnZ ")];
char *entry, nodestr[hex_str_size(PUBKEY_CMPR_LEN)];
- char buf[sizeof("%s%s%s %s-%s: %s\n")
+ /* Entries are usually small, so a stack buffer is fine; but a
+ * peer can make us log arbitrarily large strings (e.g. clnrest
+ * logging unauthenticated request parameters), which must not
+ * be allocated on the stack! */
+ char sbuf[1024];
+ char *buf = sbuf;
+ size_t buf_len = sizeof("%s%s%s %s-%s: %s\n")
+ strlen(log_prefix)
+ sizeof(tstamp)
+ strlen(level_prefix(level))
+ sizeof(nodestr)
+ strlen(entry_prefix)
- + str_len];
+ + str_len;
bool filtered;
+ if (buf_len > sizeof(sbuf))
+ buf = tal_arr(tmpctx, char, buf_len);
+
if (print_timestamps) {
char iso8601_msec_fmt[sizeof("YYYY-mm-ddTHH:MM:SS.%03dZ ")];
strftime(iso8601_msec_fmt, sizeof(iso8601_msec_fmt), "%FT%T.%%03dZ ", gmtime(&time->ts.tv_sec));
@@ -431,15 +440,15 @@ static void log_to_files(const char *log_prefix,
size_t len;
entry = buf;
if (!node_id)
- len = snprintf(buf, sizeof(buf),
+ len = snprintf(buf, buf_len,
"%s%s%s %s: %.*s\n",
log_prefix, tstamp, level_prefix(level), entry_prefix, (int)str_len, str);
else
- len = snprintf(buf, sizeof(buf), "%s%s%s %s-%s: %.*s\n",
+ len = snprintf(buf, buf_len, "%s%s%s %s-%s: %.*s\n",
log_prefix, tstamp, level_prefix(level),
nodestr,
entry_prefix, (int)str_len, str);
- assert(len < sizeof(buf));
+ assert(len < buf_len);
}
/* In complex configurations, we tell loggers to overshare: then weWhy this scored 76/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.