-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
Reject TXs when there is a mismatch #6236
Conversation
@sakridge @CriesofCarrots, fyi. Not sure if the issue referenced is still an issue after the credit-only rewrite. Can you take a peek at #3568? |
Thanks for checking this. I'll also try to understand how this relates to the credit-only rewrite. |
Yep, this does appear to still be an issue, as sigverify trusts the signature vec length per this line here: Line 85 in 17f169f
|
@CriesofCarrots Thanks for checking this out! I'll work on to finish this up tomorrow's work-time in JST. :) |
core/src/sigverify.rs
Outdated
@@ -51,6 +51,10 @@ fn verify_packet(packet: &Packet) -> u8 { | |||
let mut pubkey_start = pubkey_start as usize; | |||
let msg_start = msg_start as usize; | |||
|
|||
if sig_len == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned in the pull request description, this part changes the runtime semantics. I'm assuming there will be no transaction with zero signature...
core/src/sigverify.rs
Outdated
for _ in 0..sig_len { | ||
signature_offsets.push(sig_offset); | ||
sig_offset += size_of::<Signature>() as u32; | ||
if sig_len > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned in the pull request description, this part changes the runtime semantics. I'm assuming there will be no transaction with zero signature...
@@ -127,6 +127,7 @@ fn get_program_ids(instructions: &[Instruction]) -> Vec<Pubkey> { | |||
pub struct MessageHeader { | |||
/// The number of signatures required for this message to be considered valid. The | |||
/// signatures must match the first `num_required_signatures` of `account_keys`. | |||
/// NOTE: Serialization-related changes must be paired with the direct read at sigverify. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As bonuses, I added precautionary comments to prevent this bug from reoccurring in the future...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also add a test that fails if the serialization changes in a way to affect that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I could do that of course, however I think that will be out of scope for this PR, assuming you're meaning to write some robust set of unit tests for each plausible outcome of get_packet_offsets
. This PR getting relatively large as one from an outside contributor already.
I've already wrote units tests, directly spotting the changed behavior: 1, 2 and 3.
I understand those yet-to-be-written unit tests will be invaluable, considering sigverify will directly be faced to the outside world (= the public Internet for the permission-less DLT like solana). But the concern already are covered by the issues like #5414 and #6339.
Anyway, if there are some thoughts I'm missing or better unit test scenario, please tell me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, no problem, we can take on writing that test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for consideration! Hopefully, writing it will be me if I find some free time!
core/src/sigverify.rs
Outdated
|
||
let (_pubkey_len, pubkey_size) = decode_len(&packet.data[msg_start_offset..]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sadly, this was a blatant wrongdoing of reading a packet...
In short, this old code incorrectly reads bytes serialized from MessageHeader
(three of u8
s) as the length (ShortU16
) of short_vec
. (Moderate pun intended... :).
So, if MessageHeader.required_num_sigs
are above 0x7f
(according to the comment of Short16
), pubkey_size
will be 2
and subsequent offset calculation gets bogus, resulting in false negative results of sigverify. This have been working because most of time MessageHeader.required_num_sigs
is very small compared to the 0x7f
.
To make sure, I traced this back to the origin of introduction of this bug. Let me know if it's needed!
(By the way, this is the first bug I've ever found in the production code of solana, I think I've finally managed to make tangible contribution... yay!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, accompanying unit test is test_large_sigs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a nice catch. That was my oversight; thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, this is split into #6388.
@CriesofCarrots @garious Sorry for not being able to deliver the promised blush-up in time... But I've finally managed to finish it! Could you review this pull request and run the CI? |
Codecov Report
@@ Coverage Diff @@
## master #6236 +/- ##
========================================
+ Coverage 77.4% 78.6% +1.2%
========================================
Files 216 219 +3
Lines 42592 41922 -670
========================================
+ Hits 32969 32985 +16
+ Misses 9623 8937 -686 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach looks okay to me. Just a couple small suggestions.
But I'd love for @sakridge to take a look; adding as reviewer.
This looks pretty good, but I would like to see a test that puts the malicious transaction into the verify path to show the issue in the original code and then that it's fixed with the updated offsets logic. |
I actually would like to see this split into the fix for the issue #3568 and the deserialization fix. |
Thanks for various comments! Now that #6251 is almost finished, I'll work on this tomorrow! |
I created one! Please take a look at #6388. Currently, both are based on the same parent git commit. And semantically, this PR are based on #6388, expecting that hotfix-like PR will get merged first. After the merge this PR will look like this: https://github.com/ryoqun/solana/compare/unaligned-pubkey-read..mismatched-sig-len.
I think this newly-added test case covers that scenario. If anything is missing, please tell me! |
Oops, I created a merge commit to preserve old review comments. Is the rebase way preferred? I'm fine to do that! |
Rebase please. No need to preserve the old review comments |
Reject transactions when there is a mismatch between tx.signatures.len() and tx.message().num_required_signatures. Fixes solana-labs#3568
2de7267
to
3079bdb
Compare
@mvines done!
Ok! Rebased! |
@CriesofCarrots @sakridge Thanks for quick reviews! I'm relieved the introduction of |
(Wow, it seems CI starts without bothering you to attach the CI label anymore... I'm very happy! Thank you very much!) |
Yep, I fixed that for you :) |
Pull request has been modified.
Oops, github created a merge commit.... I'll force push... |
1cd32f6
to
b7f518d
Compare
@CriesofCarrots Thanks for reviewing again! I addressed them! |
Thanks, @ryoqun ! |
Hi, this is my second small PR.
Problem
See #3568.
Summary of Changes
tx.signatures.len()
andtx.message().num_required_signatures
.get_packets_offset
return 0 assig_len
to propagate such a rejection into the ultimate result of sigverify. This changes the runtime semantics of sigverify for the case of withsig_len
being0
. So be careful when reviewing!get_packets_offset
. (Should I create a separete pull request?)