Skip to content

Conversation

@TheBlueMatt
Copy link
Collaborator

I'm getting some fuzzing iterations in for 0.0.12, and sadly found a few small things.

The full_stack_target managed to find a bug where, if we receive
a funding_created message which has a channel_id identical to an
existing channel, we'll end up
(a) having the monitor update for the new channel fail (due to
duplicate outpoint),
(b) creating a monitor update for the new channel as we
force-close it,
(c) panicing due to the force-close monitor update is applied to
the original channel and is considered out-of-order.

Obviously we shouldn't be creating a force-close monitor update for
a channel which can never appear on chain, so we do that here and
add a test which previously failed and checks a few
duplicate-channel-id cases.

@TheBlueMatt TheBlueMatt added this to the 0.0.12 milestone Nov 14, 2020
@codecov
Copy link

codecov bot commented Nov 14, 2020

Codecov Report

Merging #750 (58eeb6b) into main (8f10a1d) will decrease coverage by 0.11%.
The diff coverage is 89.43%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #750      +/-   ##
==========================================
- Coverage   91.44%   91.32%   -0.12%     
==========================================
  Files          37       37              
  Lines       22249    22383     +134     
==========================================
+ Hits        20346    20442      +96     
- Misses       1903     1941      +38     
Impacted Files Coverage Δ
lightning/src/chain/chainmonitor.rs 94.66% <0.00%> (+2.66%) ⬆️
lightning/src/ln/chan_utils.rs 95.66% <0.00%> (-0.31%) ⬇️
lightning/src/ln/msgs.rs 89.60% <0.00%> (-0.48%) ⬇️
lightning/src/routing/router.rs 95.71% <0.00%> (-0.96%) ⬇️
lightning/src/util/events.rs 24.54% <0.00%> (ø)
lightning/src/ln/channelmanager.rs 85.02% <75.00%> (-0.34%) ⬇️
lightning/src/ln/functional_tests.rs 96.92% <94.89%> (-0.23%) ⬇️
lightning/src/chain/channelmonitor.rs 95.44% <100.00%> (ø)
lightning/src/ln/channel.rs 87.45% <100.00%> (+0.02%) ⬆️
lightning/src/ln/functional_test_utils.rs 95.13% <100.00%> (+0.02%) ⬆️
... and 5 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8f10a1d...36063ee. Read the comment docs.

@TheBlueMatt TheBlueMatt force-pushed the 2020-11-dup-chan-id-crash branch from 795b46e to 96f6124 Compare November 15, 2020 21:57
@TheBlueMatt
Copy link
Collaborator Author

full_stack_target found a second, very related bug, so went ahead and fixed that too.

Copy link
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to get these bugfixes in 🌴

// user could use this object with some proxying in between which makes this
// possible, but in tests and fuzzing, this should be a panic.
#[cfg(any(test, feature = "fuzztarget"))]
panic!();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, add panic error string and error logs if a user manages to hit this (because even though they can hit this as a proxy, iiuc they shouldn't)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do hope no user is building with feature = fuzztarget! But, added a message in case we hit it while writing tests.

self.internal_funding_transaction_generated(temporary_channel_id, funding_txo, true);
}
#[cfg(test)]
pub(crate) fn funding_transaction_generated_dup_funding_id(&self, temporary_channel_id: &[u8; 32], funding_txo: OutPoint) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than adding this function, I prefer this approach:

diff --git a/lightning/src/ln/general_unit_tests.rs b/lightning/src/ln/general_unit_tests.rs
index 15fdf1f0..b13eee6f 100644
--- a/lightning/src/ln/general_unit_tests.rs
+++ b/lightning/src/ln/general_unit_tests.rs
@@ -569,7 +569,8 @@ fn test_duplicate_chan_id() {
        }
        let funding_signed_msg = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id());

-       let channel_id = ::chain::transaction::OutPoint { txid: funding_created_msg.funding_txid, index: funding_created_msg.funding_output_index }.to_channel_id();
+       let outpoint = ::chain::transaction::OutPoint { txid: funding_created_msg.funding_txid, index: funding_created_msg.funding_output_index };
+       let channel_id = outpoint.to_channel_id();

        // Now we have the first channel past funding_created (ie it has a txid-based channel_id, not a
        // temporary one).
@@ -600,9 +601,14 @@ fn test_duplicate_chan_id() {
        nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), InitFeatures::known(), &get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id()));
        create_funding_transaction(&nodes[0], 100000, 42); // Get and check the FundingGenerationReady event

-       nodes[0].node.funding_transaction_generated_dup_funding_id(&open_chan_2_msg.temporary_channel_id, funding_output);
+       let funding_created = {
+               let mut a_channel_lock = nodes[0].node.channel_state.lock().unwrap();
+               let mut as_chan = a_channel_lock.by_id.get_mut(&open_chan_2_msg.temporary_channel_id).unwrap();
+               let logger = test_utils::TestLogger::new();
+               as_chan.get_outbound_funding_created(outpoint, &&logger).unwrap()
+       };
        check_added_monitors!(nodes[0], 0);
-       nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id()));
+       nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created);
        // At this point we'll try to add a duplicate channel monitor, which will be rejected, but
        // still needs to be cleared here.
        check_added_monitors!(nodes[1], 1);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nice! I admit I spent too long trying to manually build the message signature, didn't realize our internal method here already made it easy.

Copy link
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

last 2 comments

@TheBlueMatt TheBlueMatt force-pushed the 2020-11-dup-chan-id-crash branch 6 times, most recently from db98df6 to 22dbcbf Compare November 23, 2020 18:39
@TheBlueMatt TheBlueMatt force-pushed the 2020-11-dup-chan-id-crash branch from 22dbcbf to eb6b27f Compare November 23, 2020 18:51
@TheBlueMatt
Copy link
Collaborator Author

Squashed with no diff.

@jkczyz jkczyz self-requested a review November 23, 2020 20:56
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None).unwrap();
let open_chan_2_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &open_chan_2_msg);
nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), InitFeatures::known(), &get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider making a local variable for the AcceptChannel message for consistency with how it was done earlier.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! I had previously moved to only storing as a variable if we need to modify it and resend. I just missed that we no longer modify+resend the previous one.

The full_stack_target managed to find a bug where, if we receive
a funding_created message which has a channel_id identical to an
existing channel, we'll end up
 (a) having the monitor update for the new channel fail (due to
     duplicate outpoint),
 (b) creating a monitor update for the new channel as we
     force-close it,
 (c) panicing due to the force-close monitor update is applied to
     the original channel and is considered out-of-order.

Obviously we shouldn't be creating a force-close monitor update for
a channel which can never appear on chain, so we do that here and
add a test which previously failed and checks a few
duplicate-channel-id cases.
Like the previous commit for channel-closed monitor updates for
inbound channels during processing of a funding_created message,
this resolves a more general issue for closing outbound channels
which have sent a funding_created but not yet received a
funding_signed.

This issue was also detected by full_stack_target.

To make similar issues easier to detect in testing and fuzzing, an
additional assertion is added to panic on updates to a channel
monitor before registering it.
@TheBlueMatt TheBlueMatt force-pushed the 2020-11-dup-chan-id-crash branch from eb6b27f to 36063ee Compare November 23, 2020 22:00
@TheBlueMatt
Copy link
Collaborator Author

Only change was to make the two handle_accept_channel calls identical.

@TheBlueMatt TheBlueMatt merged commit 3c4a0c1 into lightningdevkit:main Nov 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants