Skip to content

Commit 7dc86ac

Browse files
committed
multi: update PaymentAddr to use fn.Option
Since it is allowed to not be set and so can lead to nil deref panics if it is a pointer.
1 parent 84c91f7 commit 7dc86ac

File tree

14 files changed

+87
-70
lines changed

14 files changed

+87
-70
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

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

routing/router_test.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,14 +1653,14 @@ func TestBuildRoute(t *testing.T) {
16531653
// Test that we can't build a route when no hops are given.
16541654
hops = []route.Vertex{}
16551655
_, err = ctx.router.BuildRoute(
1656-
noAmt, hops, nil, 40, nil, fn.None[[]byte](),
1656+
noAmt, hops, nil, 40, fn.None[[32]byte](), fn.None[[]byte](),
16571657
)
16581658
require.Error(t, err)
16591659

16601660
// Create hop list for an unknown destination.
16611661
hops := []route.Vertex{ctx.aliases["b"], ctx.aliases["y"]}
16621662
_, err = ctx.router.BuildRoute(
1663-
noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](),
1663+
noAmt, hops, nil, 40, fn.Some(payAddr), fn.None[[]byte](),
16641664
)
16651665
noChanErr := ErrNoChannel{}
16661666
require.ErrorAs(t, err, &noChanErr)
@@ -1672,7 +1672,8 @@ func TestBuildRoute(t *testing.T) {
16721672

16731673
// Build the route for the given amount.
16741674
rt, err := ctx.router.BuildRoute(
1675-
fn.Some(amt), hops, nil, 40, &payAddr, fn.None[[]byte](),
1675+
fn.Some(amt), hops, nil, 40, fn.Some(payAddr),
1676+
fn.None[[]byte](),
16761677
)
16771678
require.NoError(t, err)
16781679

@@ -1684,7 +1685,7 @@ func TestBuildRoute(t *testing.T) {
16841685

16851686
// Build the route for the minimum amount.
16861687
rt, err = ctx.router.BuildRoute(
1687-
noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](),
1688+
noAmt, hops, nil, 40, fn.Some(payAddr), fn.None[[]byte](),
16881689
)
16891690
require.NoError(t, err)
16901691

@@ -1702,7 +1703,7 @@ func TestBuildRoute(t *testing.T) {
17021703
// There is no amount that can pass through both channel 5 and 4.
17031704
hops = []route.Vertex{ctx.aliases["e"], ctx.aliases["c"]}
17041705
_, err = ctx.router.BuildRoute(
1705-
noAmt, hops, nil, 40, nil, fn.None[[]byte](),
1706+
noAmt, hops, nil, 40, fn.None[[32]byte](), fn.None[[]byte](),
17061707
)
17071708
require.Error(t, err)
17081709
noChanErr = ErrNoChannel{}
@@ -1722,7 +1723,7 @@ func TestBuildRoute(t *testing.T) {
17221723
// policy of channel 3.
17231724
hops = []route.Vertex{ctx.aliases["b"], ctx.aliases["z"]}
17241725
rt, err = ctx.router.BuildRoute(
1725-
noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](),
1726+
noAmt, hops, nil, 40, fn.Some(payAddr), fn.None[[]byte](),
17261727
)
17271728
require.NoError(t, err)
17281729
checkHops(rt, []uint64{1, 8}, payAddr)
@@ -1736,7 +1737,8 @@ func TestBuildRoute(t *testing.T) {
17361737
hops = []route.Vertex{ctx.aliases["d"], ctx.aliases["f"]}
17371738
amt = lnwire.NewMSatFromSatoshis(100)
17381739
rt, err = ctx.router.BuildRoute(
1739-
fn.Some(amt), hops, nil, 40, &payAddr, fn.None[[]byte](),
1740+
fn.Some(amt), hops, nil, 40, fn.Some(payAddr),
1741+
fn.None[[]byte](),
17401742
)
17411743
require.NoError(t, err)
17421744
checkHops(rt, []uint64{9, 10}, payAddr)
@@ -1752,7 +1754,7 @@ func TestBuildRoute(t *testing.T) {
17521754
// is a third pass through newRoute in which this gets corrected to end
17531755
hops = []route.Vertex{ctx.aliases["d"], ctx.aliases["f"]}
17541756
rt, err = ctx.router.BuildRoute(
1755-
noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](),
1757+
noAmt, hops, nil, 40, fn.Some(payAddr), fn.None[[]byte](),
17561758
)
17571759
require.NoError(t, err)
17581760
checkHops(rt, []uint64{9, 10}, payAddr)

rpcserver.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5321,7 +5321,7 @@ type rpcPaymentIntent struct {
53215321
outgoingChannelIDs []uint64
53225322
lastHop *route.Vertex
53235323
destFeatures *lnwire.FeatureVector
5324-
paymentAddr *[32]byte
5324+
paymentAddr fn.Option[[32]byte]
53255325
payReq []byte
53265326
metadata []byte
53275327
blindedPathSet *routing.BlindedPaymentPathSet
@@ -5530,8 +5530,9 @@ func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPayme
55305530
// Note that the payment address for the payIntent should be nil if none
55315531
// was provided with the rpcPaymentRequest.
55325532
if len(rpcPayReq.PaymentAddr) != 0 {
5533-
payIntent.paymentAddr = &[32]byte{}
5534-
copy(payIntent.paymentAddr[:], rpcPayReq.PaymentAddr)
5533+
var addr [32]byte
5534+
copy(addr[:], rpcPayReq.PaymentAddr)
5535+
payIntent.paymentAddr = fn.Some(addr)
55355536
}
55365537

55375538
// Otherwise, If the payment request field was not specified
@@ -7380,10 +7381,7 @@ func (r *rpcServer) DecodePayReq(ctx context.Context,
73807381
}
73817382

73827383
// Extract the payment address from the payment request, if present.
7383-
var paymentAddr []byte
7384-
if payReq.PaymentAddr != nil {
7385-
paymentAddr = payReq.PaymentAddr[:]
7386-
}
7384+
paymentAddr := payReq.PaymentAddr.UnwrapOr([32]byte{})
73877385

73887386
dest := payReq.Destination.SerializeCompressed()
73897387
return &lnrpc.PayReq{
@@ -7399,7 +7397,7 @@ func (r *rpcServer) DecodePayReq(ctx context.Context,
73997397
CltvExpiry: int64(payReq.MinFinalCLTVExpiry()),
74007398
RouteHints: routeHints,
74017399
BlindedPaths: blindedPaymentPaths,
7402-
PaymentAddr: paymentAddr,
7400+
PaymentAddr: paymentAddr[:],
74037401
Features: invoicesrpc.CreateRPCFeatures(payReq.Features),
74047402
}, nil
74057403
}

0 commit comments

Comments
 (0)