Skip to content

Commit 0c26481

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 6f002ea commit 0c26481

File tree

1 file changed

+23
-15
lines changed

1 file changed

+23
-15
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2075,6 +2075,21 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20752075
Some(id) => id.clone(),
20762076
};
20772077

2078+
macro_rules! insert_payment_id {
2079+
() => {
2080+
let payment = payment_entry.or_insert_with(|| PendingOutboundPayment::Retryable {
2081+
session_privs: HashSet::new(),
2082+
pending_amt_msat: 0,
2083+
pending_fee_msat: Some(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));
2090+
}
2091+
}
2092+
20782093
let channel_state = &mut *channel_lock;
20792094
if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(id) {
20802095
match {
@@ -2084,7 +2099,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20842099
if !chan.get().is_live() {
20852100
return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!".to_owned()});
20862101
}
2087-
let send_res = break_chan_entry!(self, chan.get_mut().send_htlc_and_commit(
2102+
break_chan_entry!(self, chan.get_mut().send_htlc_and_commit(
20882103
htlc_msat, payment_hash.clone(), htlc_cltv, HTLCSource::OutboundRoute {
20892104
path: path.clone(),
20902105
session_priv: session_priv.clone(),
@@ -2093,20 +2108,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20932108
payment_secret: payment_secret.clone(),
20942109
payee: payee.clone(),
20952110
}, onion_packet, &self.logger),
2096-
channel_state, chan);
2097-
2098-
let payment = payment_entry.or_insert_with(|| PendingOutboundPayment::Retryable {
2099-
session_privs: HashSet::new(),
2100-
pending_amt_msat: 0,
2101-
pending_fee_msat: Some(0),
2102-
payment_hash: *payment_hash,
2103-
payment_secret: *payment_secret,
2104-
starting_block_height: self.best_block.read().unwrap().height(),
2105-
total_msat: total_value,
2106-
});
2107-
assert!(payment.insert(session_priv_bytes, path));
2111+
channel_state, chan)
21082112

2109-
send_res
21102113
} {
21112114
Some((update_add, commitment_signed, monitor_update)) => {
21122115
if let Err(e) = self.chain_monitor.update_channel(chan.get().get_funding_txo().unwrap(), monitor_update) {
@@ -2116,8 +2119,10 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
21162119
// is restored. Therefore, we must return an error indicating that
21172120
// it is unsafe to retry the payment wholesale, which we do in the
21182121
// send_payment check for MonitorUpdateFailed, below.
2122+
insert_payment_id!(); // Only do this after possibly break'ing on Perm failure above.
21192123
return Err(APIError::MonitorUpdateFailed);
21202124
}
2125+
insert_payment_id!();
21212126

21222127
log_debug!(self.logger, "Sending payment along path resulted in a commitment_signed for channel {}", log_bytes!(chan.get().channel_id()));
21232128
channel_state.pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
@@ -2132,7 +2137,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
21322137
},
21332138
});
21342139
},
2135-
None => {},
2140+
None => { insert_payment_id!(); },
21362141
}
21372142
} else { unreachable!(); }
21382143
return Ok(());
@@ -2249,6 +2254,9 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
22492254
if has_err && has_ok {
22502255
Err(PaymentSendFailure::PartialFailure(results))
22512256
} else if has_err {
2257+
// If we failed to send any paths, we shouldn't have inserted the new PaymentId into
2258+
// our `pending_outbound_payments` map at all.
2259+
debug_assert!(self.pending_outbound_payments.lock().unwrap().get(&payment_id).is_none());
22522260
Err(PaymentSendFailure::AllFailedRetrySafe(results.drain(..).map(|r| r.unwrap_err()).collect()))
22532261
} else {
22542262
Ok(payment_id)

0 commit comments

Comments
 (0)