8000 Fix UBSan warnings triggered when loading corrupt mempool.dat files by rajarshimaitra · Pull Request #19381 · bitcoin/bitcoin · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Fix UBSan warnings triggered when loading corrupt mempool.dat files #19381

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

Closed
wants to merge 1 commit into from

Conversation

rajarshimaitra
Copy link
Contributor

This fixes some undefined behavior observed in the issue #19728.

The bugs can be reproduced by using corpus added here and the harness added in PR#19259

@rajarshimaitra rajarshimaitra changed the title Fixes UB reported in issue #19278 tests: Fixes UB reported in issue #19278 Jun 25, 2020
Copy link
Member
@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK, will review

@practicalswift
Copy link
Contributor
practicalswift commented Jun 25, 2020

Concept ACK: thanks for fixing! :)

In case of CAmount:s outside of the money range: should we perhaps let LoadMempool return false on those to indicate that something went (very) wrong (instead of just skipping PrioritiseTransaction)?

Update: Negative deltas are allowed!

@maflcko maflcko removed the Tests label Jun 25, 2020
@fanquake fanquake changed the title tests: Fixes UB reported in issue #19278 validation: Fixes UB reported in issue #19278 Jun 26, 2020
@rajarshimaitra
Copy link
Contributor Author
rajarshimaitra commented Jun 27, 2020

@practicalswift I was thinking along the same line. Also, It seems better to initiate shutdown once something like this happens. As just returning false in LoadMempool doesn't seem to be triggering any catastrophe, and I am not sure what the node is doing about its mempool when loading from disk fails. The node seems to keep on running with just a failure message, My guess is its creates a fresh mempool and override the data in the disk. Which can seem fine, unless someone specifically wants a particular mempool and if the disk data is corrupted they might not notice that something is wrong.

So I made it to initiate shutdown if loadmempool fails. Not sure if that's strictly necessary, looking for suggestions here before I update the PR.

The log

2020-06-27T14:19:55Z Cant load mempool, something went wrong, initiating shutdown.
2020-06-27T14:19:55Z Bound to [::]:18444
2020-06-27T14:19:55Z Bound to 0.0.0.0:18444
2020-06-27T14:19:55Z init message: Loading P2P addresses...
2020-06-27T14:19:55Z Loaded 0 addresses from peers.dat  1ms
2020-06-27T14:19:55Z init message: Starting network threads...
2020-06-27T14:19:55Z net thread start
2020-06-27T14:19:55Z dnsseed thread start
2020-06-27T14:19:55Z 0 addresses found from DNS seeds
2020-06-27T14:19:55Z dnsseed thread exit
2020-06-27T14:19:55Z init message: Done loading
2020-06-27T14:19:55Z addcon thread start
2020-06-27T14:19:55Z opencon thread start
2020-06-27T14:19:55Z torcontrol thread start
2020-06-27T14:19:55Z msghand thread start
2020-06-27T14:19:55Z tor: Thread interrupt
2020-06-27T14:19:55Z opencon thread exit
2020-06-27T14:19:55Z Shutdown: In progress...
2020-06-27T14:19:55Z torcontrol thread exit
2020-06-27T14:19:55Z addcon thread exit
2020-06-27T14:19:55Z net thread exit
2020-06-27T14:19:55Z msghand thread exit
2020-06-27T14:19:55Z scheduler thread exit
2020-06-27T14:19:55Z FlushStateToDisk: write coins cache to disk (0 coins, 0kB) started
2020-06-27T14:19:55Z FlushStateToDisk: write coins cache to disk (0 coins, 0kB) completed (0.00s)
2020-06-27T14:19:55Z FlushStateToDisk: write coins cache to disk (0 coins, 0kB) started
2020-06-27T14:19:55Z FlushStateToDisk: write coins cache to disk (0 coins, 0kB) completed (0.00s)
2020-06-27T14:19:55Z Shutdown: done

Also please give suggestions for a better error message.

@practicalswift
Copy link
Contributor
practicalswift commented Jun 28, 2020

@rajarshimaitra This is such an unlikely error that I don't think we need to add any special logic beyond returning false in case of !MoneyRange(…) :)

Update: Negative deltas are allowed!

@rajarshimaitra
Copy link
Contributor Author

@practicalswift updated to return failure instead of skipping PrioritiseTransaction.

@laanwj laanwj added the Mempool label Jul 1, 2020
@practicalswift
Copy link
Contributor
practicalswift commented Jul 1, 2020

@rajarshimaitra Great! What about doing the same thing for the other PrioritiseTransaction call -- that is return false in the case of !MoneyRange(i.second) :)

Update: Negative deltas are allowed!

@rajarshimaitra
Copy link
Contributor Author

@practicalswift silly me. Should have done that. will push the same.

@DrahtBot
Copy link
Contributor
DrahtBot commented Jul 11, 2020

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

Conflicts

No conflicts as of last run.

Copy link
Contributor
@practicalswift practicalswift left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK modulo nits

Could perhaps use a more descriptive PR title and commit message: what about something along the lines of "Fix UBSan warnings triggered when loading corrupt mempool.dat files"? :)

@rajarshimaitra
Copy link
Contributor Author

@practicalswift addressed all your comments and rebased.

@practicalswift
Copy link
Contributor
practicalswift commented Jul 13, 2020

@rajarshimaitra I think you still want if (amountdelta) { before doing pool.PrioritiseTransaction(tx->GetHash(), amountdelta); in order to not change the existing logic.

I suggest leaving the existing PrioritiseTransaction logic intact in both cases, and simply add two stand-alone MoneyRange checks:

Update: Negative deltas allowed.

Something along the lines of …

if (!MoneyRange(amountdelta)) {
    return false;
}
if (amountdelta) {
    pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
}

... and ...

if (!MoneyRange(i.second)) {
    return false;
}
pool.PrioritiseTransaction(i.first, i.second);

Also, would you mind also updating the PR title to the more descriptive commit message? :)~~

pool.PrioritiseTransaction(i.first, i.second);
if (!MoneyRange(i.second)) {
return false;
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} else {

nit: No need to change indentation on early-return. (same above)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I didn't get it. Can you explain a bit what are you suggesting here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was fixed in your last push AFAICT :)

@rajarshimaitra
Copy link
Contributor Author

@practicalswift If i understand you correctly this is what you are suggesting?

CAmount amountdelta = nFeeDelta;
            if (!MoneyRange(amountdelta)) {
                return false;
            }
            if (amountdelta) {
                pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
            }

and

for (const auto& i : mapDeltas) {
            if (!MoneyRange(i.second)) {
                return false;
            }
            if (i.second) {
                pool.PrioritiseTransaction(i.first, i.second);
            }
        }

Let me know if this seems correct.

@practicalswift
Copy link
Contributor
practicalswift commented Jul 17, 2020

@rajarshimaitra Yes for the first case, but for the second case pool.PrioritiseTransaction(i.first, i.second); should be done unconditionally (skip if (i.second)) in order to not change the existing logic :)

Update: Negative deltas are allowed!

Also would you mind changing the PR title to Fix UBSan warnings triggered when loading corrupt mempool.dat files?

@rajarshimaitra rajarshimaitra changed the title validation: Fixes UB reported in issue #19278 Fix UBSan warnings triggered when loading corrupt mempool.dat files Jul 18, 2020
@rajarshimaitra
Copy link
Contributor Author

Fixed and updated.

@practicalswift
Copy link
Contributor
practicalswift commented Jul 18, 2020

ACK XXX

Update: Negative deltas are allowed!

Restarted Travis which failed spuriously :)

@fjahr
Copy link
Contributor
fjahr commented Jul 21, 2020

Code review ACK 2cc2cbd

Comment on lines +5049 to +5051
if (!MoneyRange(amountdelta)) {
return false;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no requirement for deltas to be in MoneyRange... Better use a numeric_limit max

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before I push the commit does this look ok?

CAmount amountdelta = nFeeDelta;
            if (amountdelta > numeric_limits<int64_t>::max() || amountdelta < numeric_limits<int64_t>::min())  {
                return false;
            }

Copy link
Contributor
@practicalswift practicalswift Jul 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@luke-jr Oh, crap thanks for alerting about that! I naïvely assumed that the bounds of CAmount were defined by MoneyRange(…), but I now understand that this is a case where negative amounts are allowed for a CAmount. I assume the valid range here is [-MAX_MONEY, MAX_MONEY] then?

Should CAmount perhaps be reserved for cases where MoneyRange(…) defines the bounds?

@rajarshimaitra I don't think that is what @luke-jr is suggesting: note that CAmount is int64_t so amountdelta is guaranteed to be in the range you're checking for in the suggested diff ([std:: numeric_limits<int64_t>::min(), std:: numeric_limits<int64_t>::max()]) :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right. Well, this sounds quite deep for my grasp and I wouldn't know what exactly to do. Do suggest and will update the PR accordingly.

@practicalswift
Copy link
Contributor
practicalswift commented Aug 18, 2020

@rajarshimaitra To allow for negative deltas: what about replacing the MoneyRange(…) usages in this PR with IsValidFeeDelta(…) which could be something along the lines of:

NODISCARD bool IsValidFeeDelta(const CAmount fee_delta) {
    return fee_delta >= -MAX_MONEY && fee_delta <= MAX_MONEY;
}

@DrahtBot
Copy link
Contributor

🐙 This pull request conflicts with the target branch and needs rebase.

Want to unsubscribe from rebase notifications on this pull request? Just convert this pull request to a "draft".

Copy link
Member
@maflcko maflcko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of this patch is already in ee11a41. The other part I am not sure it fixes the issue. See also #20383 (comment)

if (amountdelta) {
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
}
TxValidationState state;
if (nTime + nExpiryTimeout > nNow) {
if (nTime > nNow - nExpiryTimeout) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already in ee11a41

@maflcko
Copy link
Member
maflcko commented May 10, 2021

Closing due to inactivity

@maflcko maflcko closed this May 10, 2021
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Aug 18, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants
0