aboutsummaryrefslogtreecommitdiff
path: root/src/hash
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2020-10-12 15:41:14 -0400
committerRuss Cox <rsc@golang.org>2020-10-13 00:31:47 +0000
commit5ef78c4d84620f6594ad63cbd1b3b43f41670636 (patch)
tree49e4184d9a4e630369d036e8d3880e75da4cd7d8 /src/hash
parent7bda6154caa6f0c527f4a8302e38d450b44ae68b (diff)
downloadgo-5ef78c4d84620f6594ad63cbd1b3b43f41670636.tar.gz
go-5ef78c4d84620f6594ad63cbd1b3b43f41670636.zip
hash/crc32: fix race between lazy Castagnoli init and Update/Write
The switch on tab is checking tab == castagnoliTable, but castagnoliTable can change value during a concurrent call to MakeTable. Fixes #41911. Change-Id: I6124dcdbf33e17fe302baa3e1aa03202dec61b4c Reviewed-on: https://go-review.googlesource.com/c/go/+/261639 Trust: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/hash')
-rw-r--r--src/hash/crc32/crc32.go16
-rw-r--r--src/hash/crc32/crc32_test.go10
2 files changed, 20 insertions, 6 deletions
diff --git a/src/hash/crc32/crc32.go b/src/hash/crc32/crc32.go
index 908b84adcb..f330fdb77a 100644
--- a/src/hash/crc32/crc32.go
+++ b/src/hash/crc32/crc32.go
@@ -16,6 +16,7 @@ import (
"errors"
"hash"
"sync"
+ "sync/atomic"
)
// The size of a CRC-32 checksum in bytes.
@@ -78,6 +79,7 @@ var castagnoliTable8 *slicing8Table
var castagnoliArchImpl bool
var updateCastagnoli func(crc uint32, p []byte) uint32
var castagnoliOnce sync.Once
+var haveCastagnoli uint32
func castagnoliInit() {
castagnoliTable = simpleMakeTable(Castagnoli)
@@ -93,6 +95,8 @@ func castagnoliInit() {
return slicingUpdate(crc, castagnoliTable8, p)
}
}
+
+ atomic.StoreUint32(&haveCastagnoli, 1)
}
// IEEETable is the table for the IEEE polynomial.
@@ -208,10 +212,10 @@ func readUint32(b []byte) uint32 {
// Update returns the result of adding the bytes in p to the crc.
func Update(crc uint32, tab *Table, p []byte) uint32 {
- switch tab {
- case castagnoliTable:
+ switch {
+ case atomic.LoadUint32(&haveCastagnoli) != 0 && tab == castagnoliTable:
return updateCastagnoli(crc, p)
- case IEEETable:
+ case tab == IEEETable:
// Unfortunately, because IEEETable is exported, IEEE may be used without a
// call to MakeTable. We have to make sure it gets initialized in that case.
ieeeOnce.Do(ieeeInit)
@@ -222,10 +226,10 @@ func Update(crc uint32, tab *Table, p []byte) uint32 {
}
func (d *digest) Write(p []byte) (n int, err error) {
- switch d.tab {
- case castagnoliTable:
+ switch {
+ case atomic.LoadUint32(&haveCastagnoli) != 0 && d.tab == castagnoliTable:
d.crc = updateCastagnoli(d.crc, p)
- case IEEETable:
+ case d.tab == IEEETable:
// We only create digest objects through New() which takes care of
// initialization in this case.
d.crc = updateIEEE(d.crc, p)
diff --git a/src/hash/crc32/crc32_test.go b/src/hash/crc32/crc32_test.go
index 4bdafaf8f5..cbb869dfd6 100644
--- a/src/hash/crc32/crc32_test.go
+++ b/src/hash/crc32/crc32_test.go
@@ -13,6 +13,16 @@ import (
"testing"
)
+// First test, so that it can be the one to initialize castagnoliTable.
+func TestCastagnoliRace(t *testing.T) {
+ // The MakeTable(Castagnoli) lazily initializes castagnoliTable,
+ // which races with the switch on tab during Write to check
+ // whether tab == castagnoliTable.
+ ieee := NewIEEE()
+ go MakeTable(Castagnoli)
+ ieee.Write([]byte("hello"))
+}
+
type test struct {
ieee, castagnoli uint32
in string