Fix unsigned integer wrap-around in GetBlockProofEquivalentTime#11551
Fix unsigned integer wrap-around in GetBlockProofEquivalentTime#11551practicalswift wants to merge 2 commits intobitcoin:masterfrom
Conversation
adf44fc to
de9517d
Compare
|
Again, isn't this part of #11535 too? Both are just avoiding unintentional unsigned integer wraparounds? |
|
@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 :-) |
promag
left a comment
There was a problem hiding this comment.
Nit, shorter commit message Fix unsigned integer wrap-around in GetBlockProofEquivalentTime.
|
@promag Nit addressed :-) |
|
ACK de9517d. |
|
@sipa What do you think about this change? Do you have time to review? :-) |
|
Do we not care about integer wrap-arounds? If so let me know and I'll close :-) |
|
@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. |
|
@MarcoFalke Thanks to the implicit conversion back to the return type 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! |
|
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? |
|
@MarcoFalke I'm not sure how that test would be constructed since |
|
utACK de9517d59bc9943ec62ca26ed5f7111c9bc73b6c |
|
@laanwj Willing to review? :-) |
| 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. |
de9517d to
d9ab663
Compare
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. 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. |
|
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 :-) |
ken2812221
left a comment
There was a problem hiding this comment.
utACK d9ab663095a3c2b1e6c2bce70ad897e092b0f068
|
utACK d9ab663 |
|
re-utACK d9ab663 |
…lentTime(...) when to.nChainWork <= from.nChainWork
d9ab663 to
58e0342
Compare
|
Rebased! |
|
utACK 58e0342 |
|
re-utACK 58e0342 |
|
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. |
|
@gmaxwell That's already taken care of by (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 :-)) |
|
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. |
Fix likely unintentional unsigned integer wrap-around in
GetBlockProofEquivalentTime(...)whento.nChainWork <= from.nChainWork.Description:
int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, …)contains the following code:
r.GetLow64()is of typeuint64_t.Note that the types of the two operands in
sign * r.GetLow64()differ in signedness.Since
uint64_tis wider thanintthe 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 with18446744073709551615 * 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? :-)