Skip to content

Commit 24f03b3

Browse files
committed
Fix a minor memory leak on PermanentFailure mon errs when sending
If we send a payment and fail to update the first-hop channel state with a `PermanentFailure` ChannelMonitorUpdateErr, we would have an entry in our pending payments map, but possibly not return the PaymentId back to the user to retry the payment, leading to a (rare and relatively minor) memory leak.
1 parent 2ed1ba6 commit 24f03b3

File tree

1 file changed

+22
-14
lines changed

1 file changed

+22
-14
lines changed

lightning/src/ln/channelmanager.rs

+22-14
Original file line numberDiff line numberDiff line change
@@ -2058,6 +2058,20 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20582058
Some(id) => id.clone(),
20592059
};
20602060

2061+
macro_rules! insert_payment_id {
2062+
() => {
2063+
let payment = payment_entry.or_insert_with(|| PendingOutboundPayment::Retryable {
2064+
session_privs: HashSet::new(),
2065+
pending_amt_msat: 0,
2066+
payment_hash: *payment_hash,
2067+
payment_secret: *payment_secret,
2068+
starting_block_height: self.best_block.read().unwrap().height(),
2069+
total_msat: total_value,
2070+
});
2071+
assert!(payment.insert(session_priv_bytes, path.last().unwrap().fee_msat));
2072+
}
2073+
}
2074+
20612075
let channel_state = &mut *channel_lock;
20622076
if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(id) {
20632077
match {
@@ -2067,7 +2081,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20672081
if !chan.get().is_live() {
20682082
return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!".to_owned()});
20692083
}
2070-
let send_res = break_chan_entry!(self, chan.get_mut().send_htlc_and_commit(
2084+
break_chan_entry!(self, chan.get_mut().send_htlc_and_commit(
20712085
htlc_msat, payment_hash.clone(), htlc_cltv, HTLCSource::OutboundRoute {
20722086
path: path.clone(),
20732087
session_priv: session_priv.clone(),
@@ -2076,19 +2090,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20762090
payment_secret: payment_secret.clone(),
20772091
payee: payee.clone(),
20782092
}, onion_packet, &self.logger),
2079-
channel_state, chan);
2080-
2081-
let payment = payment_entry.or_insert_with(|| PendingOutboundPayment::Retryable {
2082-
session_privs: HashSet::new(),
2083-
pending_amt_msat: 0,
2084-
payment_hash: *payment_hash,
2085-
payment_secret: *payment_secret,
2086-
starting_block_height: self.best_block.read().unwrap().height(),
2087-
total_msat: total_value,
2088-
});
2089-
assert!(payment.insert(session_priv_bytes, path.last().unwrap().fee_msat));
2093+
channel_state, chan)
20902094

2091-
send_res
20922095
} {
20932096
Some((update_add, commitment_signed, monitor_update)) => {
20942097
if let Err(e) = self.chain_monitor.update_channel(chan.get().get_funding_txo().unwrap(), monitor_update) {
@@ -2098,8 +2101,10 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20982101
// is restored. Therefore, we must return an error indicating that
20992102
// it is unsafe to retry the payment wholesale, which we do in the
21002103
// send_payment check for MonitorUpdateFailed, below.
2104+
insert_payment_id!(); // Only do this after possibly break'ing on Perm failure above.
21012105
return Err(APIError::MonitorUpdateFailed);
21022106
}
2107+
insert_payment_id!();
21032108

21042109
log_debug!(self.logger, "Sending payment along path resulted in a commitment_signed for channel {}", log_bytes!(chan.get().channel_id()));
21052110
channel_state.pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
@@ -2114,7 +2119,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
21142119
},
21152120
});
21162121
},
2117-
None => {},
2122+
None => { insert_payment_id!(); },
21182123
}
21192124
} else { unreachable!(); }
21202125
return Ok(());
@@ -2231,6 +2236,9 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
22312236
if has_err && has_ok {
22322237
Err(PaymentSendFailure::PartialFailure(results))
22332238
} else if has_err {
2239+
// If we failed to send any paths, we shouldn't have inserted the new PaymentId into
2240+
// our `pending_outbound_payments` map at all.
2241+
debug_assert!(self.pending_outbound_payments.lock().unwrap().get(&payment_id).is_none());
22342242
Err(PaymentSendFailure::AllFailedRetrySafe(results.drain(..).map(|r| r.unwrap_err()).collect()))
22352243
} else {
22362244
Ok(payment_id)

0 commit comments

Comments
 (0)