-
Notifications
You must be signed in to change notification settings - Fork 87
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
Handle padded_len_* overflows gracefully #719
Conversation
@@ -733,8 +733,8 @@ fn script_input_coin_data_offset() { | |||
.inputs_predicate_offset_at(offset) | |||
.expect("Failed to fetch offset"); | |||
|
|||
assert_ne!(bytes::padded_len(&predicate), predicate.len()); | |||
assert_eq!(bytes::padded_len(&predicate), len); | |||
assert_ne!(bytes::padded_len(&predicate), Some(predicate.len())); |
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.
Why even include the ne
case? There are many things that it's not equal to :)
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.
No idea. It was there already, and I just kept the old tests as-is.
@@ -826,8 +826,8 @@ fn upgrade_input_coin_data_offset() { | |||
.inputs_predicate_offset_at(offset) | |||
.expect("Failed to fetch offset"); | |||
|
|||
assert_ne!(bytes::padded_len(&predicate), predicate.len()); | |||
assert_eq!(bytes::padded_len(&predicate), len); | |||
assert_ne!(bytes::padded_len(&predicate), Some(predicate.len())); |
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.
Same question.
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.
Same answer.
.zip( | ||
input | ||
.predicate_len() | ||
.map(|l| bytes::padded_len_usize(l).unwrap_or(usize::MAX)), |
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.
You can use and_then
instead of map
here to keep an Option
and not need the unwrap_or
.
The usize::MAX
feels arbitrary to 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.
Using and_then
here would mean combining together (a) input has no predicate_len
and (b) that the padding overflows. These are fundamentally different things. The idea behind using usize::MAX
is that the value was already quite close to it, and impossibly large. Any actual usage of the return value will correctly cause MemoryOverflow
or similar panic. The alternative would be to return Option<Result<usize, PanicReason>>
, and that feels a bit excessive. Alternatively we could just Rust-panic on such sizes, but I'm not confident that we actually verify the sizes beforehands.
.data_offset() | ||
.map(|o| o + bytes::padded_len(data)) | ||
InputRepr::Message.data_offset().map(|o| { | ||
o.saturating_add(bytes::padded_len(data).unwrap_or(usize::MAX)) |
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.
The usize::Max
kinda makes more sense here with the saturating_add
, but I haven a hard time with None
for len being equivalent to usize::Max
.
You could do a:
bytes::padded_len(data).map(|len| InputRepr::Message.data_offset().map(|o| o.saturating_add(len))
Not really an improvement on readability though :/ So up to you.
.map(|o| o + bytes::padded_len(predicate)), | ||
| Input::MessageDataPredicate(MessageDataPredicate { predicate, .. }) => { | ||
self.predicate_offset().map(|o| { | ||
o.saturating_add(bytes::padded_len(predicate).unwrap_or(usize::MAX)) |
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.
same here
@@ -233,7 +233,9 @@ mod field { | |||
return body.script_data_offset; | |||
} | |||
|
|||
self.script_offset() + bytes::padded_len(self.body.script.as_slice()) | |||
self.script_offset().saturating_add( | |||
bytes::padded_len(self.body.script.as_slice()).unwrap_or(usize::MAX), |
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.
same here
self.script_data_offset() | ||
+ bytes::padded_len(self.body.script_data.as_slice()) | ||
self.script_data_offset().saturating_add( | ||
bytes::padded_len(self.body.script_data.as_slice()).unwrap_or(usize::MAX), |
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.
and here
fuel-types/src/bytes.rs
Outdated
} | ||
|
||
#[test] | ||
fn padded_len_value() { |
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 we name this something like padded_len_usize__gives_values_in_increments_of_8
or something like that. To say what the tested behavior is.
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.
Makes sense. I'll rename it.
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.
assert_eq!(WORD_SIZE * 1, padded_len(&[0; WORD_SIZE])); | ||
assert_eq!(WORD_SIZE * 2, padded_len(&[0; WORD_SIZE + 1])); | ||
assert_eq!(WORD_SIZE * 2, padded_len(&[0; WORD_SIZE * 2])); | ||
assert_eq!(Some(WORD_SIZE * 0), padded_len(&[])); |
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 test could use a clearer name of what the expected behavior is.
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.
.map(|len| len as Word) | ||
.unwrap_or(Word::MAX); |
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.
Maybe we also want to return PanicReason::MemoryOverflow
?
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 Word::MAX
is guaranteed to hit the check just below, and return PanicReason::ContracMaxSize
. Unless we set contract_max_size == Word::MAX
which shouldn't happen, and even then the grow_stack
below will catch it.
There was a bug in
padded_len_word
andpadded_len_usize
functions that would overflow on inputs close to max value of the type. While inputs around the max value were never sensible, at least the LDC opcode could be used to trigger the overflow.Checklist
Before requesting review