xpay: fix taken leak if we fail xpay_core early.
What changed, and why it matters
This commit fixes a small memory-management bug in the xpay plugin of Core Lightning. If the plugin's main payment function exits early (for example, because it receives an invalid invoice), it could fail to properly release a temporary copy of the invoice string, causing a minor memory leak. The fix makes an explicit copy of the string at the start so it can be safely cleaned up later. There is no direct evidence this is a security vulnerability, and the commit message does not describe it as one.
Treat as a routine bug fix. No urgent security action is indicated. If running a node with heavy xpay usage and early-exit invoice parsing, apply the patch to avoid gradual memory growth. Monitor for related denial-of-service research if disclosed.
Security signals we found
Memory/resource leak in plugin code path
Early-exit path bypasses take() ownership transfer
Fix is defensive but commit does not frame as security-relevant
Evidence from the diff
In plugins/xpay/xpay.c, xpay_core() now copies invstring onto tmpctx if it is not already a ‘taken’ allocation. Previously, the function passed invstring to helper functions with take() only on the success path. If the function returned early (e.g., invoice_decode failure for BOLT12 or parse_failure for BOLT11), the taken ownership was never established, so the original invstring could leak. The patch ensures a consistent allocation lifetime regardless of early-exit path.
Changed components
plugins/xpay/xpay.cxpay_core() functionBOLT11 and BOLT12 invoice parsing pathsInspect captured patch +6 / −2
diff --git a/plugins/xpay/xpay.c b/plugins/xpay/xpay.c
index ca5c0a95..ebf0bf57 100644
--- a/plugins/xpay/xpay.c
+++ b/plugins/xpay/xpay.c
@@ -2530,6 +2530,10 @@ static struct command_result *xpay_core(struct command *cmd,
struct out_req *req;
const char *err;
+ /* Make our own copy here, in case we exit early. */
+ if (!taken(invstring))
+ invstring = tal_strdup(tmpctx, invstring);
+
if (bolt12_has_prefix(invstring)) {
struct tlv_invoice *b12inv
= invoice_decode(tmpctx, invstring,
@@ -2555,7 +2559,7 @@ static struct command_result *xpay_core(struct command *cmd,
retryfor,
maxdelay,
layers,
- invstring,
+ take(invstring),
b12inv->invoice_node_id,
b12inv->invoice_payment_hash,
amount_msat(*b12inv->invoice_amount),
@@ -2651,7 +2655,7 @@ static struct command_result *xpay_core(struct command *cmd,
retryfor,
maxdelay,
layers,
- invstring,
+ take(invstring),
&dst,
&b11->payment_hash,
*msat,
Why this scored 21/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.