[release-branch.go1.14] math/big: correct off-by-one access in divBasic

The divBasic function computes the quotient of big nats u/v word by word.
It estimates each word qhat by performing a long division (top 2 words of u
divided by top word of v), looks at the next word to correct the estimate,
then perform a full multiplication (qhat*v) to catch any inaccuracy in the
estimate.

In the latter case, "negative" values appear temporarily and carries
must be carefully managed, and the recursive division refactoring
introduced a case where qhat*v has the same length as v, triggering an
out-of-bounds write in the case it happens when computing the top word
of the quotient.

Fixes #37501

Change-Id: I15089da4a4027beda43af497bf6de261eb792f94
Reviewed-on: https://go-review.googlesource.com/c/go/+/221980
Reviewed-by: Robert Griesemer <gri@golang.org>
(cherry picked from commit ac1fd419b6)
Reviewed-on: https://go-review.googlesource.com/c/go/+/227877
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Rémy Oudompheng 2020-03-05 07:59:00 +01:00 committed by Andrew Bonventre
parent 768e5d1e4d
commit cd65852fee
2 changed files with 31 additions and 2 deletions

View File

@ -740,7 +740,8 @@ func (z nat) divLarge(u, uIn, vIn nat) (q, r nat) {
// The remainder overwrites input u.
//
// Precondition:
// - len(q) >= len(u)-len(v)
// - q is large enough to hold the quotient u / v
// which has a maximum length of len(u)-len(v)+1.
func (q nat) divBasic(u, v nat) {
n := len(v)
m := len(u) - n
@ -779,6 +780,8 @@ func (q nat) divBasic(u, v nat) {
}
// D4.
// Compute the remainder u - (q̂*v) << (_W*j).
// The subtraction may overflow if q̂ estimate was off by one.
qhatv[n] = mulAddVWW(qhatv[0:n], v, qhat, 0)
qhl := len(qhatv)
if j+qhl > len(u) && qhatv[n] == 0 {
@ -787,7 +790,11 @@ func (q nat) divBasic(u, v nat) {
c := subVV(u[j:j+qhl], u[j:], qhatv)
if c != 0 {
c := addVV(u[j:j+n], u[j:], v)
u[j+n] += c
// If n == qhl, the carry from subVV and the carry from addVV
// cancel out and don't affect u[j+n].
if n < qhl {
u[j+n] += c
}
qhat--
}
@ -827,6 +834,10 @@ func (z nat) divRecursive(u, v nat) {
putNat(tmp)
}
// divRecursiveStep computes the division of u by v.
// - z must be large enough to hold the quotient
// - the quotient will overwrite z
// - the remainder will overwrite u
func (z nat) divRecursiveStep(u, v nat, depth int, tmp *nat, temps []*nat) {
u = u.norm()
v = v.norm()

View File

@ -786,3 +786,21 @@ func TestNatDiv(t *testing.T) {
}
}
}
// TestIssue37499 triggers the edge case of divBasic where
// the inaccurate estimate of the first word's quotient
// happens at the very beginning of the loop.
func TestIssue37499(t *testing.T) {
// Choose u and v such that v is slightly larger than u >> N.
// This tricks divBasic into choosing 1 as the first word
// of the quotient. This works in both 32-bit and 64-bit settings.
u := natFromString("0x2b6c385a05be027f5c22005b63c42a1165b79ff510e1706b39f8489c1d28e57bb5ba4ef9fd9387a3e344402c0a453381")
v := natFromString("0x2b6c385a05be027f5c22005b63c42a1165b79ff510e1706c")
q := nat(nil).make(8)
q.divBasic(u, v)
q = q.norm()
if s := string(q.utoa(16)); s != "fffffffffffffffffffffffffffffffffffffffffffffffb" {
t.Fatalf("incorrect quotient: %s", s)
}
}