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. :) |
| 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.
As mentioned in the pull request description, this part changes the runtime semantics. I'm assuming there will be no transaction with zero signature...
| 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.
As mentioned in the pull request description, this part changes the runtime semantics. I'm assuming there will be no transaction with zero signature...
| 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.
As bonuses, I added precautionary comments to prevent this bug from reoccurring in the future...
There was a problem hiding this comment.
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.
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.
Ok, no problem, we can take on writing that test.
There was a problem hiding this comment.
Thanks for consideration! Hopefully, writing it will be me if I find some free time!
| // Ultimately, the actual sigverify will determine the uncertainity. | ||
| let sig_len_maybe_trusted = packet.data[msg_start_offset] as usize; | ||
|
|
||
| let (_pubkey_len, pubkey_size) = decode_len(&packet.data[msg_start_offset..]); |
There was a problem hiding this comment.
Sadly, this was a blatant wrongdoing of reading a packet...
In short, this old code incorrectly reads bytes serialized from MessageHeader (three of u8s) 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.
Also, accompanying unit test is test_large_sigs
There was a problem hiding this comment.
This is a nice catch. That was my oversight; thanks!
|
@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 |
CriesofCarrots
left a comment
There was a problem hiding this comment.
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_offsetreturn 0 assig_lento propagate such a rejection into the ultimate result of sigverify. This changes the runtime semantics of sigverify for the case of withsig_lenbeing0. So be careful when reviewing!get_packets_offset. (Should I create a separete pull request?)