Skip to content

Commit 6485a81

Browse files
authored
Merge pull request #9134 from ellemouton/checkPayAddrBeforeDeref
routerrpc: check payaddr before using for probing
2 parents 84c91f7 + 8663950 commit 6485a81

File tree

17 files changed

+107
-73
lines changed

17 files changed

+107
-73
lines changed

docs/release-notes/release-notes-0.19.0.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
propagate mission control and debug level config values to the main LND config
2424
struct so that the GetDebugInfo response is accurate.
2525

26+
* [Fix a bug](https://github.com/lightningnetwork/lnd/pull/9134) that would
27+
cause a nil pointer dereference during the probing of a payment request that
28+
does not contain a payment address.
29+
2630
# New Features
2731
## Functional Enhancements
2832
## RPC Additions
@@ -69,5 +73,6 @@
6973
# Contributors (Alphabetical Order)
7074

7175
* CharlieZKSmith
76+
* Elle Mouton
7277
* Pins
7378
* Ziggie

feature/default_sets.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,4 @@ var defaultSetDesc = setDesc{
9292
SetInit: {}, // I
9393
SetNodeAnn: {}, // N
9494
},
95-
lnwire.Bolt11BlindedPathsOptional: {
96-
SetInvoice: {}, // I
97-
},
9895
}

invoices/modification_interceptor.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ func (s *safeCallback) Exec(req HtlcModifyRequest) (*HtlcModifyResponse,
6666
// settle an invoice, enabling a subscribed client to modify certain aspects of
6767
// those HTLCs.
6868
type HtlcModificationInterceptor struct {
69+
started atomic.Bool
70+
stopped atomic.Bool
71+
6972
// callback is the wrapped client callback function that is called when
7073
// an invoice is intercepted. This function gives the client the ability
7174
// to determine how the invoice should be settled.
@@ -79,6 +82,7 @@ type HtlcModificationInterceptor struct {
7982
func NewHtlcModificationInterceptor() *HtlcModificationInterceptor {
8083
return &HtlcModificationInterceptor{
8184
callback: &safeCallback{},
85+
quit: make(chan struct{}),
8286
}
8387
}
8488

@@ -163,11 +167,19 @@ func (s *HtlcModificationInterceptor) RegisterInterceptor(
163167

164168
// Start starts the service.
165169
func (s *HtlcModificationInterceptor) Start() error {
170+
if !s.started.CompareAndSwap(false, true) {
171+
return nil
172+
}
173+
166174
return nil
167175
}
168176

169177
// Stop stops the service.
170178
func (s *HtlcModificationInterceptor) Stop() error {
179+
if !s.stopped.CompareAndSwap(false, true) {
180+
return nil
181+
}
182+
171183
close(s.quit)
172184

173185
return nil

lnrpc/routerrpc/router_backend.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
sphinx "github.com/lightningnetwork/lightning-onion"
1717
"github.com/lightningnetwork/lnd/channeldb"
1818
"github.com/lightningnetwork/lnd/feature"
19+
"github.com/lightningnetwork/lnd/fn"
1920
"github.com/lightningnetwork/lnd/htlcswitch"
2021
"github.com/lightningnetwork/lnd/lnrpc"
2122
"github.com/lightningnetwork/lnd/lntypes"
@@ -1011,7 +1012,7 @@ func (r *RouterBackend) extractIntentFromSendRequest(
10111012
if len(rpcPayReq.PaymentAddr) > 0 {
10121013
var addr [32]byte
10131014
copy(addr[:], rpcPayReq.PaymentAddr)
1014-
payAddr = &addr
1015+
payAddr = fn.Some(addr)
10151016
}
10161017
} else {
10171018
err = payIntent.SetPaymentHash(*payReq.PaymentHash)
@@ -1128,7 +1129,7 @@ func (r *RouterBackend) extractIntentFromSendRequest(
11281129
} else {
11291130
copy(payAddr[:], rpcPayReq.PaymentAddr)
11301131
}
1131-
payIntent.PaymentAddr = &payAddr
1132+
payIntent.PaymentAddr = fn.Some(payAddr)
11321133

11331134
// Generate random SetID and root share.
11341135
var setID [32]byte
@@ -1167,7 +1168,7 @@ func (r *RouterBackend) extractIntentFromSendRequest(
11671168
var payAddr [32]byte
11681169
copy(payAddr[:], rpcPayReq.PaymentAddr)
11691170

1170-
payIntent.PaymentAddr = &payAddr
1171+
payIntent.PaymentAddr = fn.Some(payAddr)
11711172
}
11721173
}
11731174

lnrpc/routerrpc/router_server.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -528,11 +528,16 @@ func (s *Server) probePaymentRequest(ctx context.Context, paymentRequest string,
528528
AmtMsat: amtMsat,
529529
PaymentHash: paymentHash[:],
530530
FeeLimitSat: routeFeeLimitSat,
531-
PaymentAddr: payReq.PaymentAddr[:],
532531
FinalCltvDelta: int32(payReq.MinFinalCLTVExpiry()),
533532
DestFeatures: MarshalFeatures(payReq.Features),
534533
}
535534

535+
// If the payment addresses is specified, then we'll also populate that
536+
// now as well.
537+
payReq.PaymentAddr.WhenSome(func(addr [32]byte) {
538+
copy(probeRequest.PaymentAddr, addr[:])
539+
})
540+
536541
hints := payReq.RouteHints
537542

538543
// If the hints don't indicate an LSP then chances are that our probe
@@ -1453,12 +1458,12 @@ func (s *Server) BuildRoute(_ context.Context,
14531458
outgoingChan = &req.OutgoingChanId
14541459
}
14551460

1456-
var payAddr *[32]byte
1461+
var payAddr fn.Option[[32]byte]
14571462
if len(req.PaymentAddr) != 0 {
14581463
var backingPayAddr [32]byte
14591464
copy(backingPayAddr[:], req.PaymentAddr)
14601465

1461-
payAddr = &backingPayAddr
1466+
payAddr = fn.Some(backingPayAddr)
14621467
}
14631468

14641469
if req.FinalCltvDelta == 0 {

routing/integrated_routing_context_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ func (c *integratedRoutingContext) testPayment(maxParts uint32,
187187
FinalCLTVDelta: uint16(c.finalExpiry),
188188
FeeLimit: lnwire.MaxMilliSatoshi,
189189
Target: c.target.pubkey,
190-
PaymentAddr: &paymentAddr,
190+
PaymentAddr: fn.Some(paymentAddr),
191191
DestFeatures: lnwire.NewFeatureVector(
192192
baseFeatureBits, lnwire.Features,
193193
),

routing/pathfind.go

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ type finalHopParams struct {
111111
cltvDelta uint16
112112

113113
records record.CustomSet
114-
paymentAddr *[32]byte
114+
paymentAddr fn.Option[[32]byte]
115115

116116
// metadata is additional data that is sent along with the payment to
117117
// the payee.
@@ -226,20 +226,17 @@ func newRoute(sourceVertex route.Vertex,
226226
// If we're attaching a payment addr but the receiver
227227
// doesn't support both TLV and payment addrs, fail.
228228
payAddr := supports(lnwire.PaymentAddrOptional)
229-
if !payAddr && finalHop.paymentAddr != nil {
229+
if !payAddr && finalHop.paymentAddr.IsSome() {
230230
return nil, errors.New("cannot attach " +
231231
"payment addr")
232232
}
233233

234234
// Otherwise attach the mpp record if it exists.
235235
// TODO(halseth): move this to payment life cycle,
236236
// where AMP options are set.
237-
if finalHop.paymentAddr != nil {
238-
mpp = record.NewMPP(
239-
finalHop.totalAmt,
240-
*finalHop.paymentAddr,
241-
)
242-
}
237+
finalHop.paymentAddr.WhenSome(func(addr [32]byte) {
238+
mpp = record.NewMPP(finalHop.totalAmt, addr)
239+
})
243240

244241
metadata = finalHop.metadata
245242

@@ -452,7 +449,7 @@ type RestrictParams struct {
452449
// PaymentAddr is a random 32-byte value generated by the receiver to
453450
// mitigate probing vectors and payment sniping attacks on overpaid
454451
// invoices.
455-
PaymentAddr *[32]byte
452+
PaymentAddr fn.Option[[32]byte]
456453

457454
// Amp signals to the pathfinder that this payment is an AMP payment
458455
// and therefore it needs to account for additional AMP data in the
@@ -608,7 +605,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
608605
// checking that it supports the features we need. If the caller has a
609606
// payment address to attach, check that our destination feature vector
610607
// supports them.
611-
if r.PaymentAddr != nil &&
608+
if r.PaymentAddr.IsSome() &&
612609
!features.HasFeature(lnwire.PaymentAddrOptional) {
613610

614611
return nil, 0, errNoPaymentAddr
@@ -1435,9 +1432,9 @@ func lastHopPayloadSize(r *RestrictParams, finalHtlcExpiry int32,
14351432
}
14361433

14371434
var mpp *record.MPP
1438-
if r.PaymentAddr != nil {
1439-
mpp = record.NewMPP(amount, *r.PaymentAddr)
1440-
}
1435+
r.PaymentAddr.WhenSome(func(addr [32]byte) {
1436+
mpp = record.NewMPP(amount, addr)
1437+
})
14411438

14421439
var amp *record.AMP
14431440
if r.Amp != nil {

routing/pathfind_test.go

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,7 +1367,7 @@ func TestNewRoute(t *testing.T) {
13671367
// overwrite the final hop's feature vector in the graph.
13681368
destFeatures *lnwire.FeatureVector
13691369

1370-
paymentAddr *[32]byte
1370+
paymentAddr fn.Option[[32]byte]
13711371

13721372
// metadata is the payment metadata to attach to the route.
13731373
metadata []byte
@@ -1446,7 +1446,7 @@ func TestNewRoute(t *testing.T) {
14461446
// a fee to receive the payment.
14471447
name: "two hop single shot mpp",
14481448
destFeatures: tlvPayAddrFeatures,
1449-
paymentAddr: &testPaymentAddr,
1449+
paymentAddr: fn.Some(testPaymentAddr),
14501450
paymentAmount: 100000,
14511451
hops: []*models.CachedEdgePolicy{
14521452
createHop(0, 1000, 1000000, 10),
@@ -1911,7 +1911,7 @@ func runDestPaymentAddr(t *testing.T, useCache bool) {
19111911
luoji := ctx.keyFromAlias("luoji")
19121912

19131913
// Add payment address w/o any invoice features.
1914-
ctx.restrictParams.PaymentAddr = &[32]byte{1}
1914+
ctx.restrictParams.PaymentAddr = fn.Some([32]byte{1})
19151915

19161916
// Add empty destination features. This should cause us to fail, since
19171917
// this overrides anything in the graph.
@@ -2955,7 +2955,7 @@ func runInboundFees(t *testing.T, useCache bool) {
29552955
ctx := newPathFindingTestContext(t, useCache, testChannels, "a")
29562956

29572957
payAddr := [32]byte{1}
2958-
ctx.restrictParams.PaymentAddr = &payAddr
2958+
ctx.restrictParams.PaymentAddr = fn.Some(payAddr)
29592959
ctx.restrictParams.DestFeatures = tlvPayAddrFeatures
29602960

29612961
const (
@@ -2974,7 +2974,7 @@ func runInboundFees(t *testing.T, useCache bool) {
29742974
amt: paymentAmt,
29752975
cltvDelta: finalHopCLTV,
29762976
records: nil,
2977-
paymentAddr: &payAddr,
2977+
paymentAddr: fn.Some(payAddr),
29782978
totalAmt: paymentAmt,
29792979
},
29802980
nil,
@@ -3469,7 +3469,7 @@ func TestLastHopPayloadSize(t *testing.T) {
34693469
{
34703470
name: "Non blinded final hop",
34713471
restrictions: &RestrictParams{
3472-
PaymentAddr: paymentAddr,
3472+
PaymentAddr: fn.Some(*paymentAddr),
34733473
DestCustomRecords: customRecords,
34743474
Metadata: metadata,
34753475
Amp: ampOptions,
@@ -3501,12 +3501,10 @@ func TestLastHopPayloadSize(t *testing.T) {
35013501
t.Run(tc.name, func(t *testing.T) {
35023502
t.Parallel()
35033503

3504-
var mpp *record.MPP
3505-
if tc.restrictions.PaymentAddr != nil {
3506-
mpp = record.NewMPP(
3507-
tc.amount, *tc.restrictions.PaymentAddr,
3508-
)
3509-
}
3504+
mpp := fn.MapOptionZ(tc.restrictions.PaymentAddr,
3505+
func(addr [32]byte) *record.MPP {
3506+
return record.NewMPP(tc.amount, addr)
3507+
})
35103508

35113509
// In case it's an AMP payment we use the max AMP record
35123510
// size to estimate the final hop size.

routing/payment_session.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
344344
// record. If it has a blinded path though, then we
345345
// can split. Split payments to blinded paths won't have
346346
// MPP records.
347-
if p.payment.PaymentAddr == nil &&
347+
if p.payment.PaymentAddr.IsNone() &&
348348
p.payment.BlindedPathSet == nil {
349349

350350
p.log.Debugf("not splitting because payment " +

routing/router.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,7 @@ type LightningPayment struct {
862862
// PaymentAddr is the payment address specified by the receiver. This
863863
// field should be a random 32-byte nonce presented in the receiver's
864864
// invoice to prevent probing of the destination.
865-
PaymentAddr *[32]byte
865+
PaymentAddr fn.Option[[32]byte]
866866

867867
// PaymentRequest is an optional payment request that this payment is
868868
// attempting to complete.
@@ -1063,9 +1063,10 @@ func (r *ChannelRouter) PreparePayment(payment *LightningPayment) (
10631063
switch {
10641064
// If this is an AMP payment, we'll use the AMP shard tracker.
10651065
case payment.amp != nil:
1066+
addr := payment.PaymentAddr.UnwrapOr([32]byte{})
10661067
shardTracker = amp.NewShardTracker(
1067-
payment.amp.RootShare, payment.amp.SetID,
1068-
*payment.PaymentAddr, payment.Amount,
1068+
payment.amp.RootShare, payment.amp.SetID, addr,
1069+
payment.Amount,
10691070
)
10701071

10711072
// Otherwise we'll use the simple tracker that will map each attempt to
@@ -1367,8 +1368,8 @@ func (e ErrNoChannel) Error() string {
13671368
// outgoing channel, use the outgoingChan parameter.
13681369
func (r *ChannelRouter) BuildRoute(amt fn.Option[lnwire.MilliSatoshi],
13691370
hops []route.Vertex, outgoingChan *uint64, finalCltvDelta int32,
1370-
payAddr *[32]byte, firstHopBlob fn.Option[[]byte]) (*route.Route,
1371-
error) {
1371+
payAddr fn.Option[[32]byte], firstHopBlob fn.Option[[]byte]) (
1372+
*route.Route, error) {
13721373

13731374
log.Tracef("BuildRoute called: hopsCount=%v, amt=%v", len(hops), amt)
13741375

0 commit comments

Comments
 (0)