Skip to content

Fix unsigned integer wrap-around in GetBlockProofEquivalentTime#11551

Closed
practicalswift wants to merge 2 commits intobitcoin:masterfrom
practicalswift:proof-equivalent-time
Closed

Fix unsigned integer wrap-around in GetBlockProofEquivalentTime#11551
practicalswift wants to merge 2 commits intobitcoin:masterfrom
practicalswift:proof-equivalent-time

Conversation

@practicalswift
Copy link
Contributor

@practicalswift practicalswift commented Oct 23, 2017

Fix likely unintentional unsigned integer wrap-around in GetBlockProofEquivalentTime(...) when to.nChainWork <= from.nChainWork.

Description:

int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, …)
contains the following code:

int sign = 1;
if (to.nChainWork > from.nChainWork) {
…
} else {
    …
    sign = -1;
}
…
return sign * r.GetLow64();

r.GetLow64() is of type uint64_t.

Note that the types of the two operands in sign * r.GetLow64() differ in signedness.

Since uint64_t is wider than int the signed operand (sign) is converted to the unsigned type.

In the case of sign == -1 (to.nChainWork <= from.nChainWork) we wrap around and end up with 18446744073709551615 * r.GetLow64() (std::numeric_limits<uint64_t>::max() * r.GetLow64()) instead of the intended -1 * r.GetLow64().

Note however that another conversion takes place when the result is converted into the return type
(int64_t), so the resulting value should be the expected one (equivalent to -1 * r.GetLow64()).

In the case that this behaviour (wrap-around + relying on return type to fix) is intentional a comment should probably be added to indicate so :-)

GetBlockProofEquivalentTime(…) was introduced in f7303f9. Friendly ping @sipa - intentional or not? :-)

@practicalswift practicalswift changed the title Fix invalid return value in GetBlockProofEquivalentTime(...) when to.nChainWork <= from.nChainWork Fix unintentional wrap-around in GetBlockProofEquivalentTime(...) when to.nChainWork <= from.nChainWork Oct 23, 2017
@practicalswift practicalswift changed the title Fix unintentional wrap-around in GetBlockProofEquivalentTime(...) when to.nChainWork <= from.nChainWork Fix unintentional unsigned integer wrap-around in GetBlockProofEquivalentTime(...) when to.nChainWork <= from.nChainWork Oct 23, 2017
@meshcollider
Copy link
Contributor

Again, isn't this part of #11535 too? Both are just avoiding unintentional unsigned integer wraparounds?

@practicalswift
Copy link
Contributor Author

@meshcollider I thought this one was a bit more interesting than the others and deserved its own PR, but sure I can just add all unintentional unsigned integer wraparounds to #11535. I have a few more to report :-)

Copy link
Contributor

@promag promag left a comment

Choose a reason for hiding this comment

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

Nit, shorter commit message Fix unsigned integer wrap-around in GetBlockProofEquivalentTime.

@practicalswift practicalswift changed the title Fix unintentional unsigned integer wrap-around in GetBlockProofEquivalentTime(...) when to.nChainWork <= from.nChainWork Fix unsigned integer wrap-around in GetBlockProofEquivalentTime Oct 24, 2017
@practicalswift
Copy link
Contributor Author

@promag Nit addressed :-)

@promag
Copy link
Contributor

promag commented Oct 24, 2017

ACK de9517d.

@practicalswift
Copy link
Contributor Author

@promag Thanks a lot for reviewing! Would you mind taking a look at the wrap-arounds covered in #11535 too? :-)

@practicalswift
Copy link
Contributor Author

practicalswift commented Oct 24, 2017

@sipa What do you think about this change? Do you have time to review? :-)

@practicalswift
Copy link
Contributor Author

Do we not care about integer wrap-arounds? If so let me know and I'll close :-)

@maflcko
Copy link
Member

maflcko commented Feb 22, 2018

@practicalswift Can you add a test case that fails before this change and passes after this change. This prevents from re-introducing the issue in the future.

@practicalswift
Copy link
Contributor Author

practicalswift commented Feb 23, 2018

@MarcoFalke Thanks to the implicit conversion back to the return type int64_t the wrap-around is contained to GetBlockProofEquivalentTime(…) so I don't think it is possible to construct a test for this.

Judging from the reviews of this PR it seems like the current code's reliance on wrap-around + implicit conversion is quite surprising which in itself is a reason to be explicit here. Explicit is better than implicit!

@maflcko
Copy link
Member

maflcko commented Feb 23, 2018

Ah, sorry. My bad, I didn't read OP.

Then it should be possible to add a test case that (temporarily) wraps around, but passes before and after this change?

@practicalswift
Copy link
Contributor Author

@MarcoFalke I'm not sure how that test would be constructed since GetBlockProofEquivalentTime(…) returns the correct value thanks to the conversion that takes place after the unsigned integer wrap-around has taken place. Perhaps I'm missing something? :-)

@sipa
Copy link
Member

sipa commented Mar 15, 2018

utACK de9517d59bc9943ec62ca26ed5f7111c9bc73b6c

@practicalswift
Copy link
Contributor Author

practicalswift commented Apr 16, 2018

@laanwj Willing to review? :-)

@DrahtBot
Copy link
Contributor

The last travis run for this pull request was 269 days ago and is thus outdated. To trigger a fresh travis build, this pull request should be closed and re-opened.

@DrahtBot
Copy link
Contributor

DrahtBot commented Nov 9, 2018

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

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #11535 (Avoid unintentional unsigned integer wraparounds by practicalswift)

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.

@practicalswift
Copy link
Contributor Author

practicalswift commented Nov 15, 2018

I'm doing some PR cleaning and this PR has gotten a bit old :-)

This PR has received ACK/utACK:s from @MarcoFalke and the original author of the code @sipa (code introduced in f7303f9).

Does this PR still stand a chance of getting merged? Let me know otherwise and I'll close :-)

Copy link
Contributor

@ken2812221 ken2812221 left a comment

Choose a reason for hiding this comment

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

utACK d9ab663095a3c2b1e6c2bce70ad897e092b0f068

@meshcollider
Copy link
Contributor

utACK d9ab663

@promag
Copy link
Contributor

promag commented Nov 20, 2018

re-utACK d9ab663

@practicalswift
Copy link
Contributor Author

Rebased!

@ken2812221
Copy link
Contributor

utACK 58e0342

@meshcollider
Copy link
Contributor

re-utACK 58e0342

@gmaxwell
Copy link
Contributor

This code looks wrong to me. GetLow64() appears to return a full range uint64 "return pn[0] | (uint64_t)pn[1] << 32;". Simply casting it to int if its out of range would introduce UB where there isn't any potential for UB now.

@practicalswift
Copy link
Contributor Author

practicalswift commented Dec 1, 2018

@gmaxwell That's already taken care of by if (r.bits() > 63) { … } on L147, right? :-)

(Pedantic nit: An out-of-range conversion from unsigned to signed would have resulted in implementation-defined behaviour and not UB, right? Not that implementation-defined behaviour is much better from a distributed consensus perspective :-))

@gmaxwell
Copy link
Contributor

gmaxwell commented Dec 2, 2018

I was brain-farting a bit and thinking that (signed)(2^63 - 1)-1 was UB but it's actually the opposite case where -1 times a maximally negative number that I was thinking of (-(signed)(2^63 - 1)-1)-1. ... I'm hesitant to spend more time reviewing these kinds of changes than they take to write. It was also unclear if bits returned 63 or 64 on 1<<63, and I ultimately wrote a test to check. It's easy to introduce bugs with changes like this, and hard to review them to be absolutely sure. Sorry for the distraction there.

@practicalswift practicalswift deleted the proof-equivalent-time branch April 10, 2021 19:36
@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.

9 participants