]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.9] math/big: protect against aliasing in nat.divLarge
authorAlberto Donizetti <alb.donizetti@gmail.com>
Tue, 21 Nov 2017 13:16:04 +0000 (14:16 +0100)
committerAndrew Bonventre <andybons@golang.org>
Mon, 22 Jan 2018 20:25:04 +0000 (20:25 +0000)
In nat.divLarge (having signature (z nat).divLarge(u, uIn, v nat)),
we check whether z aliases uIn or v, but aliasing is currently not
checked for the u parameter.

Unfortunately, z and u aliasing each other can in some cases cause
errors in the computation.

The q return parameter (which will hold the result's quotient), is
unconditionally initialized as

    q = z.make(m + 1)

When cap(z) ≥ m+1, z.make() will reuse z's backing array, causing q
and z to share the same backing array. If then z aliases u, setting q
during the quotient computation will then corrupt u, which at that
point already holds computation state.

To fix this, we add an alias(z, u) check at the beginning of the
function, taking care of aliasing the same way we already do for uIn
and v.

Fixes #22830

Change-Id: I3ab81120d5af6db7772a062bb1dfc011de91f7ad
Reviewed-on: https://go-review.googlesource.com/78995
Run-TryBot: Alberto Donizetti <alb.donizetti@gmail.com>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Reviewed-on: https://go-review.googlesource.com/88322
Run-TryBot: Andrew Bonventre <andybons@golang.org>

src/math/big/int_test.go
src/math/big/nat.go

index 42e810b3b81c6ceb2701b1e928a5ac3c6375ce3c..46e2ff1203357dad988db274850be836a245b19d 100644 (file)
@@ -1536,6 +1536,26 @@ func TestSqrt(t *testing.T) {
        }
 }
 
+// We can't test this together with the other Exp tests above because
+// it requires a different receiver setup.
+func TestIssue22830(t *testing.T) {
+       one := new(Int).SetInt64(1)
+       base, _ := new(Int).SetString("84555555300000000000", 10)
+       mod, _ := new(Int).SetString("66666670001111111111", 10)
+       want, _ := new(Int).SetString("17888885298888888889", 10)
+
+       var tests = []int64{
+               0, 1, -1,
+       }
+
+       for _, n := range tests {
+               m := NewInt(n)
+               if got := m.Exp(base, one, mod); got.Cmp(want) != 0 {
+                       t.Errorf("(%v).Exp(%s, 1, %s) = %s, want %s", n, base, mod, got, want)
+               }
+       }
+}
+
 func BenchmarkSqrt(b *testing.B) {
        n, _ := new(Int).SetString("1"+strings.Repeat("0", 1001), 10)
        b.ResetTimer()
index 889eacb90f03c12911187d22d0f705beb12c81e5..f2cdbdb909ef36d6810b1e750887061aba41c828 100644 (file)
@@ -566,8 +566,8 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) {
        // determine if z can be reused
        // TODO(gri) should find a better solution - this if statement
        //           is very costly (see e.g. time pidigits -s -n 10000)
-       if alias(z, uIn) || alias(z, v) {
-               z = nil // z is an alias for uIn or v - cannot reuse
+       if alias(z, u) || alias(z, uIn) || alias(z, v) {
+               z = nil // z is an alias for u or uIn or v - cannot reuse
        }
        q = z.make(m + 1)