Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require FixedLengthReadable for structs that always drain reader #3640

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

valentinewallace
Copy link
Contributor

Closes #3292

@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Mar 3, 2025

👋 Thanks for assigning @TheBlueMatt as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

In general, the codebase currently tends to use the Readable trait with two
different semantics -- some objects know when to stop reading because they have
a length prefix and some automatically read to the end of the reader.

This differing usage can lead to bugs. For objects that read-to-end, it would
be much safer if the compiler enforced that they could only be read from a
length-limiting reader.

We already have a LengthReadable trait that requires the underlying reader to
provide the length, which is similar to what we want. So rather than adding an
additional trait, here we refactor LengthReadable to require a fixed length
reader.

Upcoming commits will switch read-to-end structs that are currently read with
Readable to use LengthReadable instead.
@valentinewallace valentinewallace force-pushed the 2025-03-fixed-len-readable branch from 6f66ebb to 47916c1 Compare March 3, 2025 23:24
@valentinewallace
Copy link
Contributor Author

Looking for concept ACKs before fixing fuzz!

@valentinewallace
Copy link
Contributor Author

Also do we also want to do the same for LengthReadableArgs? It's only used for onion{message,payment} payload decodes at the moment and the usage doesn't look problematic, but we could...

@valentinewallace valentinewallace force-pushed the 2025-03-fixed-len-readable branch from 47916c1 to 792fcf0 Compare March 4, 2025 18:18
Copy link

codecov bot commented Mar 4, 2025

Codecov Report

Attention: Patch coverage is 60.83333% with 47 lines in your changes missing coverage. Please review.

Project coverage is 90.02%. Comparing base (5099dce) to head (792fcf0).
Report is 15 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/ln/wire.rs 45.45% 33 Missing and 3 partials ⚠️
lightning/src/ln/msgs.rs 82.14% 4 Missing and 1 partial ⚠️
lightning/src/offers/offer.rs 0.00% 3 Missing ⚠️
lightning/src/ln/channelmanager.rs 50.00% 2 Missing ⚠️
lightning/src/util/ser.rs 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3640      +/-   ##
==========================================
+ Coverage   89.20%   90.02%   +0.82%     
==========================================
  Files         152      155       +3     
  Lines      118661   126135    +7474     
  Branches   118661   126135    +7474     
==========================================
+ Hits       105849   113559    +7710     
+ Misses      10227    10101     -126     
+ Partials     2585     2475     -110     

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

@valentinewallace valentinewallace marked this pull request as ready for review March 4, 2025 21:04
@ldk-reviews-bot ldk-reviews-bot requested a review from arik-so March 4, 2025 21:05
@valentinewallace valentinewallace added the weekly goal Someone wants to land this this week label Mar 4, 2025
Copy link
Contributor

@arik-so arik-so left a comment

Choose a reason for hiding this comment

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

I think this makes a ton of sense, and not seeing any issues with any of the commits (though the whitespace one is also in 3620)

@ldk-reviews-bot
Copy link

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@arik-so
Copy link
Contributor

arik-so commented Mar 5, 2025

nit: might point out in the last commit message that the remaining structs are the ones not covered by impl_writeable_msg

@valentinewallace valentinewallace force-pushed the 2025-03-fixed-len-readable branch from 792fcf0 to 36698bb Compare March 5, 2025 20:59
See previous commit.

When deserializing objects via this macro, there is no length prefix so the
deser code will read the provided reader until it runs out of bytes.

Readable is not an appropriate trait for this situation because it should only
be used for structs that are prefixed with a length and know when to stop
reading. LengthReadable instead requires that the caller supply only the bytes
that are reserved for this struct.
Easier to review the previous commit this way.
When deserializing these structs, they will consume the reader until it is out
of bytes. Therefore, it's safer if they are only provided with readers that
limit how much of the byte stream will be read.

These remaining structs are largely the ones not covered when we transitioned
impl_writeable_msg to use LengthReadable in the previous commit.
@valentinewallace valentinewallace force-pushed the 2025-03-fixed-len-readable branch from 36698bb to b95f2ce Compare March 5, 2025 21:09
@arik-so
Copy link
Contributor

arik-so commented Mar 5, 2025

nit: squash the formatting commit in between getting the second ACK and merging.

@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @TheBlueMatt! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

/// Reads a `Self` in from the given [`LengthRead`].
fn read_from_fixed_length_buffer<R: LengthRead>(reader: &mut R) -> Result<Self, DecodeError>;
/// Reads a `Self` in from the given [`FixedLengthReader`].
fn read_from_fixed_length_buffer<'a, R: Read>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should either use a trait here, rather than forcing a FixedLengthReader, or we should remove LengthRead entirely (which looks ~unused now?). It'd be kinda nice to use a trait, I think, but maybe you thought it wouldn't be strict enough? I guess we could kinda just rename LengthRead as LengthLimiedRead and describe the semantics?

@@ -1101,7 +1101,9 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
// update_fail_htlc as we do when we reject a payment.
let mut msg_ser = update_add.encode();
msg_ser[1000] ^= 0xff;
let new_msg = UpdateAddHTLC::read(&mut Cursor::new(&msg_ser)).unwrap();
let mut cursor = Cursor::new(&msg_ser);
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: we don't actually need a Cursor, we can just &mut &msg_ser[..]. Same elsewhere in this commit. Would be nice to slowly phase out Cursor as we touch it.

@@ -1033,8 +1034,10 @@ impl OfferContents {
}
}

impl Readable for Offer {
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
impl LengthReadable for Offer {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't we need to do this for all the other bolt 12 structs too?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
weekly goal Someone wants to land this this week
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Define a separate trait for reading objects that know when to stop vs not
4 participants