Fix issue 20493 - incorrect result of BigInt * BigInt #7349
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The problem comes down to a buffer overflow.
How could a buffer overflow happen in
@safecode?Well, it's basically a logical buffer overflow, while technically no out of bounds memory is accessed.
Two buffers are created in one allocation of size a + b (presumably as an optimization).
buffer[a..$]is given topartial, whilescratchbufremains the wholebuffer[0..$]instead ofbuffer[0..a].That means that if more than
aelements are written toscratchbuf, it messes uppartial, which is what happens in the bug report.The buffer size for Karatsuba multiplication of
x * yis calculated as2 * x.length(x and y are aBigDigit[]which is auint[]).Most calls to
mulKaratsubaclaim2 * half(x.length)ofscratchbufand pass the rest to a recursive call of half the instance size. I think the underlying assumption is that the geometric series 1 + 0.5 + 0.25 ... <= 2 so the bound is sufficient.It fails to account for two things however:
x.lengthis more thany.length * sqrt(2)), another half of the buffer is claimed.So why wasn't this caught earlier? Well, there is a
KARATSUBALIMITcurrently set to10, and aBigDigit[]withlength < KARATSUBALIMITgets a small size optimization so no Karatsuba multiplication is done. This limit gave some constant leeway to the buffer. When numbers get sufficiently large, this asymptotic growth of the required buffer size catches up with the constant leeway of the actual buffer size with lower asymptotic growth however.The smallest case of this bug with the current limit is when
(x.length, y.length) = (116, 99), so this bug only manifests once numbers reach a size of2^(32*116) ~= 10^1117which is probably why it got undetected for so long.Finding the correct minimum buffer size is hard, since it depends on
KARATSUBALIMIT(for base cases), log2(x.length) (for rounding errors), and how often the recursive case is unbalanced.With some brute force testing I found that a little over factor 2 seems sufficient for at least 10 000 big digits even with a smaller KARATSUBALIMIT of 2, so I settled with
2.25for now.I also truncated the
scratchbuffso that at least instead of silently giving an incorrect answer, it raises a RangeError on failure.