-
Notifications
You must be signed in to change notification settings - Fork 37.7k
fuzz: BIP 30, CVE-2018-17144 #17860
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
fuzz: BIP 30, CVE-2018-17144 #17860
Conversation
Super strong concept ACK: very happy to see more people interested in adding fuzzers. Very welcome! :) Also: the habit of after fixing a security bug also implementing a fuzz target that could have found the bug is something I think we should strive for. I think @kcc (known for making C++ reasonably sane by introducing the sanitizers, libFuzzer, OSS-Fuzz, CFI, etc.) first coined the term "regression fuzzing" for that :) A good example of "regression fuzzing" was this OOB read in |
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
src/bench/block_assemble.cpp
Outdated
@@ -30,7 +30,7 @@ static void AssembleBlock(benchmark::State& state) | |||
std::array<CTransactionRef, NUM_BLOCKS - COINBASE_MATURITY + 1> txs; | |||
for (size_t b{0}; b < NUM_BLOCKS; ++b) { | |||
CMutableTransaction tx; | |||
tx.vin.push_back(MineBlock(g_testing_setup->m_node, SCRIPT_PUB)); | |||
tx.vin.push_back(CTxIn{MineBlock(g_testing_setup->m_node, SCRIPT_PUB)}); |
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.
would emplace_back be more efficient / proper here?
This is kind of an aside, but thought I'd post here since this harness makes use of the I modified |
Very nice fuzzing harness! Thanks for adding! Tested ACK 5b90f46 modulo squash of last commit Compared to the other fuzzers this one is very slow (6 executions/second), but I guess that is largely unavoidable given the code being tested :) Also, a slow fuzzing harness is better than no fuzzing harness. |
Good question. This is actually a question in the review club next week: https://bitcoincore.reviews/17860.html You (and all other reviewers) are invited to join and share your thoughts and ideas. |
ACK 8b868f1 |
I am trying fuzzing One possible cause for crash is memory limit as reported by the fuzzer (52MB), and it suggests doing this: While setting ulimit at [51<<10] gives a I am new to fuzzing, and am not very sure whats exactly happening here. Seems like its crashing for the very first test case itself. Would appreciate some guidance to navigate this. I am at |
@codeShark149 Does it work if you fuzz with Try this:
|
@codeShark149 I fixed that error on macOS by turning AFL's forkserver off |
I've played a bit more with this harness: I'm currently using a seed corpus of 3247 files. I'm fuzzing at a rate of 6 execs/second which means that it takes almost ten minutes just to initialize A lot of the time is spent setting up (and tearing down) the Can we think of ways to speed up this fuzzing harness? Perhaps a fully featured |
@practicalswift I suspected as much, that the testing harness is the heavy part of the test. On my non-beefy machine it looks like I'm getting 1 a second? Alternative might be to allow it to run longer to build a longer chain? |
Another data point: As a crude way to quantify the impact of the test setup I made |
You mind posting a profiler view of this? I'll try to optimize this if I can. (VS has an optimization profiler section). |
@sanjaykdragon I don't have any profiler view to post :) The quoted numbers are from the output you'll get when running
|
@practicalswift yes libfuzzer can fuzz it.
@Crypt-iQ thanks for suggesting but for some reason that did'nt work. The problem is simple though. The instrumented binary takes about 103 MB memory to run. So just had to change Some note on execution: libfuzzer reported around 5 exec/sec. AFL reported 3.13/sec. Which is natural given the thing being tested. |
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.
ACK 8b868f1
Tested with libFuzzer, seeing 6 executions/second on a 2 year old Intel Core i7-6500U CPU 2.50GHz × 4 running Debian Linux.
Edit: Really slowing down as it runs into the thousands of iterations; showing 0 exec/s and making an execution every few seconds.
Am doing further testing with the CVE fix reverted. I'm not sure this test is effective enough.
Tested ACK 8b868f1 Reviewed code and successfully ran with AFL. |
Now running since 2 1l2 days, taking > 1 gb of RAM, and it's slowed to a crawl of < 10 executions per minute and still has not found the CVE. It also drains my laptop battery at least twice faster than normal.
Maybe I've done something dumb in removing and rebuilding without the CVE fix: diff --git a/src/consensus/tx_check.cpp b/src/consensus/tx_check.cpp
@@ -36,12 +36,6 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
// Failure to run this check will result in either a crash or an inflation bug, depending on the implementation of
// the underlying coins database.
- std::set<COutPoint> vInOutPoints;
- for (const auto& txin : tx.vin) {
- if (!vInOutPoints.insert(txin.prevout).second)
- return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate");
- }
I'm not sure why the test runs continually slower. Unless my experience is atypical, this seems to be a real handicap. This also underscores how necessary it is to run the fuzz tests continuously for them to be most effective. |
@jonatack The increase in memory usage and slow-down is clearly undesired. Which version of clang are you using? I presume 3.8? Could you try with https://packages.debian.org/stretch-backports/clang-6.0 , or even later, if possible? Your patch to reintroduce the CVE looks correct. |
With the current utils it is only possible to mine valid blocks. This commit adds new util methods to mine invalid blocks.
Concept ACK, will review this week |
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.
Tested ACK fa18fe3
I reintroduced CVE-2018-17144 and the fuzz target found the bug after ~1 day on 10 cores.
#2133 NEW cov: 8553 ft: 28450 corp: 659/48Kb lim: 124 exec/s: 19 rss: 110Mb L: 69/124 MS: 2 PersAutoDict-ChangeASCIIInt- DE: "\377\377\377>"-
#2151 NEW cov: 8553 ft: 28451 corp: 660/48Kb lim: 124 exec/s: 19 rss: 110Mb L: 93/124 MS: 3 CrossOver-EraseBytes-CopyPart-
#2153 NEW cov: 8553 ft: 28452 corp: 661/48Kb lim: 124 exec/s: 19 rss: 110Mb L: 121/124 MS: 2 ChangeBinInt-ShuffleBytes-
#2158 NEW cov: 8553 ft: 28463 corp: 662/49Kb lim: 124 exec/s: 19 rss: 110Mb L: 124/124 MS: 5 ShuffleBytes-ChangeBit-ChangeBinInt-ShuffleBytes-PersAutoDict- DE: "\002\000\000\000"-
#2173 NEW cov: 8553 ft: 28464 corp: 663/49Kb lim: 124 exec/s: 19 rss: 110Mb L: 77/124 MS: 5 ChangeBinInt-CrossOver-ChangeBit-InsertRepeatedBytes-ChangeByte-
#2174 NEW cov: 8553 ft: 28465 corp: 664/49Kb lim: 124 exec/s: 19 rss: 110Mb L: 99/124 MS: 1 ChangeBinInt-
fuzz: test/fuzz/utxo_total_supply.cpp:84: auto utxo_total_supply_fuzz_target(FuzzBufferType)::(anonymous class)::operator()() const: Assertion `circulation == utxo_stats.total_amount' failed.
==16109== ERROR: libFuzzer: deadly signal
#0 0x55dc406d0314 in __sanitizer_print_stack_trace (/root/bitcoin/src/test/fuzz/fuzz+0x9c1314) (BuildId: 2dbb839c5abc29bd1bf44537252b5378dcc49df4)
#1 0x55dc406a75d7 in fuzzer::PrintStackTrace() (/root/bitcoin/src/test/fuzz/fuzz+0x9985d7) (BuildId: 2dbb839c5abc29bd1bf44537252b5378dcc49df4)
#2 0x55dc4068cc33 in fuzzer::Fuzzer::CrashCallback() (/root/bitcoin/src/test/fuzz/fuzz+0x97dc33) (BuildId: 2dbb839c5abc29bd1bf44537252b5378dcc49df4)
#3 0x7fd78765472f (/lib/x86_64-linux-gnu/libpthread.so.0+0x1272f) (BuildId: 1545d6e6c7da9e2e706253a30cbfe720b9101e62)
Minimized input in base64: zhoHDwcH+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4m5ubm/gI+Pj4+Pj4m5ub mwj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+A==
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.
Code review ACK fa2d8b6
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.
Tested ACK fa2d8b6
I was also able to catch the reintroduced CVE-2018-17144 (with the previous commit fa18fe3, current one is still running).
I can't really see how this actually fuzzes BIP42. Wouldn't the fuzzer need to create millions of blocks to detect it, so if we'd reintroduce that bug, the fuzzer would never be able to find it in practice?
Finally, I also observed that the fuzzer gets slower with time and after a few hours creates inputs that will take several seconds to run. That seems unavoidable, because the chains that are created are getting longer, and each block must be mined at a small but non-zero difficulty. However, it means that we should probably keep an eye on the qa-corpus seeds once this gets merged.
|
||
if (duplicate_coinbase_height == ActiveHeight()) { | ||
// we mined the duplicate coinbase | ||
assert(current_block->vtx.at(0)->vin.at(0).scriptSig == duplicate_coinbase_script); |
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 is this check inside the was_valid
branch?
As far as I understand it, the second block with the duplicate coinbase could be valid (if the coinbase from block 1 was spent), or BIP-30 invalid (otherwise) - but this should hold regardless.
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.
Yes, could move outside
Yeah, it was mostly a joke. Now with LIMITED_WHILE this is no longer possible to fuzz at all. |
I guess |
fafb4da fuzz: Avoid timeout in utxo_total_supply (MarcoFalke) Pull request description: Looks like for high block counts it may be better to mock the chain, otherwise a high limit will lead to fuzz input bloat and timeouts, see bitcoin/bitcoin#17860 (comment). It can be checked that the fuzz target can still find the CVE, see bitcoin/bitcoin#17860 (review) with a diff of: ```diff diff --git a/src/consensus/tx_check.cpp b/src/consensus/tx_check.cpp index f949655..6f4cfb5f51 100644 --- a/src/consensus/tx_check.cpp +++ b/src/consensus/tx_check.cpp @@ -39,8 +39,6 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state) // the underlying coins database. std::set<COutPoint> vInOutPoints; for (const auto& txin : tx.vin) { - if (!vInOutPoints.insert(txin.prevout).second) - return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate"); } if (tx.IsCoinBase()) ``` Also, fix a nit, see bitcoin/bitcoin#17860 (comment) ACKs for top commit: dergoegge: ACK fafb4da Tree-SHA512: a28fe9cd6ebb4c9bed5a5b35be76c1c436a87586c8fc3b3c4c8559a4a77ac08098324370da421d794c99579882c0872b6b29415de47ade6a05a08504a3d494c4
fafb4da fuzz: Avoid timeout in utxo_total_supply (MarcoFalke) Pull request description: Looks like for high block counts it may be better to mock the chain, otherwise a high limit will lead to fuzz input bloat and timeouts, see bitcoin#17860 (comment). It can be checked that the fuzz target can still find the CVE, see bitcoin#17860 (review) with a diff of: ```diff diff --git a/src/consensus/tx_check.cpp b/src/consensus/tx_check.cpp index f949655..6f4cfb5f51 100644 --- a/src/consensus/tx_check.cpp +++ b/src/consensus/tx_check.cpp @@ -39,8 +39,6 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state) // the underlying coins database. std::set<COutPoint> vInOutPoints; for (const auto& txin : tx.vin) { - if (!vInOutPoints.insert(txin.prevout).second) - return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate"); } if (tx.IsCoinBase()) ``` Also, fix a nit, see bitcoin#17860 (comment) ACKs for top commit: dergoegge: ACK fafb4da Tree-SHA512: a28fe9cd6ebb4c9bed5a5b35be76c1c436a87586c8fc3b3c4c8559a4a77ac08098324370da421d794c99579882c0872b6b29415de47ade6a05a08504a3d494c4
Add a validation fuzz test for BIP 30 and CVE-2018-17144