Make Assert(…) usable in all contexts. Make implicit assumptions explicit. #20122

pull practicalswift wants to merge 2 commits into bitcoin:master from practicalswift:Assert changing 4 files +5 −4
  1. practicalswift commented at 6:42 pm on October 11, 2020: contributor
    • Make Assert(…) (util/check.h) usable in all contexts.
    • Use Assert(…) to make implicit assumptions explicit.

    Before the first commit:

    0./chain.h:404:23: error: C++11 only allows consecutive left square brackets when introducing an attribute
    1        return (*this)[Assert(pindex)->nHeight] == pindex;
    2                      ^
    
  2. util: Allow Assert(...) to be used in all contexts 91bdd43998
  3. Use Assert(...) to make implicit assumptions explicit d697e3c401
  4. DrahtBot added the label P2P on Oct 11, 2020
  5. DrahtBot added the label Utils/log/libs on Oct 11, 2020
  6. DrahtBot added the label Validation on Oct 11, 2020
  7. DrahtBot commented at 2:11 pm on October 13, 2020: member

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #20138 (net: Assume that SetCommonVersion is called at most once per peer by MarcoFalke)

    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.

  8. in src/chain.h:404 in d697e3c401
    400@@ -400,7 +401,7 @@ class CChain {
    401 
    402     /** Efficiently check whether a block is present in this chain. */
    403     bool Contains(const CBlockIndex *pindex) const {
    404-        return (*this)[pindex->nHeight] == pindex;
    405+        return (*this)[Assert(pindex)->nHeight] == pindex;
    


    ajtowns commented at 1:57 am on October 15, 2020:

    Introducing the non-null attribute for the pindex argument might be a useful addition here?

    I tried converting this to CBlockIndex& index and it has some flow-on effects (to CChain::Next, CChain::FindFork, and others) that look like they might be worth investigating and cleaning up.

  9. in src/net_processing.cpp:4276 in d697e3c401
    4272@@ -4273,7 +4273,7 @@ bool PeerManager::SendMessages(CNode* pto)
    4273                     bool fGotBlockFromCache = false;
    4274                     {
    4275                         LOCK(cs_most_recent_block);
    4276-                        if (most_recent_block_hash == pBestIndex->GetBlockHash()) {
    4277+                        if (most_recent_block_hash == Assert(pBestIndex)->GetBlockHash()) {
    


    ajtowns commented at 2:03 am on October 15, 2020:

    Might be clearer to have:

    0             if (!fRevertToInv && !vHeaders.empty()) {
    1+                assert(pBestIndex != nullptr); // was assigned to non-null pindex in order for vHeaders to be populated
    2                 if (vHeaders.size() == 1 && state.fPreferHeaderAndIDs) {
    

    instead? (prior related PR #13548)

  10. in src/validation.cpp:4324 in d697e3c401
    4320@@ -4321,7 +4321,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
    4321         return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", ::ChainActive().Height() - pindexFailure->nHeight + 1, nGoodTransactions);
    4322 
    4323     // store block count as we move pindex at check level >= 4
    4324-    int block_count = ::ChainActive().Height() - pindex->nHeight;
    4325+    int block_count = ::ChainActive().Height() - Assert(pindex)->nHeight;
    


    ajtowns commented at 2:23 am on October 15, 2020:

    I think this assertion is effectively testing if ::ChainActive().Tip() == nullptr as the other ways of setting pindex all seem to be pindex = pindex->pprev after testing pindex->pprev. Might be better to put the Assert there?

    There are other places where we assume ChainActive().Tip() is non-null (ie, we immediately dereference it), but at a quick glance I couldn’t assure myself that was actually always true. Might be better to actually make it always true (and add a non-null attribute on the response?).

  11. ajtowns commented at 2:47 am on October 15, 2020: member

    Definite ack on the first commit; not super-convinced by the actual uses.

    Are these changes inspired by static analysis? They look like they’re making errors go away more than making it clear why the code is safe.

    I think doing an inline Assert(foo) only makes sense when foo is complicated/slow or has side-effects; when foo is just a pointer/int, better to have the assertion on its own line/statement so that it’s easier to spot.

  12. practicalswift closed this on Oct 19, 2020

  13. practicalswift commented at 8:45 pm on October 19, 2020: contributor

    @ajtowns I’m leaving this one as up for grabs now that the first commit is included in #20138 :)

    They look like they’re making errors go away more than making it clear why the code is safe.

    They won’t make any error go away, but they guarantee we get an assertion failure in case of an error (instead of UB) :)

  14. ajtowns commented at 1:19 am on October 21, 2020: member
    @practicalswift Sorry, I meant “making complaints from automated tools go away, without fixing any underlying bugs or making it easier for people to see why the code is safe” :) I think adding asserts should be making it clearer for people to see why the code is safe as well as automated tools. (If you are getting this from an automated tool, are there reports from it around? I remember there were previously somewhere)
  15. practicalswift commented at 8:53 am on October 21, 2020: contributor

    I think adding asserts should be making it clearer for people to see why the code is safe as well as automated tools.

    I agree about that statement :)

    In addition to that use I also think assertions can be useful to get deterministic termination in case where we otherwise would get a null pointer dereference (if our assumption of non-null turns out to be false).

    Why is that desired?

    One might think that a null pointer dereference is guaranteed to lead to immediate termination. Unfortunately that is not the case: deterministic termination is the best outcome. Another possible outcome is what in the literature is called “optimization-unsafety” which can be problematic from a security perspective.

    There is a neat paper covering optimization-safe code called “Towards Optimization-Safe Systems: Analyzing the Impact of Undefined Behavior” which I recommend for context.

    Personally I think adding assertions can be handy as a cheap way to avoid some instances of potentially “optimization-unsafe code”. That is my personal opinion: I know others might not agree and that is fine of course :)

    (If you are getting this from an automated tool, are there reports from it around? I remember there were previously somewhere)

    IIRC Coverity and Clang’s Cross Translation Unit (CTU) static analysis flag these cases. I think most static analysers doing pointer analysis would flag at least some of these since it is non-trivial for them to infer non-nullptr in these cases AFAICT. I wouldn’t be surprised if PVS Studio and cppcheck flags these too as an example.

    I don’t have any reports readily available, but feel free to check the instructions in #18920 for instructions on how to get Bitcoin Core reports from Clang Static Analysis, clang-tidy and cppcheck. It only takes a couple of minutes of setup :)

    Hope that helps! :)

  16. practicalswift deleted the branch on Apr 10, 2021
  17. DrahtBot locked this on Aug 18, 2022

github-metadata-mirror

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-12-18 21:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me