What changed, and why it matters
This change replaces a placeholder 'context.TODO()' with a proper cancellation context in a routing function used when sending Lightning payments. It is a code-quality and robustness improvement that lets long-running route-finding operations be cancelled cleanly. There is no direct evidence this fixes an exploitable security vulnerability.
Treat as a normal code-quality patch. Review whether any other payment-lifecycle helpers still use context.TODO() or context.Background(), and consider adding tests that verify cancellation behaviour during route requests.
Security signals we found
Replacement of context.TODO() with a real cancellation context
Potential reduction in resource exhaustion / goroutine leak risk during route computation
No explicit security framing by the vendor in commit title or message
Evidence from the diff
The commit modifies paymentLifecycle.requestRoute() to accept a context.Context argument instead of internally using context.TODO(). The caller in the payment lifecycle now passes cleanupCtx, allowing the route request to honour cancellation/timeout signals. Test callers are updated to pass t.Context(). This is a defensive cleanup that prevents goroutine leaks or unbounded operations when a payment lifecycle is cancelled, but the diff alone does not demonstrate a specific security bug.
Changed components
routing/payment_lifecycle.gorouting/payment_lifecycle_test.goInspect captured patch +6 / −8
diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go
index 6405e85..c8a59a3 100644
--- a/routing/payment_lifecycle.go
+++ b/routing/payment_lifecycle.go
@@ -288,7 +288,7 @@ lifecycle:
}
// Now request a route to be used to create our HTLC attempt.
- rt, err := p.requestRoute(ps)
+ rt, err := p.requestRoute(cleanupCtx, ps)
if err != nil {
return exitWithErr(err)
}
@@ -399,11 +399,9 @@ func (p *paymentLifecycle) checkContext(ctx context.Context) error {
// requestRoute is responsible for finding a route to be used to create an HTLC
// attempt.
-func (p *paymentLifecycle) requestRoute(
+func (p *paymentLifecycle) requestRoute(ctx context.Context,
ps *paymentsdb.MPPaymentState) (*route.Route, error) {
- ctx := context.TODO()
-
remainingFees := p.calcFeeBudget(ps.FeesPaid)
// Query our payment session to construct a route.
diff --git a/routing/payment_lifecycle_test.go b/routing/payment_lifecycle_test.go
index 7e94315..a03218b 100644
--- a/routing/payment_lifecycle_test.go
+++ b/routing/payment_lifecycle_test.go
@@ -393,7 +393,7 @@ func TestRequestRouteSucceed(t *testing.T) {
mock.Anything,
).Return(dummyRoute, nil)
- result, err := p.requestRoute(ps)
+ result, err := p.requestRoute(t.Context(), ps)
require.NoError(t, err, "expect no error")
require.Equal(t, dummyRoute, result, "returned route not matched")
@@ -430,7 +430,7 @@ func TestRequestRouteHandleCriticalErr(t *testing.T) {
mock.Anything,
).Return(nil, errDummy)
- result, err := p.requestRoute(ps)
+ result, err := p.requestRoute(t.Context(), ps)
// Expect an error is returned since it's critical.
require.ErrorIs(t, err, errDummy, "error not matched")
@@ -470,7 +470,7 @@ func TestRequestRouteHandleNoRouteErr(t *testing.T) {
p.identifier, paymentsdb.FailureReasonNoRoute,
).Return(nil).Once()
- result, err := p.requestRoute(ps)
+ result, err := p.requestRoute(t.Context(), ps)
// Expect no error is returned since it's not critical.
require.NoError(t, err, "expected no error")
@@ -513,7 +513,7 @@ func TestRequestRouteFailPaymentError(t *testing.T) {
mock.Anything,
).Return(nil, errNoTlvPayload)
- result, err := p.requestRoute(ps)
+ result, err := p.requestRoute(t.Context(), ps)
// Expect an error is returned.
require.ErrorIs(t, err, errDummy, "error not matched")
Why this scored 16/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.