loopd: nil-guard reservation/instant-out RPC handlers

When loopd is started without --experimental the swap client server's
reservationManager and instantOutManager are nil. ListReservations
already returns codes.Unimplemented in that case; the rest of the
instant-out / reservation RPC family didn't, and would dereference a
nil pointer.

Affected handlers (all of which now return the same Unimplemented
status):

  - ReservationRequest (new in PR #883)
  - ReservationQuote   (new in PR #883)
  - InstantOut
  - InstantOutQuote
  - ListInstantOuts

Without this fix an authenticated caller can crash the daemon by
invoking any of these RPCs against a non-experimental loopd. With
default localhost binding the attack surface is small, but loop is
also commonly fronted by lit / LSP wrappers that expose RPCs to other
internal services, so a single packet is enough for a remote DoS.
This commit is contained in:
Slyghtning 2026-05-11 15:22:05 +02:00
parent 56be61c31d
commit e4c4e11627
No known key found for this signature in database
GPG key ID: F82D456EA023C9BF

View file

@ -1743,6 +1743,11 @@ func (s *swapClientServer) ReservationRequest(ctx context.Context,
req *looprpc.ReservationRequestRequest) (
*looprpc.ReservationRequestResponse, error) {
if s.reservationManager == nil {
return nil, status.Error(codes.Unimplemented,
"Restart loop with --experimental")
}
reservation, err := s.reservationManager.RequestReservationFromServer(
ctx, btcutil.Amount(req.Amt), req.Expiry,
btcutil.Amount(req.MaxPrepayAmt),
@ -1755,10 +1760,16 @@ func (s *swapClientServer) ReservationRequest(ctx context.Context,
Reservation: toClientReservation(reservation),
}, nil
}
func (s *swapClientServer) ReservationQuote(ctx context.Context,
req *looprpc.ReservationQuoteRequest) (
*looprpc.ReservationQuoteResponse, error) {
if s.reservationManager == nil {
return nil, status.Error(codes.Unimplemented,
"Restart loop with --experimental")
}
quote, err := s.reservationManager.QuoteReservation(
ctx, btcutil.Amount(req.Amt), req.Expiry,
)