Fix various issues found in full_stack_target fuzzing - #2808

Merged
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1
Jan 8, 2024
Merged

Fix various issues found in full_stack_target fuzzing#2808
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

#2804 reported a message-processing-reachable unwrap, which is concerning, but more generally it reported a serious coverage gap in our full_stack_target fuzzer. This fuzzer should be our first line of defense against such issues, but sadly was using a predefined config object which left us not testing cases that required specific config flags. This fixes#2804 (in an imo cleaner way than #2805, at least for downstream devs even if not in LDK) as well as a number of other quite minor debug and overflow issues found after I updated the full_stack_target to test different config flags.

If we get a `Feature` object which has excess zero bytes, we
shouldn't consider it a different `Feature` from another with the
same bits set, but no excess zero bytes. Here we fix both the
`Hash` and `PartialEq` implementation for `Features` to ignore
excess zero bytes.
When we or our counterparty are updating the fees on the channel,
we currently check that the resulting balance is sufficient not
only to meet the reserve threshold, but also not push it below
dust. This isn't required in the BOLTs and may lead to spurious
force-closures (which would be a bit safer, but reserve should
always exceed the dust threshold).
Worse, the current logic is broken - it compares the output value
in *billionths of satoshis* to the dust limit in satoshis. Thus,
the code is borderline dead anyway, but can overflow for channels
with several million Bitcoin, causing the fuzzer to get mad (and
lead to spurious force-closures for few-billion-dollar channels).
When contest delays are >= 0x8000, script pushes require an extra
byte to avoid being interpreted as a negative int. Thus, for
channels with CSV delays longer than ~7.5 months we may generate
transactions with slightly too little fee. This isn't really a huge
deal, but we should prefer to be conservative here, and slightly
too high fee in the general case is better than slightly too little
fee in other cases.
If we try to open a channel with a peer that is disconnected (but
with which we have some other channels), we'll end up with an
unfunded channel which will lead to a panic when the peer
reconnects. Here we drop this debug assertion without bother to add
a new test, given this behavior will change in a PR very soon.
If a peer provides a feerate which nears `u32::MAX`, we may
overflow calculating the dust buffer feerate, leading to spuriously
keeping non-anchor channels open when they should be force-closed.
@codecov-commenter

codecov-commenter commented Dec 29, 2023

Copy link
Copy Markdown

Codecov Report

Attention: 5 lines in your changes are missing coverage. Please review.

Comparison is base (4deb263) 88.49% compared to head (bceca1e) 88.73%.
Report is 14 commits behind head on main.

❗ Current head bceca1e differs from pull request most recent head 7f24e83. Consider uploading reports for the commit 7f24e83 to get more accurate results

FilesPatch %Lines
lightning/src/ln/channel.rs88.57%4 Missing ⚠️
lightning/src/ln/features.rs96.55%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #2808 +/- ##
==========================================
+ Coverage 88.49% 88.73% +0.23% 
==========================================
Files 114 114 Lines 91935 95699 +3764 Branches 91935 95699 +3764 ==========================================
+ Hits 81359 84914 +3555 - Misses 8097 8336 +239 + Partials 2479 2449 -30 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One question, otherwise LGTM.

Comment threadlightning/src/ln/channel.rs Outdated
} else {
let channel_type = ChannelTypeFeatures::from_init(&their_features);
if channel_type != ChannelTypeFeatures::only_static_remote_key() {
if channel_type != channel_type_from_open_channel(msg) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This change is a bit confusing to me: why make it look like we'd consider msg after all, when in fact it would always result in ChannelTypeFeatures::only_static_remote_key()? Leaving it as-is seems more readable?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Yea, I was trying to keep from making the "default channel type is only-static-remote-key" assumption copied in several places, but I agree this ended up being more awkward than not. Instead I moved the whole "what is the channel type given the message" thing into the new method and made it fallible. This no longer directly solves the issue, but rather actually handling the failures does, which is also kinda nice in that we will avoid giving users an event for a channel we can't use.

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The changes look great! I have one small question and two suggestions for the comments.

Comment threadlightning/src/ln/chan_utils.rs
@@ -1876,7 +1872,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
if let Some(feerate) = outbound_feerate_update {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the description above it is written:

// may, at any point, increase _by_ at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
// whichever is higher.

which implies:

cmp::max(feerate_by_kw + 2530, feerate_plus_quarter)

Whereas, the line should read:

pub fn get_dust_buffer_feerate(&self, outbound_feerate_update: Option<u32>) -> u32 {
// When calculating our exposure to dust HTLCs, we assume that the channel feerate
- // may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,+ // may, at any point, increase to at least 10 sat/vB (i.e 2530 sat/kWU) or by 25%,
// whichever is higher. This ensures that we aren't suddenly exposed to significantly

Since we are touching this function in this PR. I believe we should also address this small but significant comment change.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh, good catch! I'd like to actually address this by making the code match the comment, not the other way around, so I'll address this in a later PR - #2815

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cool!

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/ln/chan_utils.rs
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed.

tnull
tnull previously approved these changes Jan 8, 2024

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

One nit, but feel free to land as is.

Comment threadlightning/src/ln/channelmanager.rs Outdated

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ACK
The changes look great, and since the comment will be addressed in a follow-up PR, I believe this PR is good to go!

If we receive an `OpenChannel` message without a `channel_type`
with `manually_accept_inbound_channels` set, we will `unwrap()`
`None`.
This is uncommon these days as most nodes support `channel_type`,
but sadly is rather trivial for a peer to hit for those with manual
channel acceptance enabled.
Reported in and fixeslightningdevkit#2804. Luckily, the updated
`full_stack_target` has no issue reaching this issue quickly.
@TheBlueMatt
TheBlueMattforce-pushed the 2023-12-fuzzing-fixes-1 branch from dea37db to 7f24e83CompareJanuary 8, 2024 18:20
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed the nit:

$ git diff-tree -U1 bceca1eed7aced011150f0f6ca7f0dd6fbe71d6a 7f24e833
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 5efef2e90..8e0ac2fdf 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -6172,10 +6172,7 @@ where
if self.default_configuration.manually_accept_inbound_channels {
- let channel_type_res = channel::channel_type_from_open_channel(- &msg, &peer_state.latest_features, &self.channel_type_features()- );- let channel_type = match channel_type_res {- Ok(channel_type) => channel_type,- Err(e) =>- return Err(MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)),- };+ let channel_type = channel::channel_type_from_open_channel(+ &msg, &peer_state.latest_features, &self.channel_type_features()+ ).map_err(|e|+ MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)+ )?;
let mut pending_events = self.pending_events.lock().unwrap();

@TheBlueMatt
TheBlueMatt merged commit 3fbee85 into lightningdevkit:mainJan 8, 2024
Sign up for freeto 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.

Remove panic when inbound peer sends OpenChannelRequest with no type specified and node is manually accepting requests

5 participants

@TheBlueMatt@codecov-commenter@tnull@wpaulino@shaavan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Fix various issues found in full_stack_target fuzzing - #2808

Merged
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1
Jan 8, 2024
Merged

Fix various issues found in full_stack_target fuzzing#2808
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

#2804 reported a message-processing-reachable unwrap, which is concerning, but more generally it reported a serious coverage gap in our full_stack_target fuzzer. This fuzzer should be our first line of defense against such issues, but sadly was using a predefined config object which left us not testing cases that required specific config flags. This fixes#2804 (in an imo cleaner way than #2805, at least for downstream devs even if not in LDK) as well as a number of other quite minor debug and overflow issues found after I updated the full_stack_target to test different config flags.

If we get a `Feature` object which has excess zero bytes, we
shouldn't consider it a different `Feature` from another with the
same bits set, but no excess zero bytes. Here we fix both the
`Hash` and `PartialEq` implementation for `Features` to ignore
excess zero bytes.
When we or our counterparty are updating the fees on the channel,
we currently check that the resulting balance is sufficient not
only to meet the reserve threshold, but also not push it below
dust. This isn't required in the BOLTs and may lead to spurious
force-closures (which would be a bit safer, but reserve should
always exceed the dust threshold).
Worse, the current logic is broken - it compares the output value
in *billionths of satoshis* to the dust limit in satoshis. Thus,
the code is borderline dead anyway, but can overflow for channels
with several million Bitcoin, causing the fuzzer to get mad (and
lead to spurious force-closures for few-billion-dollar channels).
When contest delays are >= 0x8000, script pushes require an extra
byte to avoid being interpreted as a negative int. Thus, for
channels with CSV delays longer than ~7.5 months we may generate
transactions with slightly too little fee. This isn't really a huge
deal, but we should prefer to be conservative here, and slightly
too high fee in the general case is better than slightly too little
fee in other cases.
If we try to open a channel with a peer that is disconnected (but
with which we have some other channels), we'll end up with an
unfunded channel which will lead to a panic when the peer
reconnects. Here we drop this debug assertion without bother to add
a new test, given this behavior will change in a PR very soon.
If a peer provides a feerate which nears `u32::MAX`, we may
overflow calculating the dust buffer feerate, leading to spuriously
keeping non-anchor channels open when they should be force-closed.
@codecov-commenter

codecov-commenter commented Dec 29, 2023

Copy link
Copy Markdown

Codecov Report

Attention: 5 lines in your changes are missing coverage. Please review.

Comparison is base (4deb263) 88.49% compared to head (bceca1e) 88.73%.
Report is 14 commits behind head on main.

❗ Current head bceca1e differs from pull request most recent head 7f24e83. Consider uploading reports for the commit 7f24e83 to get more accurate results

FilesPatch %Lines
lightning/src/ln/channel.rs88.57%4 Missing ⚠️
lightning/src/ln/features.rs96.55%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #2808 +/- ##
==========================================
+ Coverage 88.49% 88.73% +0.23% 
==========================================
Files 114 114 Lines 91935 95699 +3764 Branches 91935 95699 +3764 ==========================================
+ Hits 81359 84914 +3555 - Misses 8097 8336 +239 + Partials 2479 2449 -30 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One question, otherwise LGTM.

Comment threadlightning/src/ln/channel.rs Outdated
} else {
let channel_type = ChannelTypeFeatures::from_init(&their_features);
if channel_type != ChannelTypeFeatures::only_static_remote_key() {
if channel_type != channel_type_from_open_channel(msg) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This change is a bit confusing to me: why make it look like we'd consider msg after all, when in fact it would always result in ChannelTypeFeatures::only_static_remote_key()? Leaving it as-is seems more readable?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Yea, I was trying to keep from making the "default channel type is only-static-remote-key" assumption copied in several places, but I agree this ended up being more awkward than not. Instead I moved the whole "what is the channel type given the message" thing into the new method and made it fallible. This no longer directly solves the issue, but rather actually handling the failures does, which is also kinda nice in that we will avoid giving users an event for a channel we can't use.

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The changes look great! I have one small question and two suggestions for the comments.

Comment threadlightning/src/ln/chan_utils.rs
@@ -1876,7 +1872,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
if let Some(feerate) = outbound_feerate_update {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the description above it is written:

// may, at any point, increase _by_ at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
// whichever is higher.

which implies:

cmp::max(feerate_by_kw + 2530, feerate_plus_quarter)

Whereas, the line should read:

pub fn get_dust_buffer_feerate(&self, outbound_feerate_update: Option<u32>) -> u32 {
// When calculating our exposure to dust HTLCs, we assume that the channel feerate
- // may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,+ // may, at any point, increase to at least 10 sat/vB (i.e 2530 sat/kWU) or by 25%,
// whichever is higher. This ensures that we aren't suddenly exposed to significantly

Since we are touching this function in this PR. I believe we should also address this small but significant comment change.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh, good catch! I'd like to actually address this by making the code match the comment, not the other way around, so I'll address this in a later PR - #2815

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cool!

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/ln/chan_utils.rs
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed.

tnull
tnull previously approved these changes Jan 8, 2024

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

One nit, but feel free to land as is.

Comment threadlightning/src/ln/channelmanager.rs Outdated

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ACK
The changes look great, and since the comment will be addressed in a follow-up PR, I believe this PR is good to go!

If we receive an `OpenChannel` message without a `channel_type`
with `manually_accept_inbound_channels` set, we will `unwrap()`
`None`.
This is uncommon these days as most nodes support `channel_type`,
but sadly is rather trivial for a peer to hit for those with manual
channel acceptance enabled.
Reported in and fixeslightningdevkit#2804. Luckily, the updated
`full_stack_target` has no issue reaching this issue quickly.
@TheBlueMatt
TheBlueMattforce-pushed the 2023-12-fuzzing-fixes-1 branch from dea37db to 7f24e83CompareJanuary 8, 2024 18:20
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed the nit:

$ git diff-tree -U1 bceca1eed7aced011150f0f6ca7f0dd6fbe71d6a 7f24e833
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 5efef2e90..8e0ac2fdf 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -6172,10 +6172,7 @@ where
if self.default_configuration.manually_accept_inbound_channels {
- let channel_type_res = channel::channel_type_from_open_channel(- &msg, &peer_state.latest_features, &self.channel_type_features()- );- let channel_type = match channel_type_res {- Ok(channel_type) => channel_type,- Err(e) =>- return Err(MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)),- };+ let channel_type = channel::channel_type_from_open_channel(+ &msg, &peer_state.latest_features, &self.channel_type_features()+ ).map_err(|e|+ MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)+ )?;
let mut pending_events = self.pending_events.lock().unwrap();

@TheBlueMatt
TheBlueMatt merged commit 3fbee85 into lightningdevkit:mainJan 8, 2024
Sign up for freeto 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.

Remove panic when inbound peer sends OpenChannelRequest with no type specified and node is manually accepting requests

5 participants

@TheBlueMatt@codecov-commenter@tnull@wpaulino@shaavan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix various issues found in full_stack_target fuzzing - #2808

Merged
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1
Jan 8, 2024
Merged

Fix various issues found in full_stack_target fuzzing#2808
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

#2804 reported a message-processing-reachable unwrap, which is concerning, but more generally it reported a serious coverage gap in our full_stack_target fuzzer. This fuzzer should be our first line of defense against such issues, but sadly was using a predefined config object which left us not testing cases that required specific config flags. This fixes#2804 (in an imo cleaner way than #2805, at least for downstream devs even if not in LDK) as well as a number of other quite minor debug and overflow issues found after I updated the full_stack_target to test different config flags.

If we get a `Feature` object which has excess zero bytes, we
shouldn't consider it a different `Feature` from another with the
same bits set, but no excess zero bytes. Here we fix both the
`Hash` and `PartialEq` implementation for `Features` to ignore
excess zero bytes.
When we or our counterparty are updating the fees on the channel,
we currently check that the resulting balance is sufficient not
only to meet the reserve threshold, but also not push it below
dust. This isn't required in the BOLTs and may lead to spurious
force-closures (which would be a bit safer, but reserve should
always exceed the dust threshold).
Worse, the current logic is broken - it compares the output value
in *billionths of satoshis* to the dust limit in satoshis. Thus,
the code is borderline dead anyway, but can overflow for channels
with several million Bitcoin, causing the fuzzer to get mad (and
lead to spurious force-closures for few-billion-dollar channels).
When contest delays are >= 0x8000, script pushes require an extra
byte to avoid being interpreted as a negative int. Thus, for
channels with CSV delays longer than ~7.5 months we may generate
transactions with slightly too little fee. This isn't really a huge
deal, but we should prefer to be conservative here, and slightly
too high fee in the general case is better than slightly too little
fee in other cases.
If we try to open a channel with a peer that is disconnected (but
with which we have some other channels), we'll end up with an
unfunded channel which will lead to a panic when the peer
reconnects. Here we drop this debug assertion without bother to add
a new test, given this behavior will change in a PR very soon.
If a peer provides a feerate which nears `u32::MAX`, we may
overflow calculating the dust buffer feerate, leading to spuriously
keeping non-anchor channels open when they should be force-closed.
@codecov-commenter

codecov-commenter commented Dec 29, 2023

Copy link
Copy Markdown

Codecov Report

Attention: 5 lines in your changes are missing coverage. Please review.

Comparison is base (4deb263) 88.49% compared to head (bceca1e) 88.73%.
Report is 14 commits behind head on main.

❗ Current head bceca1e differs from pull request most recent head 7f24e83. Consider uploading reports for the commit 7f24e83 to get more accurate results

FilesPatch %Lines
lightning/src/ln/channel.rs88.57%4 Missing ⚠️
lightning/src/ln/features.rs96.55%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #2808 +/- ##
==========================================
+ Coverage 88.49% 88.73% +0.23% 
==========================================
Files 114 114 Lines 91935 95699 +3764 Branches 91935 95699 +3764 ==========================================
+ Hits 81359 84914 +3555 - Misses 8097 8336 +239 + Partials 2479 2449 -30 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One question, otherwise LGTM.

Comment threadlightning/src/ln/channel.rs Outdated
} else {
let channel_type = ChannelTypeFeatures::from_init(&their_features);
if channel_type != ChannelTypeFeatures::only_static_remote_key() {
if channel_type != channel_type_from_open_channel(msg) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This change is a bit confusing to me: why make it look like we'd consider msg after all, when in fact it would always result in ChannelTypeFeatures::only_static_remote_key()? Leaving it as-is seems more readable?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Yea, I was trying to keep from making the "default channel type is only-static-remote-key" assumption copied in several places, but I agree this ended up being more awkward than not. Instead I moved the whole "what is the channel type given the message" thing into the new method and made it fallible. This no longer directly solves the issue, but rather actually handling the failures does, which is also kinda nice in that we will avoid giving users an event for a channel we can't use.

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The changes look great! I have one small question and two suggestions for the comments.

Comment threadlightning/src/ln/chan_utils.rs
@@ -1876,7 +1872,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
if let Some(feerate) = outbound_feerate_update {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the description above it is written:

// may, at any point, increase _by_ at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
// whichever is higher.

which implies:

cmp::max(feerate_by_kw + 2530, feerate_plus_quarter)

Whereas, the line should read:

pub fn get_dust_buffer_feerate(&self, outbound_feerate_update: Option<u32>) -> u32 {
// When calculating our exposure to dust HTLCs, we assume that the channel feerate
- // may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,+ // may, at any point, increase to at least 10 sat/vB (i.e 2530 sat/kWU) or by 25%,
// whichever is higher. This ensures that we aren't suddenly exposed to significantly

Since we are touching this function in this PR. I believe we should also address this small but significant comment change.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh, good catch! I'd like to actually address this by making the code match the comment, not the other way around, so I'll address this in a later PR - #2815

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cool!

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/ln/chan_utils.rs
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed.

tnull
tnull previously approved these changes Jan 8, 2024

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

One nit, but feel free to land as is.

Comment threadlightning/src/ln/channelmanager.rs Outdated

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ACK
The changes look great, and since the comment will be addressed in a follow-up PR, I believe this PR is good to go!

If we receive an `OpenChannel` message without a `channel_type`
with `manually_accept_inbound_channels` set, we will `unwrap()`
`None`.
This is uncommon these days as most nodes support `channel_type`,
but sadly is rather trivial for a peer to hit for those with manual
channel acceptance enabled.
Reported in and fixeslightningdevkit#2804. Luckily, the updated
`full_stack_target` has no issue reaching this issue quickly.
@TheBlueMatt
TheBlueMattforce-pushed the 2023-12-fuzzing-fixes-1 branch from dea37db to 7f24e83CompareJanuary 8, 2024 18:20
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed the nit:

$ git diff-tree -U1 bceca1eed7aced011150f0f6ca7f0dd6fbe71d6a 7f24e833
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 5efef2e90..8e0ac2fdf 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -6172,10 +6172,7 @@ where
if self.default_configuration.manually_accept_inbound_channels {
- let channel_type_res = channel::channel_type_from_open_channel(- &msg, &peer_state.latest_features, &self.channel_type_features()- );- let channel_type = match channel_type_res {- Ok(channel_type) => channel_type,- Err(e) =>- return Err(MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)),- };+ let channel_type = channel::channel_type_from_open_channel(+ &msg, &peer_state.latest_features, &self.channel_type_features()+ ).map_err(|e|+ MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)+ )?;
let mut pending_events = self.pending_events.lock().unwrap();

@TheBlueMatt
TheBlueMatt merged commit 3fbee85 into lightningdevkit:mainJan 8, 2024
Sign up for freeto 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.

Remove panic when inbound peer sends OpenChannelRequest with no type specified and node is manually accepting requests

5 participants

@TheBlueMatt@codecov-commenter@tnull@wpaulino@shaavan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix various issues found in full_stack_target fuzzing - #2808

Merged
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1
Jan 8, 2024
Merged

Fix various issues found in full_stack_target fuzzing#2808
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

#2804 reported a message-processing-reachable unwrap, which is concerning, but more generally it reported a serious coverage gap in our full_stack_target fuzzer. This fuzzer should be our first line of defense against such issues, but sadly was using a predefined config object which left us not testing cases that required specific config flags. This fixes#2804 (in an imo cleaner way than #2805, at least for downstream devs even if not in LDK) as well as a number of other quite minor debug and overflow issues found after I updated the full_stack_target to test different config flags.

If we get a `Feature` object which has excess zero bytes, we
shouldn't consider it a different `Feature` from another with the
same bits set, but no excess zero bytes. Here we fix both the
`Hash` and `PartialEq` implementation for `Features` to ignore
excess zero bytes.
When we or our counterparty are updating the fees on the channel,
we currently check that the resulting balance is sufficient not
only to meet the reserve threshold, but also not push it below
dust. This isn't required in the BOLTs and may lead to spurious
force-closures (which would be a bit safer, but reserve should
always exceed the dust threshold).
Worse, the current logic is broken - it compares the output value
in *billionths of satoshis* to the dust limit in satoshis. Thus,
the code is borderline dead anyway, but can overflow for channels
with several million Bitcoin, causing the fuzzer to get mad (and
lead to spurious force-closures for few-billion-dollar channels).
When contest delays are >= 0x8000, script pushes require an extra
byte to avoid being interpreted as a negative int. Thus, for
channels with CSV delays longer than ~7.5 months we may generate
transactions with slightly too little fee. This isn't really a huge
deal, but we should prefer to be conservative here, and slightly
too high fee in the general case is better than slightly too little
fee in other cases.
If we try to open a channel with a peer that is disconnected (but
with which we have some other channels), we'll end up with an
unfunded channel which will lead to a panic when the peer
reconnects. Here we drop this debug assertion without bother to add
a new test, given this behavior will change in a PR very soon.
If a peer provides a feerate which nears `u32::MAX`, we may
overflow calculating the dust buffer feerate, leading to spuriously
keeping non-anchor channels open when they should be force-closed.
@codecov-commenter

codecov-commenter commented Dec 29, 2023

Copy link
Copy Markdown

Codecov Report

Attention: 5 lines in your changes are missing coverage. Please review.

Comparison is base (4deb263) 88.49% compared to head (bceca1e) 88.73%.
Report is 14 commits behind head on main.

❗ Current head bceca1e differs from pull request most recent head 7f24e83. Consider uploading reports for the commit 7f24e83 to get more accurate results

FilesPatch %Lines
lightning/src/ln/channel.rs88.57%4 Missing ⚠️
lightning/src/ln/features.rs96.55%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #2808 +/- ##
==========================================
+ Coverage 88.49% 88.73% +0.23% 
==========================================
Files 114 114 Lines 91935 95699 +3764 Branches 91935 95699 +3764 ==========================================
+ Hits 81359 84914 +3555 - Misses 8097 8336 +239 + Partials 2479 2449 -30 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One question, otherwise LGTM.

Comment threadlightning/src/ln/channel.rs Outdated
} else {
let channel_type = ChannelTypeFeatures::from_init(&their_features);
if channel_type != ChannelTypeFeatures::only_static_remote_key() {
if channel_type != channel_type_from_open_channel(msg) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This change is a bit confusing to me: why make it look like we'd consider msg after all, when in fact it would always result in ChannelTypeFeatures::only_static_remote_key()? Leaving it as-is seems more readable?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Yea, I was trying to keep from making the "default channel type is only-static-remote-key" assumption copied in several places, but I agree this ended up being more awkward than not. Instead I moved the whole "what is the channel type given the message" thing into the new method and made it fallible. This no longer directly solves the issue, but rather actually handling the failures does, which is also kinda nice in that we will avoid giving users an event for a channel we can't use.

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The changes look great! I have one small question and two suggestions for the comments.

Comment threadlightning/src/ln/chan_utils.rs
@@ -1876,7 +1872,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
if let Some(feerate) = outbound_feerate_update {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the description above it is written:

// may, at any point, increase _by_ at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
// whichever is higher.

which implies:

cmp::max(feerate_by_kw + 2530, feerate_plus_quarter)

Whereas, the line should read:

pub fn get_dust_buffer_feerate(&self, outbound_feerate_update: Option<u32>) -> u32 {
// When calculating our exposure to dust HTLCs, we assume that the channel feerate
- // may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,+ // may, at any point, increase to at least 10 sat/vB (i.e 2530 sat/kWU) or by 25%,
// whichever is higher. This ensures that we aren't suddenly exposed to significantly

Since we are touching this function in this PR. I believe we should also address this small but significant comment change.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh, good catch! I'd like to actually address this by making the code match the comment, not the other way around, so I'll address this in a later PR - #2815

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cool!

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/ln/chan_utils.rs
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed.

tnull
tnull previously approved these changes Jan 8, 2024

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

One nit, but feel free to land as is.

Comment threadlightning/src/ln/channelmanager.rs Outdated

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ACK
The changes look great, and since the comment will be addressed in a follow-up PR, I believe this PR is good to go!

If we receive an `OpenChannel` message without a `channel_type`
with `manually_accept_inbound_channels` set, we will `unwrap()`
`None`.
This is uncommon these days as most nodes support `channel_type`,
but sadly is rather trivial for a peer to hit for those with manual
channel acceptance enabled.
Reported in and fixeslightningdevkit#2804. Luckily, the updated
`full_stack_target` has no issue reaching this issue quickly.
@TheBlueMatt
TheBlueMattforce-pushed the 2023-12-fuzzing-fixes-1 branch from dea37db to 7f24e83CompareJanuary 8, 2024 18:20
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed the nit:

$ git diff-tree -U1 bceca1eed7aced011150f0f6ca7f0dd6fbe71d6a 7f24e833
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 5efef2e90..8e0ac2fdf 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -6172,10 +6172,7 @@ where
if self.default_configuration.manually_accept_inbound_channels {
- let channel_type_res = channel::channel_type_from_open_channel(- &msg, &peer_state.latest_features, &self.channel_type_features()- );- let channel_type = match channel_type_res {- Ok(channel_type) => channel_type,- Err(e) =>- return Err(MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)),- };+ let channel_type = channel::channel_type_from_open_channel(+ &msg, &peer_state.latest_features, &self.channel_type_features()+ ).map_err(|e|+ MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)+ )?;
let mut pending_events = self.pending_events.lock().unwrap();

@TheBlueMatt
TheBlueMatt merged commit 3fbee85 into lightningdevkit:mainJan 8, 2024
Sign up for freeto 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.

Remove panic when inbound peer sends OpenChannelRequest with no type specified and node is manually accepting requests

5 participants

@TheBlueMatt@codecov-commenter@tnull@wpaulino@shaavan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Fix various issues found in full_stack_target fuzzing - #2808

Merged
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1
Jan 8, 2024
Merged

Fix various issues found in full_stack_target fuzzing#2808
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

#2804 reported a message-processing-reachable unwrap, which is concerning, but more generally it reported a serious coverage gap in our full_stack_target fuzzer. This fuzzer should be our first line of defense against such issues, but sadly was using a predefined config object which left us not testing cases that required specific config flags. This fixes#2804 (in an imo cleaner way than #2805, at least for downstream devs even if not in LDK) as well as a number of other quite minor debug and overflow issues found after I updated the full_stack_target to test different config flags.

If we get a `Feature` object which has excess zero bytes, we
shouldn't consider it a different `Feature` from another with the
same bits set, but no excess zero bytes. Here we fix both the
`Hash` and `PartialEq` implementation for `Features` to ignore
excess zero bytes.
When we or our counterparty are updating the fees on the channel,
we currently check that the resulting balance is sufficient not
only to meet the reserve threshold, but also not push it below
dust. This isn't required in the BOLTs and may lead to spurious
force-closures (which would be a bit safer, but reserve should
always exceed the dust threshold).
Worse, the current logic is broken - it compares the output value
in *billionths of satoshis* to the dust limit in satoshis. Thus,
the code is borderline dead anyway, but can overflow for channels
with several million Bitcoin, causing the fuzzer to get mad (and
lead to spurious force-closures for few-billion-dollar channels).
When contest delays are >= 0x8000, script pushes require an extra
byte to avoid being interpreted as a negative int. Thus, for
channels with CSV delays longer than ~7.5 months we may generate
transactions with slightly too little fee. This isn't really a huge
deal, but we should prefer to be conservative here, and slightly
too high fee in the general case is better than slightly too little
fee in other cases.
If we try to open a channel with a peer that is disconnected (but
with which we have some other channels), we'll end up with an
unfunded channel which will lead to a panic when the peer
reconnects. Here we drop this debug assertion without bother to add
a new test, given this behavior will change in a PR very soon.
If a peer provides a feerate which nears `u32::MAX`, we may
overflow calculating the dust buffer feerate, leading to spuriously
keeping non-anchor channels open when they should be force-closed.
@codecov-commenter

codecov-commenter commented Dec 29, 2023

Copy link
Copy Markdown

Codecov Report

Attention: 5 lines in your changes are missing coverage. Please review.

Comparison is base (4deb263) 88.49% compared to head (bceca1e) 88.73%.
Report is 14 commits behind head on main.

❗ Current head bceca1e differs from pull request most recent head 7f24e83. Consider uploading reports for the commit 7f24e83 to get more accurate results

FilesPatch %Lines
lightning/src/ln/channel.rs88.57%4 Missing ⚠️
lightning/src/ln/features.rs96.55%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #2808 +/- ##
==========================================
+ Coverage 88.49% 88.73% +0.23% 
==========================================
Files 114 114 Lines 91935 95699 +3764 Branches 91935 95699 +3764 ==========================================
+ Hits 81359 84914 +3555 - Misses 8097 8336 +239 + Partials 2479 2449 -30 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One question, otherwise LGTM.

Comment threadlightning/src/ln/channel.rs Outdated
} else {
let channel_type = ChannelTypeFeatures::from_init(&their_features);
if channel_type != ChannelTypeFeatures::only_static_remote_key() {
if channel_type != channel_type_from_open_channel(msg) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This change is a bit confusing to me: why make it look like we'd consider msg after all, when in fact it would always result in ChannelTypeFeatures::only_static_remote_key()? Leaving it as-is seems more readable?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Yea, I was trying to keep from making the "default channel type is only-static-remote-key" assumption copied in several places, but I agree this ended up being more awkward than not. Instead I moved the whole "what is the channel type given the message" thing into the new method and made it fallible. This no longer directly solves the issue, but rather actually handling the failures does, which is also kinda nice in that we will avoid giving users an event for a channel we can't use.

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The changes look great! I have one small question and two suggestions for the comments.

Comment threadlightning/src/ln/chan_utils.rs
@@ -1876,7 +1872,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
if let Some(feerate) = outbound_feerate_update {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the description above it is written:

// may, at any point, increase _by_ at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
// whichever is higher.

which implies:

cmp::max(feerate_by_kw + 2530, feerate_plus_quarter)

Whereas, the line should read:

pub fn get_dust_buffer_feerate(&self, outbound_feerate_update: Option<u32>) -> u32 {
// When calculating our exposure to dust HTLCs, we assume that the channel feerate
- // may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,+ // may, at any point, increase to at least 10 sat/vB (i.e 2530 sat/kWU) or by 25%,
// whichever is higher. This ensures that we aren't suddenly exposed to significantly

Since we are touching this function in this PR. I believe we should also address this small but significant comment change.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh, good catch! I'd like to actually address this by making the code match the comment, not the other way around, so I'll address this in a later PR - #2815

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cool!

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/ln/chan_utils.rs
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed.

tnull
tnull previously approved these changes Jan 8, 2024

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

One nit, but feel free to land as is.

Comment threadlightning/src/ln/channelmanager.rs Outdated

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ACK
The changes look great, and since the comment will be addressed in a follow-up PR, I believe this PR is good to go!

If we receive an `OpenChannel` message without a `channel_type`
with `manually_accept_inbound_channels` set, we will `unwrap()`
`None`.
This is uncommon these days as most nodes support `channel_type`,
but sadly is rather trivial for a peer to hit for those with manual
channel acceptance enabled.
Reported in and fixeslightningdevkit#2804. Luckily, the updated
`full_stack_target` has no issue reaching this issue quickly.
@TheBlueMatt
TheBlueMattforce-pushed the 2023-12-fuzzing-fixes-1 branch from dea37db to 7f24e83CompareJanuary 8, 2024 18:20
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed the nit:

$ git diff-tree -U1 bceca1eed7aced011150f0f6ca7f0dd6fbe71d6a 7f24e833
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 5efef2e90..8e0ac2fdf 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -6172,10 +6172,7 @@ where
if self.default_configuration.manually_accept_inbound_channels {
- let channel_type_res = channel::channel_type_from_open_channel(- &msg, &peer_state.latest_features, &self.channel_type_features()- );- let channel_type = match channel_type_res {- Ok(channel_type) => channel_type,- Err(e) =>- return Err(MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)),- };+ let channel_type = channel::channel_type_from_open_channel(+ &msg, &peer_state.latest_features, &self.channel_type_features()+ ).map_err(|e|+ MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)+ )?;
let mut pending_events = self.pending_events.lock().unwrap();

@TheBlueMatt
TheBlueMatt merged commit 3fbee85 into lightningdevkit:mainJan 8, 2024
Sign up for freeto 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.

Remove panic when inbound peer sends OpenChannelRequest with no type specified and node is manually accepting requests

5 participants

@TheBlueMatt@codecov-commenter@tnull@wpaulino@shaavan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix various issues found in full_stack_target fuzzing - #2808

Merged
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1
Jan 8, 2024
Merged

Fix various issues found in full_stack_target fuzzing#2808
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

#2804 reported a message-processing-reachable unwrap, which is concerning, but more generally it reported a serious coverage gap in our full_stack_target fuzzer. This fuzzer should be our first line of defense against such issues, but sadly was using a predefined config object which left us not testing cases that required specific config flags. This fixes#2804 (in an imo cleaner way than #2805, at least for downstream devs even if not in LDK) as well as a number of other quite minor debug and overflow issues found after I updated the full_stack_target to test different config flags.

If we get a `Feature` object which has excess zero bytes, we
shouldn't consider it a different `Feature` from another with the
same bits set, but no excess zero bytes. Here we fix both the
`Hash` and `PartialEq` implementation for `Features` to ignore
excess zero bytes.
When we or our counterparty are updating the fees on the channel,
we currently check that the resulting balance is sufficient not
only to meet the reserve threshold, but also not push it below
dust. This isn't required in the BOLTs and may lead to spurious
force-closures (which would be a bit safer, but reserve should
always exceed the dust threshold).
Worse, the current logic is broken - it compares the output value
in *billionths of satoshis* to the dust limit in satoshis. Thus,
the code is borderline dead anyway, but can overflow for channels
with several million Bitcoin, causing the fuzzer to get mad (and
lead to spurious force-closures for few-billion-dollar channels).
When contest delays are >= 0x8000, script pushes require an extra
byte to avoid being interpreted as a negative int. Thus, for
channels with CSV delays longer than ~7.5 months we may generate
transactions with slightly too little fee. This isn't really a huge
deal, but we should prefer to be conservative here, and slightly
too high fee in the general case is better than slightly too little
fee in other cases.
If we try to open a channel with a peer that is disconnected (but
with which we have some other channels), we'll end up with an
unfunded channel which will lead to a panic when the peer
reconnects. Here we drop this debug assertion without bother to add
a new test, given this behavior will change in a PR very soon.
If a peer provides a feerate which nears `u32::MAX`, we may
overflow calculating the dust buffer feerate, leading to spuriously
keeping non-anchor channels open when they should be force-closed.
@codecov-commenter

codecov-commenter commented Dec 29, 2023

Copy link
Copy Markdown

Codecov Report

Attention: 5 lines in your changes are missing coverage. Please review.

Comparison is base (4deb263) 88.49% compared to head (bceca1e) 88.73%.
Report is 14 commits behind head on main.

❗ Current head bceca1e differs from pull request most recent head 7f24e83. Consider uploading reports for the commit 7f24e83 to get more accurate results

FilesPatch %Lines
lightning/src/ln/channel.rs88.57%4 Missing ⚠️
lightning/src/ln/features.rs96.55%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #2808 +/- ##
==========================================
+ Coverage 88.49% 88.73% +0.23% 
==========================================
Files 114 114 Lines 91935 95699 +3764 Branches 91935 95699 +3764 ==========================================
+ Hits 81359 84914 +3555 - Misses 8097 8336 +239 + Partials 2479 2449 -30 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One question, otherwise LGTM.

Comment threadlightning/src/ln/channel.rs Outdated
} else {
let channel_type = ChannelTypeFeatures::from_init(&their_features);
if channel_type != ChannelTypeFeatures::only_static_remote_key() {
if channel_type != channel_type_from_open_channel(msg) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This change is a bit confusing to me: why make it look like we'd consider msg after all, when in fact it would always result in ChannelTypeFeatures::only_static_remote_key()? Leaving it as-is seems more readable?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Yea, I was trying to keep from making the "default channel type is only-static-remote-key" assumption copied in several places, but I agree this ended up being more awkward than not. Instead I moved the whole "what is the channel type given the message" thing into the new method and made it fallible. This no longer directly solves the issue, but rather actually handling the failures does, which is also kinda nice in that we will avoid giving users an event for a channel we can't use.

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The changes look great! I have one small question and two suggestions for the comments.

Comment threadlightning/src/ln/chan_utils.rs
@@ -1876,7 +1872,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
if let Some(feerate) = outbound_feerate_update {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the description above it is written:

// may, at any point, increase _by_ at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
// whichever is higher.

which implies:

cmp::max(feerate_by_kw + 2530, feerate_plus_quarter)

Whereas, the line should read:

pub fn get_dust_buffer_feerate(&self, outbound_feerate_update: Option<u32>) -> u32 {
// When calculating our exposure to dust HTLCs, we assume that the channel feerate
- // may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,+ // may, at any point, increase to at least 10 sat/vB (i.e 2530 sat/kWU) or by 25%,
// whichever is higher. This ensures that we aren't suddenly exposed to significantly

Since we are touching this function in this PR. I believe we should also address this small but significant comment change.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh, good catch! I'd like to actually address this by making the code match the comment, not the other way around, so I'll address this in a later PR - #2815

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cool!

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/ln/chan_utils.rs
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed.

tnull
tnull previously approved these changes Jan 8, 2024

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

One nit, but feel free to land as is.

Comment threadlightning/src/ln/channelmanager.rs Outdated

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ACK
The changes look great, and since the comment will be addressed in a follow-up PR, I believe this PR is good to go!

If we receive an `OpenChannel` message without a `channel_type`
with `manually_accept_inbound_channels` set, we will `unwrap()`
`None`.
This is uncommon these days as most nodes support `channel_type`,
but sadly is rather trivial for a peer to hit for those with manual
channel acceptance enabled.
Reported in and fixeslightningdevkit#2804. Luckily, the updated
`full_stack_target` has no issue reaching this issue quickly.
@TheBlueMatt
TheBlueMattforce-pushed the 2023-12-fuzzing-fixes-1 branch from dea37db to 7f24e83CompareJanuary 8, 2024 18:20
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed the nit:

$ git diff-tree -U1 bceca1eed7aced011150f0f6ca7f0dd6fbe71d6a 7f24e833
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 5efef2e90..8e0ac2fdf 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -6172,10 +6172,7 @@ where
if self.default_configuration.manually_accept_inbound_channels {
- let channel_type_res = channel::channel_type_from_open_channel(- &msg, &peer_state.latest_features, &self.channel_type_features()- );- let channel_type = match channel_type_res {- Ok(channel_type) => channel_type,- Err(e) =>- return Err(MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)),- };+ let channel_type = channel::channel_type_from_open_channel(+ &msg, &peer_state.latest_features, &self.channel_type_features()+ ).map_err(|e|+ MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)+ )?;
let mut pending_events = self.pending_events.lock().unwrap();

@TheBlueMatt
TheBlueMatt merged commit 3fbee85 into lightningdevkit:mainJan 8, 2024
Sign up for freeto 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.

Remove panic when inbound peer sends OpenChannelRequest with no type specified and node is manually accepting requests

5 participants

@TheBlueMatt@codecov-commenter@tnull@wpaulino@shaavan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix various issues found in full_stack_target fuzzing - #2808

Merged
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1
Jan 8, 2024
Merged

Fix various issues found in full_stack_target fuzzing#2808
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

#2804 reported a message-processing-reachable unwrap, which is concerning, but more generally it reported a serious coverage gap in our full_stack_target fuzzer. This fuzzer should be our first line of defense against such issues, but sadly was using a predefined config object which left us not testing cases that required specific config flags. This fixes#2804 (in an imo cleaner way than #2805, at least for downstream devs even if not in LDK) as well as a number of other quite minor debug and overflow issues found after I updated the full_stack_target to test different config flags.

If we get a `Feature` object which has excess zero bytes, we
shouldn't consider it a different `Feature` from another with the
same bits set, but no excess zero bytes. Here we fix both the
`Hash` and `PartialEq` implementation for `Features` to ignore
excess zero bytes.
When we or our counterparty are updating the fees on the channel,
we currently check that the resulting balance is sufficient not
only to meet the reserve threshold, but also not push it below
dust. This isn't required in the BOLTs and may lead to spurious
force-closures (which would be a bit safer, but reserve should
always exceed the dust threshold).
Worse, the current logic is broken - it compares the output value
in *billionths of satoshis* to the dust limit in satoshis. Thus,
the code is borderline dead anyway, but can overflow for channels
with several million Bitcoin, causing the fuzzer to get mad (and
lead to spurious force-closures for few-billion-dollar channels).
When contest delays are >= 0x8000, script pushes require an extra
byte to avoid being interpreted as a negative int. Thus, for
channels with CSV delays longer than ~7.5 months we may generate
transactions with slightly too little fee. This isn't really a huge
deal, but we should prefer to be conservative here, and slightly
too high fee in the general case is better than slightly too little
fee in other cases.
If we try to open a channel with a peer that is disconnected (but
with which we have some other channels), we'll end up with an
unfunded channel which will lead to a panic when the peer
reconnects. Here we drop this debug assertion without bother to add
a new test, given this behavior will change in a PR very soon.
If a peer provides a feerate which nears `u32::MAX`, we may
overflow calculating the dust buffer feerate, leading to spuriously
keeping non-anchor channels open when they should be force-closed.
@codecov-commenter

codecov-commenter commented Dec 29, 2023

Copy link
Copy Markdown

Codecov Report

Attention: 5 lines in your changes are missing coverage. Please review.

Comparison is base (4deb263) 88.49% compared to head (bceca1e) 88.73%.
Report is 14 commits behind head on main.

❗ Current head bceca1e differs from pull request most recent head 7f24e83. Consider uploading reports for the commit 7f24e83 to get more accurate results

FilesPatch %Lines
lightning/src/ln/channel.rs88.57%4 Missing ⚠️
lightning/src/ln/features.rs96.55%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #2808 +/- ##
==========================================
+ Coverage 88.49% 88.73% +0.23% 
==========================================
Files 114 114 Lines 91935 95699 +3764 Branches 91935 95699 +3764 ==========================================
+ Hits 81359 84914 +3555 - Misses 8097 8336 +239 + Partials 2479 2449 -30 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One question, otherwise LGTM.

Comment threadlightning/src/ln/channel.rs Outdated
} else {
let channel_type = ChannelTypeFeatures::from_init(&their_features);
if channel_type != ChannelTypeFeatures::only_static_remote_key() {
if channel_type != channel_type_from_open_channel(msg) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This change is a bit confusing to me: why make it look like we'd consider msg after all, when in fact it would always result in ChannelTypeFeatures::only_static_remote_key()? Leaving it as-is seems more readable?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Yea, I was trying to keep from making the "default channel type is only-static-remote-key" assumption copied in several places, but I agree this ended up being more awkward than not. Instead I moved the whole "what is the channel type given the message" thing into the new method and made it fallible. This no longer directly solves the issue, but rather actually handling the failures does, which is also kinda nice in that we will avoid giving users an event for a channel we can't use.

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The changes look great! I have one small question and two suggestions for the comments.

Comment threadlightning/src/ln/chan_utils.rs
@@ -1876,7 +1872,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
if let Some(feerate) = outbound_feerate_update {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the description above it is written:

// may, at any point, increase _by_ at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
// whichever is higher.

which implies:

cmp::max(feerate_by_kw + 2530, feerate_plus_quarter)

Whereas, the line should read:

pub fn get_dust_buffer_feerate(&self, outbound_feerate_update: Option<u32>) -> u32 {
// When calculating our exposure to dust HTLCs, we assume that the channel feerate
- // may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,+ // may, at any point, increase to at least 10 sat/vB (i.e 2530 sat/kWU) or by 25%,
// whichever is higher. This ensures that we aren't suddenly exposed to significantly

Since we are touching this function in this PR. I believe we should also address this small but significant comment change.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh, good catch! I'd like to actually address this by making the code match the comment, not the other way around, so I'll address this in a later PR - #2815

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cool!

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/ln/chan_utils.rs
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed.

tnull
tnull previously approved these changes Jan 8, 2024

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

One nit, but feel free to land as is.

Comment threadlightning/src/ln/channelmanager.rs Outdated

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ACK
The changes look great, and since the comment will be addressed in a follow-up PR, I believe this PR is good to go!

If we receive an `OpenChannel` message without a `channel_type`
with `manually_accept_inbound_channels` set, we will `unwrap()`
`None`.
This is uncommon these days as most nodes support `channel_type`,
but sadly is rather trivial for a peer to hit for those with manual
channel acceptance enabled.
Reported in and fixeslightningdevkit#2804. Luckily, the updated
`full_stack_target` has no issue reaching this issue quickly.
@TheBlueMatt
TheBlueMattforce-pushed the 2023-12-fuzzing-fixes-1 branch from dea37db to 7f24e83CompareJanuary 8, 2024 18:20
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed the nit:

$ git diff-tree -U1 bceca1eed7aced011150f0f6ca7f0dd6fbe71d6a 7f24e833
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 5efef2e90..8e0ac2fdf 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -6172,10 +6172,7 @@ where
if self.default_configuration.manually_accept_inbound_channels {
- let channel_type_res = channel::channel_type_from_open_channel(- &msg, &peer_state.latest_features, &self.channel_type_features()- );- let channel_type = match channel_type_res {- Ok(channel_type) => channel_type,- Err(e) =>- return Err(MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)),- };+ let channel_type = channel::channel_type_from_open_channel(+ &msg, &peer_state.latest_features, &self.channel_type_features()+ ).map_err(|e|+ MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)+ )?;
let mut pending_events = self.pending_events.lock().unwrap();

@TheBlueMatt
TheBlueMatt merged commit 3fbee85 into lightningdevkit:mainJan 8, 2024
Sign up for freeto 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.

Remove panic when inbound peer sends OpenChannelRequest with no type specified and node is manually accepting requests

5 participants

@TheBlueMatt@codecov-commenter@tnull@wpaulino@shaavan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Fix various issues found in full_stack_target fuzzing - #2808

Merged
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1
Jan 8, 2024
Merged

Fix various issues found in full_stack_target fuzzing#2808
TheBlueMatt merged 6 commits into
lightningdevkit:mainfrom
TheBlueMatt:2023-12-fuzzing-fixes-1

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

#2804 reported a message-processing-reachable unwrap, which is concerning, but more generally it reported a serious coverage gap in our full_stack_target fuzzer. This fuzzer should be our first line of defense against such issues, but sadly was using a predefined config object which left us not testing cases that required specific config flags. This fixes#2804 (in an imo cleaner way than #2805, at least for downstream devs even if not in LDK) as well as a number of other quite minor debug and overflow issues found after I updated the full_stack_target to test different config flags.

If we get a `Feature` object which has excess zero bytes, we
shouldn't consider it a different `Feature` from another with the
same bits set, but no excess zero bytes. Here we fix both the
`Hash` and `PartialEq` implementation for `Features` to ignore
excess zero bytes.
When we or our counterparty are updating the fees on the channel,
we currently check that the resulting balance is sufficient not
only to meet the reserve threshold, but also not push it below
dust. This isn't required in the BOLTs and may lead to spurious
force-closures (which would be a bit safer, but reserve should
always exceed the dust threshold).
Worse, the current logic is broken - it compares the output value
in *billionths of satoshis* to the dust limit in satoshis. Thus,
the code is borderline dead anyway, but can overflow for channels
with several million Bitcoin, causing the fuzzer to get mad (and
lead to spurious force-closures for few-billion-dollar channels).
When contest delays are >= 0x8000, script pushes require an extra
byte to avoid being interpreted as a negative int. Thus, for
channels with CSV delays longer than ~7.5 months we may generate
transactions with slightly too little fee. This isn't really a huge
deal, but we should prefer to be conservative here, and slightly
too high fee in the general case is better than slightly too little
fee in other cases.
If we try to open a channel with a peer that is disconnected (but
with which we have some other channels), we'll end up with an
unfunded channel which will lead to a panic when the peer
reconnects. Here we drop this debug assertion without bother to add
a new test, given this behavior will change in a PR very soon.
If a peer provides a feerate which nears `u32::MAX`, we may
overflow calculating the dust buffer feerate, leading to spuriously
keeping non-anchor channels open when they should be force-closed.
@codecov-commenter

codecov-commenter commented Dec 29, 2023

Copy link
Copy Markdown

Codecov Report

Attention: 5 lines in your changes are missing coverage. Please review.

Comparison is base (4deb263) 88.49% compared to head (bceca1e) 88.73%.
Report is 14 commits behind head on main.

❗ Current head bceca1e differs from pull request most recent head 7f24e83. Consider uploading reports for the commit 7f24e83 to get more accurate results

FilesPatch %Lines
lightning/src/ln/channel.rs88.57%4 Missing ⚠️
lightning/src/ln/features.rs96.55%0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@## main #2808 +/- ##
==========================================
+ Coverage 88.49% 88.73% +0.23% 
==========================================
Files 114 114 Lines 91935 95699 +3764 Branches 91935 95699 +3764 ==========================================
+ Hits 81359 84914 +3555 - Misses 8097 8336 +239 + Partials 2479 2449 -30 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One question, otherwise LGTM.

Comment threadlightning/src/ln/channel.rs Outdated
} else {
let channel_type = ChannelTypeFeatures::from_init(&their_features);
if channel_type != ChannelTypeFeatures::only_static_remote_key() {
if channel_type != channel_type_from_open_channel(msg) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This change is a bit confusing to me: why make it look like we'd consider msg after all, when in fact it would always result in ChannelTypeFeatures::only_static_remote_key()? Leaving it as-is seems more readable?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Yea, I was trying to keep from making the "default channel type is only-static-remote-key" assumption copied in several places, but I agree this ended up being more awkward than not. Instead I moved the whole "what is the channel type given the message" thing into the new method and made it fallible. This no longer directly solves the issue, but rather actually handling the failures does, which is also kinda nice in that we will avoid giving users an event for a channel we can't use.

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The changes look great! I have one small question and two suggestions for the comments.

Comment threadlightning/src/ln/chan_utils.rs
@@ -1876,7 +1872,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
if let Some(feerate) = outbound_feerate_update {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the description above it is written:

// may, at any point, increase _by_ at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
// whichever is higher.

which implies:

cmp::max(feerate_by_kw + 2530, feerate_plus_quarter)

Whereas, the line should read:

pub fn get_dust_buffer_feerate(&self, outbound_feerate_update: Option<u32>) -> u32 {
// When calculating our exposure to dust HTLCs, we assume that the channel feerate
- // may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,+ // may, at any point, increase to at least 10 sat/vB (i.e 2530 sat/kWU) or by 25%,
// whichever is higher. This ensures that we aren't suddenly exposed to significantly

Since we are touching this function in this PR. I believe we should also address this small but significant comment change.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh, good catch! I'd like to actually address this by making the code match the comment, not the other way around, so I'll address this in a later PR - #2815

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cool!

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/ln/chan_utils.rs
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed.

tnull
tnull previously approved these changes Jan 8, 2024

@tnulltnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

One nit, but feel free to land as is.

Comment threadlightning/src/ln/channelmanager.rs Outdated

@shaavanshaavan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ACK
The changes look great, and since the comment will be addressed in a follow-up PR, I believe this PR is good to go!

If we receive an `OpenChannel` message without a `channel_type`
with `manually_accept_inbound_channels` set, we will `unwrap()`
`None`.
This is uncommon these days as most nodes support `channel_type`,
but sadly is rather trivial for a peer to hit for those with manual
channel acceptance enabled.
Reported in and fixeslightningdevkit#2804. Luckily, the updated
`full_stack_target` has no issue reaching this issue quickly.
@TheBlueMatt
TheBlueMattforce-pushed the 2023-12-fuzzing-fixes-1 branch from dea37db to 7f24e83CompareJanuary 8, 2024 18:20
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Addressed the nit:

$ git diff-tree -U1 bceca1eed7aced011150f0f6ca7f0dd6fbe71d6a 7f24e833
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 5efef2e90..8e0ac2fdf 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -6172,10 +6172,7 @@ where
if self.default_configuration.manually_accept_inbound_channels {
- let channel_type_res = channel::channel_type_from_open_channel(- &msg, &peer_state.latest_features, &self.channel_type_features()- );- let channel_type = match channel_type_res {- Ok(channel_type) => channel_type,- Err(e) =>- return Err(MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)),- };+ let channel_type = channel::channel_type_from_open_channel(+ &msg, &peer_state.latest_features, &self.channel_type_features()+ ).map_err(|e|+ MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)+ )?;
let mut pending_events = self.pending_events.lock().unwrap();

@TheBlueMatt
TheBlueMatt merged commit 3fbee85 into lightningdevkit:mainJan 8, 2024
Sign up for freeto 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.

Remove panic when inbound peer sends OpenChannelRequest with no type specified and node is manually accepting requests

5 participants

@TheBlueMatt@codecov-commenter@tnull@wpaulino@shaavan