-
Notifications
You must be signed in to change notification settings - Fork 38.7k
script: prevent UB when computing abs value for num opcode serialize #18413
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
Conversation
|
This shouldn't be needed, I think, because |
|
I must be wrong, because I don't really understand why it's possible that that fuzzer is able to trigger a negation of -2^63. |
|
@sipa FWIW: |
d209eb7 to
bab50c5
Compare
src/script/script.h
Outdated
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.
I think you want to use std::abs, which since C++11 is overloaded to work for all signed integer types as input (avoiding the assumption that long long is enough for 64 bits, and also avoiding promoting values to higher than 64 bits in case long long is even bigger).
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 thanks I'm testing this. I thought that llabs showed more clearly the intend but using abs might be better if you want to switch to a smaller type later indeed.
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.
Oh no, this won't ever switch to a smaller type.
I'm just commenting this because hardcoding the assumption that long long = 64 bits seems bad (if there were an abs64bit or so that would seem ideal, but there isn't). Thankfully, thanks to std::abs being overloaded for all integer types, there is no need for such an assumption.
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.
Hah indeed yes, this is better that way then.
In the input provided by @practicalswift above, I could not find the proposed input in https://github.com/bitcoin-core/qa-assets/ nor bitcoin-core/qa-assets#7 so I guess the end goal here is to increase code coverage by adding more data to qa-assets in a future PR ? TL;DR I reckon there is 3 options:
From what I gather, So if you find this PR too intrusive, limiting the output of |
bab50c5 to
699f677
Compare
|
Code review ACK bab50c56e1fa2477f7d625855485e8527c78747c, with either It's more obviously correct and should avoid the fuzzer issues we're seeing. I have also verified that there are no consensus-critical code paths that can trigger this (they're all restricted to much smaller numbers). I think the use of |
|
OK, thanks. (also note that I would'nt mind trying to do the PR for the |
|
@pierreN Feel free to restrict the number conversion in ParseScript yourself if you like. |
theStack
left a comment
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.
I'm afraid that using std::abs instead of the manual calculation doesn't get rid of the UB.
According to https://en.cppreference.com/w/cpp/numeric/math/abs: "Computes the absolute value of an integer number. The behavior is undefined if the result cannot be represented by the return type."
So, for the corner case input -2^63 (=numeric_limits<int64_t>::min()), both variants are UB (probably internally, std::abs does exactly work the same than our manual way -- one example for libstdc++: https://github.com/openbsd/src/blob/master/gnu/lib/libstdc%2B%2B/libstdc%2B%2B/include/c_std/std_cstdlib.h#L149).
See also https://embeddedgurus.com/stack-overflow/2012/02/the-absolute-truth-about-abs/ (talks about C, but that applies to C++ as well I'd guess).
|
Thanks, it seems you are right. I guess the fuzzer is not triggered here since we use I'm not sure that adding a special check for -2^63 inside Hence, I reckon there is 2 possible options for this PR :
What do you guys think ? |
|
@theStack Looking at the standard library implementation isn't relevant, as it is allowed to rely on platform-specific behavior. However, it looks like std::abs is indeed specified to be undefined if the result cannot be represented in the input type. What is wrong with An alternative can be |
699f677 to
abeae82
Compare
|
The issue with It seems however that 2s complement by hand works : https://godbolt.org/z/cr-phu so I updated the branch accordingly. Thanks. |
|
FWIW, I just checked where else the manual Lines 18 to 26 in 5b4a9f4
I didnt' check though in which range the amount values are passed from the call-sites. Maybe you want to look into it if UB is also a problem here in the course of this (or a possible future) PR.
|
Thanks. From a quick check and believing that the variable name It seems it costs nothing to prevent it though, and I'd be happy to do a PR for that if others agree. |
|
@theStack Good catch regarding bitcoin/src/test/fuzz/integer.cpp Lines 122 to 128 in 3f5107d
I think ideally all our functions should i.) state pre-conditions clearly (using documentation and/or assertions), and ii.) robustly handle all inputs that are not explicitly disallowed by the clearly stated pre-conditions :) |
Looking at the interesting With a quick regexp search I also found occurences in :
|
9ab14e4 Limit decimal range of numbers ParseScript accepts (pierrenn) Pull request description: Following up on this suggestion : #18413 (comment), prevent the output of `atoi64` in the `core_read.cpp:ParseScript` helper to send to `CScriptNum::serialize` values wider than 32-bit. Since the `ParseScript` helper is only used by the tool defined in `bitcoin-tx.cpp`, this only prevents users to provide too much unrealistic values. ACKs for top commit: laanwj: ACK 9ab14e4 Tree-SHA512: ee228269d19d04e8fee0aa7c0ae2bb0a2b437b8e574356e8d9b2279318242057d51fcf39a842aa3afe27408d0f2d5276df245d07a3f4828644a366f80587b666
I suggest doing that in a follow-up PR to keep this PR focused. |
|
@practicalswift What is the fuzzer that hits this in current master? And what is the backtrace? |
|
@MarcoFalke Oh, it seems I got the crashes mixed up. This is the remaining case (it is unrelated to this PR): This remaining case is fixed in #18383. |
abeae82 to
60175ab
Compare
60175ab to
c6819c4
Compare
|
So following this comment : #18491 (comment) just added |
|
Could also add the following diff: diff --git a/src/test/fuzz/scriptnum_ops.cpp b/src/test/fuzz/scriptnum_ops.cpp
index db44bb9e19..d45c85bc51 100644
--- a/src/test/fuzz/scriptnum_ops.cpp
+++ b/src/test/fuzz/scriptnum_ops.cpp
@@ -128,10 +128,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
script_num &= fuzzed_data_provider.ConsumeIntegral<int64_t>();
break;
}
- // Avoid negation failure:
- // script/script.h:332:35: runtime error: negation of -9223372036854775808 cannot be represented in type 'int64_t' (aka 'long'); cast to an unsigned type to negate this value to itself
- if (script_num != CScriptNum{std::numeric_limits<int64_t>::min()}) {
(void)script_num.getvch();
- }
}
} |
c6819c4 to
a3fc3c5
Compare
|
Ow, right, thanks. Branch updated. |
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsNo conflicts as of last run. |
a3fc3c5 to
7b47f78
Compare
7b47f78 to
2748e87
Compare
|
It's been around a month this PR has been opened - do you guys think I should close it? Thanks. |
|
ACK 2748e87 |
|
ACK 2748e87, only checked that the bitcoind binary does not change with clang -O2 🎓 Show signature and timestampSignature: Timestamp of file with hash |
|
ACK 2748e87 |
|
@pierreN no worries, a month (or much, much longer) isn't unusual due to the number of PRs opened relative to the number of people who review. |
|
Ow my bad - OK, thanks for the ACKs, I'll leave the PR open. |
…num opcode serialize 2748e87 script: prevent UB when computing abs value for num opcode serialize (pierrenn) Pull request description: This was reported by practicalswift here bitcoin#18046 It seems that the original author of the line used a reference to glibc `abs`: https://github.com/lattera/glibc/blob/master/stdlib/abs.c However depending on some implementation details this can be undefined behavior for unusual values. A detailed explanation of the UB is provided here : https://stackoverflow.com/questions/17313579/is-there-a-safe-way-to-get-the-unsigned-absolute-value-of-a-signed-integer-with (by [Billy O'Neal](https://twitter.com/malwareminigun)) Simple relevant godbolt example : https://godbolt.org/z/yRwtCG Thanks! ACKs for top commit: sipa: ACK 2748e87 MarcoFalke: ACK 2748e87, only checked that the bitcoind binary does not change with clang -O2 🎓 practicalswift: ACK 2748e87 Tree-SHA512: 539a34c636c2674c66cb6e707d9d0dfdce63f59b5525610ed88da10c9a8d59d81466b111ad63b850660cef3750d732fc7755530c81a2d61f396be0707cd86dec
Given that bitcoin#18413 has not been backported.
Summary: Backport of core [[bitcoin/bitcoin#18413 | PR18413]]. Test Plan: ninja all check-all ninja bitcoin-fuzzers ./test/fuzz/test_runner.py <path_to_corpus> Reviewers: #bitcoin_abc, deadalnix Reviewed By: #bitcoin_abc, deadalnix Differential Revision: https://reviews.bitcoinabc.org/D9017
921c4a4 script: prevent UB when computing abs value for num opcode serialize (pierrenn) 58ebadb Fix memory leak in net_tests (Pieter Wuille) 786f396 Fix memory leak in wallet tests (random-zebra) d8b125d Avoid integer overflows in scriptnum tests (Pieter Wuille) Pull request description: Fix `scriptnum_tests` currently failing with debug enabled, due to integer overflow, backporting bitcoin#9512. Last commit coming from bitcoin#18413. ACKs for top commit: Fuzzbawls: ACK 921c4a4 furszy: utACK 921c4a4 and merging.. Tree-SHA512: 07827bfda430e0704b427e373700ff37049bddaa59a56d415618441cb63fb8500bdb19f9df834574ade4618f0825853e44255f3a304486b4bbcb60dbbb382938
Given that #18413 has not been backported.
This was reported by practicalswift here #18046
It seems that the original author of the line used a reference to glibc
abs: https://github.com/lattera/glibc/blob/master/stdlib/abs.cHowever depending on some implementation details this can be undefined behavior for unusual values.
A detailed explanation of the UB is provided here : https://stackoverflow.com/questions/17313579/is-there-a-safe-way-to-get-the-unsigned-absolute-value-of-a-signed-integer-with (by Billy O'Neal)
Simple relevant godbolt example : https://godbolt.org/z/yRwtCG
Thanks!