-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix edge case for limb overflow in SMOD #9934
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
Changes from 3 commits
c2d093e
65a71c2
267ece3
5230bf6
507b17d
678fca5
23203ee
b0d8d7c
36b7ff5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -544,6 +544,52 @@ public void mulMod() { | |||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| @Test | ||||||||
| public void mulSubOverflowWithAddBackBug() { | ||||||||
| // When the dividend's leading limb equals the modulus's leading limb, the trial quotient | ||||||||
| // overflows and is clamped to 2^64-1. Verify correctness for each Modulus size. | ||||||||
|
|
||||||||
| // Modulus192 path (b.u3==0, b.u2!=0) | ||||||||
| UInt256 a1 = | ||||||||
| UInt256.fromBytesBE( | ||||||||
| new BigInteger("7effffff8000000000000000000000000000000000000000d900000000000001", 16) | ||||||||
| .toByteArray()); | ||||||||
| UInt256 b1 = | ||||||||
| UInt256.fromBytesBE( | ||||||||
| new BigInteger("7effffff800000007effffff800000008000ff0000010000", 16).toByteArray()); | ||||||||
| BigInteger expected1 = new BigInteger("7effffff800000007dff00feffff0001d901fe0000020001", 16); | ||||||||
| assertThat(new BigInteger(1, a1.mod(b1).toBytesBE())).isEqualTo(expected1); | ||||||||
|
|
||||||||
| // Modulus128 path (b.u3==0, b.u2==0, b.u1!=0) | ||||||||
| UInt256 a2 = | ||||||||
| UInt256.fromBytesBE( | ||||||||
| new BigInteger("7effffff800000000000000000000000d900000000000001", 16).toByteArray()); | ||||||||
| UInt256 b2 = | ||||||||
| UInt256.fromBytesBE( | ||||||||
| new BigInteger("7effffff800000007fffffffffffffff", 16).toByteArray()); | ||||||||
| BigInteger aBI2 = new BigInteger(1, a2.toBytesBE()); | ||||||||
| BigInteger bBI2 = new BigInteger(1, b2.toBytesBE()); | ||||||||
| BigInteger expected2 = aBI2.mod(bBI2); | ||||||||
| assertThat(new BigInteger(1, a2.mod(b2).toBytesBE())).isEqualTo(expected2); | ||||||||
|
||||||||
| assertThat(new BigInteger(1, a2.mod(b2).toBytesBE())).isEqualTo(expected2); | |
| assertThat(new BigInteger(1, a2.mod(b2).toBytesBE())).isEqualTo(expected2); | |
| assertThat(new BigInteger(1, a2.signedMod(b2).toBytesBE())).isEqualTo(expected2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be possible to write this without branches - did you check if this produces branching at assembly level?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was thinking about comparing the last limb instead:
It passes all tests, but after consideration I think it's safer to go with your version as it seems safer to compare each limb one by one with the modulus.
Also from a performance perspective this is such a rare case that it doesn't really matter much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here a check that multipy substract with overflow gives a negative answer is sufficient, meaning there is a borrow, exactly like multiply substract operation.
I am not sure why you say it is safer, it is logically the same to check for a leftover borrow or to check the result has wrapped around, and it is similar to what we do in mulSub.