From a5a3be16366579f6701642f41f9146ce196cd617 Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Tue, 21 Nov 2017 14:16:04 +0100 Subject: [PATCH] [release-branch.go1.8] math/big: protect against aliasing in nat.divLarge MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Run-TryBot: Robert Griesemer TryBot-Result: Gobot Gobot Reviewed-by: Robert Griesemer Reviewed-on: https://go-review.googlesource.com/89156 Run-TryBot: Andrew Bonventre Reviewed-by: Andrew Bonventre --- src/math/big/int_test.go | 20 ++++++++++++++++++++ src/math/big/nat.go | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/math/big/int_test.go b/src/math/big/int_test.go index b8e0778ca3..4a4e548576 100644 --- a/src/math/big/int_test.go +++ b/src/math/big/int_test.go @@ -1487,6 +1487,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() diff --git a/src/math/big/nat.go b/src/math/big/nat.go index 9b1a626c4c..607430064c 100644 --- a/src/math/big/nat.go +++ b/src/math/big/nat.go @@ -575,8 +575,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) -- 2.48.1