plugins/pay: don't crash if erring index is past route array end.
What changed, and why it matters
This commit fixes a crash in Core Lightning's `pay` plugin. When processing a payment failure, the plugin assumed that the reported error location was always within the known payment route. In some cases—likely involving multi-hop route hints—the reported index could be larger than the route array, causing an assertion failure that killed the plugin and, because the plugin is marked as important, shut down the entire `lightningd` node. The fix replaces the crash with a safe early return.
Apply the patch. Because the crash can be triggered by a payment failure response, operators should upgrade nodes that process untrusted payments. Consider reviewing other assertion-based bounds checks in plugin error paths for similar assumptions.
Security signals we found
Denial-of-service vector: remote-triggered assertion failure in important plugin
Crash in payment error-handling path
Out-of-bounds index validation hardened from assert to runtime check
Plugin marked important; crash causes `lightningd` shutdown
Evidence from the diff
In plugins/libplugin-pay.c, payment_result_infer() previously used assert(i <= len) to validate r->erring_index against tal_count(route). The commit changes this to a runtime check: if i > len, the function returns early. This prevents an abort when an error index points past the end of the route array, which the author hypothesizes may occur with multi-hop routehints. The rest of the function then safely dereferences route[i-1] and route[i] only when the index is in bounds.
Changed components
plugins/libplugin-pay.cpayment_result_infer()pay pluginInspect captured patch +5 / −2
diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c
index 2792bc1b..a1895605 100644
--- a/plugins/libplugin-pay.c
+++ b/plugins/libplugin-pay.c
@@ -1196,12 +1196,15 @@ static void payment_result_infer(struct route_hop *route,
len = tal_count(route);
i = *r->erring_index;
- assert(i <= len);
+ /* This can actually be greater than the route length? Perhaps
+ * multi-hop routehints? Ignore. */
+ if (i > len)
+ return;
if (r->erring_node == NULL)
r->erring_node = &route[i-1].node_id;
- /* The above assert was enough for the erring_node, but might be off
+ /* The above check was enough for the erring_node, but might be off
* by one on channel and direction, in case the destination failed on
* us. */
if (i == len)
Why this scored 60/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.