-
Notifications
You must be signed in to change notification settings - Fork 278
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
decodeBytes is susceptible to an overflow bug sneak and can incur an OOM or runtime panic due to unproperly checked size on both 32-bit and 64-bit architectures #339
Comments
Kindly /cc-ing @alessio @alexanderbez @aaronc @erikgrinaker. |
Good catch, thanks! Opened #340 for this. |
Thank you for the fix @erikgrinaker! Actually I just re-ran some thoughts and also fuzzed and alas identified a vector that crashes as well with the same runtime out of range error when the length is saved as if len(bz) < n+int(size) {
return nil, n, fmt.Errorf("insufficient bytes decoding []byte of length %v", size)
} n+int(9223372036854775806) on 64-bit systems --> -X However, the PR #340 does fix this problem, thus I'd like to retitle this bug as for both 32-bit and 64-bit systems aka agnostic and we should perhaps issue updates to code using this ASAP. For reference: it took me 10min-1hr(writing repros, examining call sites) to mentally calculate through this problem but it took the fuzzer less than 1 second to find it, thus more reason to fuzz our code :-) You'll like this @okwme |
You're right, thanks again for this. This bug was introduced with 0.15.0, which was released three weeks ago, and is not used in any stable Cosmos SDK releases. Also, this can only be exploited via I've submitted updated release notes in #346, and pinged the SDK team. |
Thank you @erikgrinaker for the proactive, fast action, and for all your work! |
Sigh, the previous "fix" for if uint64(n)+s >= uint64(^uint(0)>>1) {
return nil, n, fmt.Errorf("invalid out of range length %v decoding []byte", uint64(n)+s)
} Which of course can overflow such that the sum is less than both Submitted another fix in #347, would appreciate a review. |
Sounds good, thank you @erikgrinaker. For testing we can do: |
Reading through the code in encoding.go, we've got this interesting pattern of code
iavl/encoding.go
Lines 15 to 25 in f5c104d
In that code block, you'll notice that:
a)
size, n, err := decodeUvarint(bz []byte) (uint64, int, error)
--> size is a uint64 containing the size aka length of the buffer to be allocatedb) To check for overflows, we use int(size): which checks if uint64->int returns a negative value
c) Unfortunately:
make([]byte, size)
uses the plain uint64 value unfettered and uncheckedPoint b) masks a problem with overflows; Point c) is where the problem is, and to cause this disruption, I can Uvarint encode any size larger than 32-bit ranges, the overflow will wrap around to it and given the fallacious check yet naked transmission to make, it'll panic with out of memory
Implications
Given that int's range is (-2147483648 to 2147483647) on 32-bit systems, and (-9223372036854775808 to 9223372036854775807) on 64-bit systems, on 32-bit systems, any value with a wrap around of greater than (1<<32), e.g. (int32(uint64(1<<32)+2)) == 2; will return that value so to falsely make all those checks pass and then poison the code with an OOM or runtime panic, I can do this
Recommendation
For robust checks we can do the following:
a) Ensure that size never exceeds the max value of int on either of 32-bit or 64-bit architecures
b) Ensure that make receives the same truncation used, so size is passed in as int(size) -- in fact just the lone change would have masked the problem
I believe this is a high value DOS vector that can take down various components
The text was updated successfully, but these errors were encountered: