signet: Fix uninitialized read in validation #20035
pull MarcoFalke wants to merge 3 commits into bitcoin:master from MarcoFalke:2009-signetUninitRead changing 2 files +9 −11-
MarcoFalke commented at 12:37 pm on September 29, 2020: member
-
MarcoFalke commented at 12:41 pm on September 29, 2020: member
Too sad that teaching the compiler to warn about this would make the code massively verbose, but maybe it is worth a shot now that this is the second time this year?
-
theStack commented at 12:54 pm on September 29, 2020: member
Concept ACK
The parameters
BIP16Exception
andBIP34Hash
are also not initialized for Signet? -
MarcoFalke commented at 1:01 pm on September 29, 2020: memberunit256 are default initialized to all-zeros
-
theStack commented at 1:24 pm on September 29, 2020: member
unit256 are default initialized to all-zeros
Right, in those case there is no uninitialized read.
Still, looking at mainnet, testnet and regtest, without exception all parameters (i.e. all 22 fields of
struct Params
) are explicitely initialized – hence I think for signet and all future *nets the same should be done. Both for consistency and do avoid such uninitialized read errors in the feature. The following params are not set for signet, i.e. uninitialized or default initialized:MinBIP9WarningHeight
(fixed in this PR)nMinimumChainWork
defaultAssumeValid
BIP16Exception
BIP34Hash
-
DrahtBot added the label Validation on Sep 29, 2020
-
practicalswift commented at 7:47 pm on September 29, 2020: contributor
@MarcoFalke Nice catch! How did you find this one?
It’s always a bit sad to see unitialized reads enter our code base without being caught automatically at CI stage by either dynamic analysis (MSAN or Valgrind) or static analysis :(
-
practicalswift commented at 4:32 am on September 30, 2020: contributor
The UUM takes place in
WarningBitsConditionChecker::Condition
which is executed in the message handler thread:0$ valgrind --exit-on-first-error=yes --track-origins=yes --suppressions=contrib/valgrind.supp src/bitcoind -signet 1… 2==9443== Thread 23 b-msghand: 3==9443== Conditional jump or move depends on uninitialised value(s) 4==9443== at 0x6F3E65: WarningBitsConditionChecker::Condition(CBlockIndex const*, Consensus::Params const&) const (validation.cpp:1862) 5==9443== by 0x744269: AbstractThresholdConditionChecker::GetStateFor(CBlockIndex const*, Consensus::Params const&, std::map<CBlockIndex const*, ThresholdState, std::less<CBlockIndex const*>, std::allocator<std::pair<CBlockIndex const* const, ThresholdState> > >&) const (versionbits.cpp:70) 6==9443== by 0x6D77FA: UpdateTip(CTxMemPool&, CBlockIndex const*, CChainParams const&) (validation.cpp:2458) 7==9443== by 0x6D8B64: CChainState::ConnectTip(BlockValidationState&, CChainParams const&, CBlockIndex*, std::shared_ptr<CBlock const> const&, ConnectTrace&, DisconnectedBlockTransactions&) (validation.cpp:2649) 8==9443== by 0x6D9712: CChainState::ActivateBestChainStep(BlockValidationState&, CChainParams const&, CBlockIndex*, std::shared_ptr<CBlock const> const&, bool&, ConnectTrace&) (validation.cpp:2781) 9==9443== by 0x6D9E8F: CChainState::ActivateBestChain(BlockValidationState&, CChainParams const&, std::shared_ptr<CBlock const>) (validation.cpp:2908) 10==9443== by 0x6E089E: ChainstateManager::ProcessNewBlock(CChainParams const&, std::shared_ptr<CBlock const>, bool, bool*) (validation.cpp:3864) 11==9443== by 0x3FADD3: PeerManager::ProcessMessage(CNode&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, CDataStream&, std::chrono::duration<long, std::ratio<1l, 1000000l> >, std::atomic<bool> const&) (net_processing.cpp:3506) 12==9443== by 0x3FDA75: PeerManager::ProcessMessages(CNode*, std::atomic<bool>&) (net_processing.cpp:3864) 13==9443== by 0x390065: CConnman::ThreadMessageHandler() (net.cpp:2155) 14==9443== by 0x3E018D: void std::__invoke_impl<void, void (CConnman::*&)(), CConnman*&>(std::__invoke_memfun_deref, void (CConnman::*&)(), CConnman*&) (invoke.h:73) 15==9443== by 0x3DB61C: std::__invoke_result<void (CConnman::*&)(), CConnman*&>::type std::__invoke<void (CConnman::*&)(), CConnman*&>(void (CConnman::*&)(), CConnman*&) (invoke.h:95) 16==9443== Uninitialised value was created by a heap allocation 17==9443== at 0x4C3052A: operator new(unsigned long) (vg_replace_malloc.c:342) 18==9443== by 0xA1C134: CreateChainParams(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (chainparams.cpp:495) 19==9443== by 0xA1C31B: SelectParams(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (chainparams.cpp:505) 20==9443== by 0x30ABB6: AppInit(int, char**) (bitcoind.cpp:82) 21==9443== by 0x30B53E: main (bitcoind.cpp:172)
-
practicalswift commented at 4:39 am on September 30, 2020: contributor
Unfortunately it seems like this UUM is not triggered by the code paths exercised by
feature_signet.py
. Covering the triggering code path in the functional test would be nice as part of this fix :)0$ test/functional/test_runner.py --valgrind --timeout-factor=0 feature_signet 1 2TEST | STATUS | DURATION 3 4feature_signet.py | ✓ Passed | 151 s 5 6ALL | ✓ Passed | 151 s (accumulated) 7Runtime: 151 s
-
kallewoof approved
-
kallewoof commented at 7:05 am on September 30, 2020: memberutACK
-
MarcoFalke force-pushed on Sep 30, 2020
-
MarcoFalke commented at 12:53 pm on September 30, 2020: memberPushed some non-bugfix changes
-
DrahtBot commented at 7:24 pm on September 30, 2020: member
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
Conflicts
No conflicts as of last run.
-
kallewoof commented at 4:43 am on October 1, 2020: memberre-ACK. Agree with moving chain work / assume valid comments.
-
practicalswift commented at 6:37 am on October 1, 2020: contributor@kallewoof @MarcoFalke Have someone investigated why this UUM appear not to be exercised in the functional tests?
-
kallewoof commented at 7:03 am on October 1, 2020: member
@practicalswift @MarcoFalke I made a minimal test that fails on master: https://github.com/kallewoof/bitcoin/tree/202010-signet-uuv-min9-test
0test/validation_block_tests.cpp:379: error: in "validation_block_tests/chainparameters": check chain->GetConsensus().MinBIP9WarningHeight == 0 has failed [809056355 != 0] 12020-10-01T06:51:08.729020Z [scheduler] scheduler thread exit 2test/validation_block_tests.cpp:376: Leaving test case "chainparameters"; testing time: 79044us 3test/validation_block_tests.cpp:33: Leaving test suite "validation_block_tests"; testing time: 13338900us 4test/validation_chainstate_tests.cpp:16: Test suite "validation_chainstate_tests" is skipped because disabled 5[...] 6wallet/test/scriptpubkeyman_tests.cpp:13: Test suite "scriptpubkeyman_tests" is skipped because disabled 7Leaving test module "Bitcoin Core Test Suite"; testing time: 13340245us 8 9*** 1 failure is detected in the test module "Bitcoin Core Test Suite"
The test succeeds on top of this PR.
-
MarcoFalke commented at 7:08 am on October 1, 2020: memberThe versionbits condition isn’t evaluated for the first retarget period after the genesis block, so no test hits it.
-
theStack approved
-
theStack commented at 7:11 am on October 1, 2020: memberACK fa82143e41c067e7d16d612c775701be94974d54 🩹
-
jonatack commented at 8:22 am on October 1, 2020: member
fa82143e maybe also change
0--- a/src/chainparams.cpp 1+++ b/src/chainparams.cpp 2@@ -365,7 +365,7 @@ public: 3 consensus.nSubsidyHalvingInterval = 150; 4 consensus.BIP16Exception = uint256(); 5 consensus.BIP34Height = 500; // BIP34 activated on regtest (Used in functional tests) 6- consensus.BIP34Hash = uint256(); 7+ consensus.BIP34Hash = uint256{};
-
in src/chainparams.cpp:327 in fa82143e41 outdated
314@@ -317,6 +315,7 @@ class SigNetParams : public CChainParams { 315 consensus.fPowNoRetargeting = false; 316 consensus.nRuleChangeActivationThreshold = 1916; 317 consensus.nMinerConfirmationWindow = 2016; 318+ consensus.MinBIP9WarningHeight = 0;
jonatack commented at 8:27 am on October 1, 2020:fadb384e perhaps group
MinBIP9WarningHeight
with the other height settings as done in the other three chains0--- a/src/chainparams.cpp 1+++ b/src/chainparams.cpp 2@@ -309,13 +309,13 @@ public: 3 consensus.BIP66Height = 1; 4 consensus.CSVHeight = 1; 5 consensus.SegwitHeight = 1; 6+ consensus.MinBIP9WarningHeight = 0; 7 consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks 8 consensus.nPowTargetSpacing = 10 * 60; 9 consensus.fPowAllowMinDifficultyBlocks = false; 10 consensus.fPowNoRetargeting = false; 11 consensus.nRuleChangeActivationThreshold = 1916; 12 consensus.nMinerConfirmationWindow = 2016; 13- consensus.MinBIP9WarningHeight = 0;
MarcoFalke commented at 8:32 am on October 1, 2020:I’ve put it in the line right before powLimit, like in all other chain constructorsjonatack commented at 8:27 am on October 1, 2020: memberGreat find – LGTM modulo the two commentsin src/chainparams.cpp:398 in fa82143e41 outdated
381@@ -378,8 +382,8 @@ class CRegTestParams : public CChainParams { 382 consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; 383 consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; 384 385- consensus.nMinimumChainWork = uint256S("0x00");
promag commented at 8:43 am on October 1, 2020:fa82143e41c067e7d16d612c775701be94974d54
Not sure what is the motivation of being explicit here, I’d just remove these cases.
MarcoFalke commented at 8:58 am on October 1, 2020:promag commented at 8:44 am on October 1, 2020: memberCode review ACK fa82143e41c067e7d16d612c775701be94974d54.jonatack commented at 11:28 am on October 1, 2020: memberCode review ACK fa82143e41c067e7d16d612c775701be94974d54practicalswift commented at 4:22 pm on October 1, 2020: contributorWant to share how you found this one (great find!)? :) Was it found by static analysis or dynamic analysis? Automatic testing or manual testing?
I’m thinking about what we can learn from this UUM case. More specifically I’m thinking about potential ways to improve manual and/or automated processes to catch UUM:s pre-merge (in the spirit of #18288, #17633, #18159, #18166 and similar attempts to kill this bug class for good) :)
FWIW, some previous UUM cases if someone wants to analyse specific instances of this bug class more in-depth:
- 2020: Use of uninitialized memory in Erlay networking code - found pre-merge
- 2020: Use of uninitialized memory in BIP324 encrypted p2p transport de-/serializer code (truth in advertising: I haven’t verified this one by writing a PoC) - found pre-merge
- 2020: util: Avoid potential uninitialized read in FormatISO8601DateTime(int64_t) by checking gmtime_s/gmtime_r return
- 2019: Use of uninitialized memory in networking code when receiving a transaction we already have - found post-merge
- 2019: wallet: Uninitialized read in bumpfee(…)
- 2018: wallet: Fix non-determinism in ParseHDKeypath(…). Avoid using an uninitialized variable in path calculation.
- 2018: wallet: Fix use of uninitialized value bnb_used in CWallet::CreateTransaction(…)
- 2017: [net] Fix use of uninitialized value in getnetworkinfo(const JSONRPCRequest&)
- 2017: [test] Avoid reading a potentially uninitialized variable in tx_invalid-test (transaction_tests.cpp)
MarcoFalke commented at 4:26 pm on October 1, 2020: memberIt was found by reading/reviewing the codemichaelfolkson commented at 12:52 pm on October 6, 2020: contributorConcept ACK, Approach ACK
Haven’t looked into @kallewoof test but presumably that will be in a follow up PR.
Thanks for sharing resources too @practicalswift. Added to a StackExchange post.
practicalswift commented at 6:29 pm on October 6, 2020: contributorACK fa82143e41c067e7d16d612c775701be94974d54: diff looks correctMarcoFalke added this to the milestone 0.21.0 on Oct 9, 2020laanwj commented at 8:57 am on October 15, 2020: memberSorry, needs rebase (probably with taproot changes)signet: Fix uninitialized read in validation fa64892b82doc: Move assumed-values doxygen comments to header fa729cdb2cMarcoFalke force-pushed on Oct 15, 2020MarcoFalke commented at 9:30 am on October 15, 2020: memberRebasedkallewoof commented at 3:43 am on October 16, 2020: memberTypo in 3rd commit message (unit256
).Initialize default-initialized uint256 consensus params to zero explicitly fa723e3d43MarcoFalke force-pushed on Oct 16, 2020MarcoFalke commented at 4:32 am on October 16, 2020: memberFixed typo in commit msgpracticalswift commented at 6:07 am on October 16, 2020: contributorre-ACK fa723e3d43e63e8424d97d21d8f2cc8136aba206: patch still looks correctkallewoof commented at 6:47 am on October 16, 2020: memberReACK fa723e3d43e63e8424d97d21d8f2cc8136aba206theStack approvedtheStack commented at 8:15 am on October 16, 2020: memberre-ACK fa723e3d43e63e8424d97d21d8f2cc8136aba206 🍐laanwj merged this on Oct 16, 2020laanwj closed this on Oct 16, 2020
MarcoFalke deleted the branch on Oct 16, 2020sidhujag referenced this in commit 0b2a28fa79 on Oct 16, 2020DrahtBot locked this on Feb 15, 2022
MarcoFalke theStack practicalswift kallewoof DrahtBot jonatack promag michaelfolkson laanwjLabels
ValidationMilestone
0.21.0
This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-11-21 15:12 UTC
More mirrored repositories can be found on mirror.b10c.me